Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows 7 64 bit and accessing Win32 API calls via P/Invoke & Marshal problems

I'm relatively new to .net/C# (though very experienced in Win32 / MFC and other platforms) and need to write a utility to talk to a custom USB HID device. The protocol is pretty simple and I already have a working utility written in MFC, but I would prefer to write the utility in .Net / C# as I'm trying to move with the times and leave MFC behind.

I did some investigation and came across this article which seemed to help me understand how to access HID devices from .Net/C#, especially as it's just calling out the to Win32 API calls I am already familiar with:

http://www.developerfusion.com/article/84338/making-usb-c-friendly/

The sample code provided gave me an excellent introduction to how to access the Win32 API calls to talk to the USB device (just as my previous MFC code did) and this all works fine on a 32 bit installation of Windows Vista or 7, but when I try to run the same code on a 64 bit installation it fails. Even if I try creating a dedicated 64 bit application it still fails.

I'm pretty sure the problem is with how the Marshal is passing the parameters (on the stack?) to the Win32 API, but my knowledge and experience of .Net/C# at this stage is not really good enough to understand exactly what the issue is and how to solve it - the problem is likely more advanced than the level I am currently at.

Everything seems to work fine in the code until I reach the instruction...

while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface))    // this gets the device interface information for a device at index 'nIndex' in the memory block

Where the SetupDI... returns true on 32 bit systems and subsequently iterates through all the connected USB devices but returns false on 64 bit systems. I'm pretty sure it's likely to be an issue with how parameters are being passed into the Win32 API function but I don't understand what the exact problem is. The DLLImport definition for the function is:

[DllImport("setupapi.dll", SetLastError = true)] protected static extern bool SetupDiEnumDeviceInterfaces(IntPtr lpDeviceInfoSet, uint nDeviceInfoData, ref Guid gClass, uint nIndex, ref DeviceInterfaceData oInterfaceData);

I wonder if anyone is able to suggest what the offending parameter might be and how I might fix it?

Thanks in advance for any help offered, if more information is required please ask for it! Rich

like image 547
Richard Baxter Avatar asked Aug 23 '11 21:08

Richard Baxter


2 Answers

From MSDN:

BOOL SetupDiEnumDeviceInterfaces(
  __in      HDEVINFO DeviceInfoSet,
  __in_opt  PSP_DEVINFO_DATA DeviceInfoData,
  __in      const GUID *InterfaceClassGuid,
  __in      DWORD MemberIndex,
  __out     PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData
);
DeviceInfoData [in, optional]
A pointer to an SP_DEVINFO_DATA structure...

Note that DeviceInfoData is a pointer - so should be IntPtr, not uInt:

    [DllImport("setupapi.dll", SetLastError = true)] protected static
    extern bool SetupDiEnumDeviceInterfaces(IntPtr lpDeviceInfoSet,
        IntPtr pDeviceInfoData, ref Guid gClass,
        uint nIndex, ref DeviceInterfaceData oInterfaceData);

And when calling it, pass IntPtr.Zero instead of 0.

like image 109
BrendanMcK Avatar answered Oct 05 '22 10:10

BrendanMcK


You might be able to track down the offending parameter problem (if that's what it is) by checking the value from GetLastError, which can be obtained from Marshal.GetLastWin32Error() in a .NET app.

One possible problem might be how the oInterface variable is initialized. The cbSize element is supposed to be set. And a 64-bit version of that structure (SP_DEVICE_INTERFACE_DATA) might be larger than the 32-bit version. I looked at it just now briefly and counted in my head (always prone to error), and it looks like the 32-bit version will be 28 bytes, and the 64-bit version would be 32 bytes.

like image 24
Mark Wilkins Avatar answered Oct 05 '22 09:10

Mark Wilkins