Unity-DirectInput icon indicating copy to clipboard operation
Unity-DirectInput copied to clipboard

Cannot call GetDeviceFFBCapabilities

Open Risto-Paasivirta opened this issue 2 years ago • 4 comments

Cannot call certain functions of the library: GetDeviceCapabilities GetActiveDevices

Throws an error of: MarshalDirectiveException: string/stringbuilder marshalling conversion 29 not implemented

Risto-Paasivirta avatar Dec 04 '23 08:12 Risto-Paasivirta

I did not manage to fix this easily other than modify the C++ side to pass the information as char** arrays instead of safearrays.

Risto-Paasivirta avatar Dec 05 '23 10:12 Risto-Paasivirta

Here is an example on how I managed to Marshal a string array of unknown length from the C++ to C# This might be better than to rely on the safearrays, I could not find any information on if this is some version or runtime SDK related thing on why the conversion 29 is not working for me on any machine.

  DIRECTINPUTFORCEFEEDBACK_API const char** TestMarshal(int* count);
  DIRECTINPUTFORCEFEEDBACK_API void FreeStrings(const char** strings, int count);
const char** TestMarshal(int* count) {
    // Example strings
    const char* strings[] = { "Marshal", "String", "Array", "Test", nullptr };

    // Calculate the number of strings
    *count = 0;
    while (strings[*count] != nullptr) {
        *count += 1;
    }

    // Allocate memory for the array of strings
    const char** result = new const char* [*count];
    for (int i = 0; i < *count; ++i) {
        result[i] = _strdup(strings[i]);
    }

    return result;
}

void FreeStrings(const char** strings, int count) {
    for (int i = 0; i < count; ++i) {
        free((void*)strings[i]);
    }
    delete[] strings;
}

And here is the C# part

    [DllImport(DLLFile)] public static extern IntPtr TestMarshal(out int count);
    [DllImport(DLLFile)] public static extern IntPtr FreeStrings(IntPtr strings, int count);

public static string[] TestMarshaling() 
{
    IntPtr stringsPtr;
    int count;

    stringsPtr = Native.TestMarshal(out count);

    // Read the array of strings directly from memory
    string[] resultStrings = new string[count];

    for (int i = 0; i < count; ++i)
    {
        IntPtr currentPtr = Marshal.ReadIntPtr(stringsPtr, i * IntPtr.Size);

        // Read each character until a null character is encountered
        int charIndex = 0;
        byte currentByte;
        while ((currentByte = Marshal.ReadByte(currentPtr, charIndex)) != 0)
        {
            charIndex++;
        }

        // Allocate a byte array and copy the characters
        byte[] bytes = new byte[charIndex];
        Marshal.Copy(currentPtr, bytes, 0, charIndex);

        // Not sure if need to use different encoding?
        resultStrings[i] = System.Text.Encoding.ASCII.GetString(bytes);
    }

    // Free the memory allocated in C++
    Native.FreeStrings(stringsPtr, count);

    return resultStrings;
}

Risto-Paasivirta avatar Dec 05 '23 10:12 Risto-Paasivirta

Wonderful, this will help massively. I will admit, extended characters slipped my mind 😅. The SAFEARRAY started just to run some tests during development. I'm just doing some interviews, but I'll get to this issue in the coming days.

If you don't mind me asking, what's your use case for this library?

MrTimcakes avatar Dec 05 '23 11:12 MrTimcakes

I can't say much about the project since I'm under NDAs, it is a work for a customer (non-game simulation), and we need to convey forces back to the user as it is vital component of the simulation. Good thing is that the end machines are pre-known hardware so we don't need to cater to various different setups as the program is not made for public use.

Overall it works and I want to thank you for the hard work you have put into this plugin 👍 Weird that Unity hasn't done anything like this by default with their new input system.

If there's anything I can help with polishing this let me know. C++ is not my forte, but ChatGPT is helping me greatly to get things rolling 😄

Risto-Paasivirta avatar Dec 05 '23 11:12 Risto-Paasivirta