MrTimcakes / Unity-DirectInput

Unity Native Plugin to expose DirectX DirectInput ForceFeedback
GNU Lesser General Public License v3.0
31 stars 11 forks source link

Cannot call GetDeviceFFBCapabilities #10

Open Risto-Paasivirta opened 9 months ago

Risto-Paasivirta commented 9 months ago

Cannot call certain functions of the library: GetDeviceCapabilities GetActiveDevices

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

Risto-Paasivirta commented 9 months ago

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 commented 9 months ago

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;
}
MrTimcakes commented 9 months ago

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?

Risto-Paasivirta commented 9 months ago

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 😄