|
On 32bit platforms, all SetupApi structures are 1-Byte packed. On 64bit platforms the SetupApi structures are 8-byte packed. (i.e., for 32 bit SP_DEVINFO_DATA.cbSize=28, for 64Bit SP_DEVINFO_DATA.cbSize=(36+4)=40)
I believe if you define the structurs like the following, the compiler will automatically handle the byte alignment at runtime. Haven't check it yet though...
[StructLayout(LayoutKind.Sequential)]
struct SP_DEVINFO_DATA
{
public UInt32 cbSize;
public Guid ClassGuid;
public UInt32 DevInst;
public IntPtr Reserved;
}
|
|
|
|
|
Yes, I already posted this in the forum linked to in another of the replies of this message. The only mystery to me now is with SP_DEVICE_INTERFACE_DETAIL_DATA[^]. SetupDiGetDeviceInterfaceDetail requires that SP_DEVICE_INTERFACE_DETAIL_DATA.cbSize be set to 5 on 32bit and 8 on 64bit. All I can assume is that this is 4+1 vs 4+4 but that doesn't really make much sense, given the data types in the struct (a DWORD and a TCHAR* pointer). I would expect it to be 8 on 32bit and 12 on 64bit. To make this worse, sizeof() in .net returns 260 (4+256) by adding the total size of the array rather than just the size of a pointer.
Do you know an elegant way to get this value that will also hold for any future platforms down the line, such as 128 bit? Is it supposed to be returning sizeof(DWORD) + sizeof(TCHAR), in which case I can understand the 5 on 32bit but not the 8 on 64bit. Could this be to do with 8 byte packing on 64bit rounding the size up?
With the following example, changing the "Pack" can achieve the correct size:
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct SP_DEVICE_INTERFACE_DETAIL_DATA
{
public int cbSize;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1)]
public string DevicePath;
} With Pack = 1, it gives 5 for 32bit compatibility; with Pack = 8, it gives 8 for 64bit compatibility. Pack = 0 is supposed to use the default pack size for the platform but I compiled and ran this on 32 bit and it still gave me 8 (same on 64 bit). Also, this struct requires that SizeConst = 256 so this means using a totally separate struct to find the size, if using pack eventually does work (and without recompiling).
modified on Saturday, July 26, 2008 7:14 AM
|
|
|
|
|
Actually, I read on another web site that the 'cbSize' parameter of the structure should be the size of the DWORD plus the first character of the string. If this is the case, then possibly, the 5 and 8 makes sense.
On a 32-bit system, COM may be expecting standard ASCII characters, which are one byte. This would give us 4 bytes for the cbSize parameter and 1 byte for the array. Being that the packing is 1-byte, this would give us a total size of 5 bytes for the structure.
On a 64-bit system, COM may be expecting Unicode characters, which are two bytes. This would give us 4 bytes for the cbSize parameter and 2 bytes for the array. Being that the packing is 8-byte, this would give us a total size of 6 bytes for the structure and 2 bytes of padding, making the total structure size 8 bytes.
So really, I think the problem boils down to what COM expects and not the actual structure size as reported by the compiler. I think the following code will work on both platforms.
oDetail.Size = (uint)(Marshal.SizeOf(typeof(UInt32)) + IntPtr.Size/4);
On 32-bit systems, IntPtr will be 4 bytes. While on 64-bit systems, IntPtr will be 8 bytes. This will give us the 1- and 2-bytes we need to determine the structure size that COM expects.
Give it a try and let me know if it works on 64-bit. I don't have a 64-bit OS here to test it on.
SIDE NOTE: The array of bytes in this structure can be any size, and COM is expecting a NULL terminated string to determine the end. So in reality, it only need to know the first byte of the arry. Hope this makes sense...
|
|
|
|
|
Many places suggest System.Runtime.InteropServices.Marshal.SystemDefaultCharSize (1 on 32bit, 2 on 64bit). How would you get that to round up to 8 on 64 bit though? You could do things to round it manually but if you are doing that, it is just as bad as what I am currently using:
.cbSize = IntPtr.Size == 8 ? 8 : 5;
These places (such as pinvoke.net) have never actually bothered testing on 64 bit, though, and it just ends up setting it to 4+2=6. They just assume it works because on 32 bit, 4+1=5.
Is there anything anywhere that can define the packing size that SetupApi is using on the particular system at runtime?
|
|
|
|
|
On my system, Windows XP Pro 32-bit, Marshal.SystemDefaultCharSize returns 2 and not 1. That's why I decided to use the IntPtr.SizeOf() instead. It will have a different size on either platform, 4 for 32-bit, and 8 for 64-bit.
As far as getting the structure sizes to 5 and 8, it should happen automatically when you compile for the 64-bit OS because of structure alignment.
I'll install Windows Server 2003 64-bit on one of my computers down stairs and give it a try and see what I come up with. I'll run 2 scenarios: 1 - app compiled 32-bit running on 64-bit, then I will try app compiled 64-bit running on 64-bit and what turns up.
I probably won't get to it till Monday though. I'll be gone all day tomorrow.
|
|
|
|
|
Yes, I didn't really believe that the 64 bit version would be any more Unicode than the 32 bit version but many places suggest it without having tried it. On both 32 and 64bit, and on both Marshal.SystemDefaultCharSize = 1 and Marshal.SystemDefaultCharSize = 2, the SetupDiGetDeviceInterfaceDetail[^] function seems to always use non-Unicode ( _A ?) anyway.
I can see how it would be 5 on 32bit and 8 on 64bit, assuming that both use 8 bit characters but 64bit rounds the 5 up to 8, but the only way it is done automatically is if you manually set the packing on the struct. Pack=1 gives 5, Pack=8 gives 8. However, I can't see how to get 32bit to pack with 1 and 64bit to pack with 8 at runtime and anything to find this value at compile time will not work (may as well compile one version with the constant value 5 and one with the constant value 8 and distribute them separately, which is not what .net is about). It's really something to do with what SetupApi is using. Pack=0 (default platform packing) seems to be 8 for both 32 and 64bit regardless of what SetupApi is using.
Unless there is some way of pulling the packing size that SetupApi is using on the machine at runtime, so that it can be used to round up, there is no way of doing this better than the small conditional that I gave in my previous message. Even with 4+IntPtr.Size/2 (which I don't think would work anyway, this would be 6 on 32bit and 8 on 64bit but on every 32 bit machine I have tried (XP, 2003 and Vista), 5 is required and on every 64 bit machine I have tried (XP, 2003 and Vista), 8 is required), you still need to know whether it should be rounding up to 8, for example.
I think ideally it would just be something like "Round 5 (or maybe 6) up to GetSetupApiPackSizeFromDll()" to work on all current and future systems at runtime.
modified on Sunday, July 27, 2008 4:31 AM
|
|
|
|
|
The use of a function to return the pack size is probably the most reasonable solution. You could probably use the version number and/or size of the SetupAPI dll to determine which structure size should be used. Which ever way is used will most likely break in the future. But hey, it's Microsoft...
On another note, if anyone is updating this library, it would be nice to have a couple extra features.
1. Maintain a list of connected devices in the HIDDevice class. Users of my Data Terminal can have more than one connected at a time.
2. Be able to turn ON and OFF asyn'c reads from the usb port. Sometimes I need to send a command to the terminal which will return information immediately. So it would be nice to have a Write/Read command.
With those two features added, I think this library would be complete.
|
|
|
|
|
I've been working on this problem for a different application.
I found that on Vista 32-bit, the cbSize for SP_DEVICE_INTERFACE_DETAIL_DATA is 6, not 5. This seems to do with the SystemDefaultCharSize being 2 in Vista (Unicode).
So, I've been testing the following code as a solution for XP x86, Vista x86, and Vista x64:
didd.cbSize = 4 + Marshal.SystemDefaultCharSize;
if (IntPtr.Size == 8) didd.cbSize += 2;
|
|
|
|
|
As I said, it must always be 5 on 32bit and 8 on 64bit, regardless of whether the OS is unicode by default. This is to do with the packing size on 32 bit (Pack=1) and on 64bit (Pack=8) for SetupApi. There's no better way of setting this than just using a conditional such as
didd.cbSize = IntPtr.Size == 8 ? 8 : 5;
If you can think of a way that can tell the packing size that SetupApi wants (and don't bother with the Pack=0 automatic packing), that would be more flexible for the future (128bit?) but unless you can get that, there is nothing you can do that will be better than a little conditional.
What you really want to do is set it to 5 and then round it up to a multiple of the packing size (1 on 32bit, 8 on 64bit). 5 rounded up to a multiple 8 will always be 8 though, so you may as well just put it right there in the code.
Using the default character size is not reliable. What you have used there will be 4+1=5 on XP32 (works) and 4+2+2=8 on Vista 64 (works) but 4+2=6 on Vista 32 (doesn't work).
|
|
|
|
|
|
Maybe you should read the rest of this thread. I made that post while I started this thread (answering my own question because nobody else would) and since then it has been posted back 2 or 3 times by other people in this same thread.
Consider using libhidnet on sourceforge, which has support for Windows 32bit, Windows 64bit and Unix/Linux/Mac
|
|
|
|
|
Hi,
Yeah, sorry about that. I didn't see the other posts - either I'm not used to CP like you or something went wrong... no idea.
Anyway, I saw you'd mentioned one bug, but that post covered another. Not to worry anyway, to date this project was the best I'd found.
Thank you for this, and for pointing me to HID.net, although I'll probably stick with this library.
|
|
|
|
|
Hello guys,
I have tried recently to run the program, but as i debugged it, it seems it can't find my usb flash drive. The "while" statement :
while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface))
{....}
in
public static HIDDevice FindDevice(int nVid, int nPid, Type oType)
doesn't reach my usb's vendor_id and product_id
Thanks for your time!
|
|
|
|
|
Hi eythimis,
Because a USB Flash Drive doesn't belong to the HID device class you can't find him right now. In most cases a Flas Drive is a (bulk) USB Mass storage Device. You can change the GUID ID (Search for the right GUID on the internet) in the program code to find the device. But Interfacing a bulk device does need other code, so anything but recognising shall need some additional coding.
Greetings wimar
|
|
|
|
|
I'm having a similar issue but using a Magtek HID device... i have 4 HID devices installed on this machine - but when i hit while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface))
...it evaluates the first item and then falls out of the loop without evaluating any other items. I'm going to dig into the windows API - but thought you might have a quicker solution.
|
|
|
|
|
Hi there, I need to get Specified Device to return the Product Name and vendor Name, but there's a problem when I try to Find the Specified Device :
This is the code I put after Device Arrived :
private void usb_OnDeviceArrived(object sender, EventArgs e)
{
this.lb_message.Items.Add("Found a Device");
HIDDevice hid1 = SpecifiedDevice.FindSpecifiedDevice(int.Parse("4D8", System.Globalization.NumberStyles.HexNumber), int.Parse("FF9D", System.Globalization.NumberStyles.HexNumber));
}
and it raised error :
Additional information: Msg:UsbLibrary.HIDDeviceException: Msg:Failed to get the detailed data from the hid. WinEr:00000000
at UsbLibrary.HIDDevice.Initialise(String strPath) in C:\Documents and Settings\wimar.GRAFIDRUK\My Documents\Avans School\BLOK 12\Project RF Sniffer\PC Applicatie\Sniffer\UsbLibrary\HIDDevice.cs:line 111
at UsbLibrary.HIDDevice.FindDevice(Int32 nVid, Int32 nPid, Type oType) in C:\Documents and Settings\wimar.GRAFIDRUK\My Documents\Avans School\BLOK 12\Project RF Sniffer\PC Applicatie\Sniffer\UsbLibrary\HIDDevice.cs:line 257
What's Wrong ?
|
|
|
|
|
The error goes from HIDDevice.cs (Ln.265)
oNewDevice.Initialise(strDevicePath);
where the strDevicePath = "\\\\?\\hid#vid_04d8&pid_ff9d#6&3aa3f0e7&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
|
|
|
|
|
I have got to same problem with Mr.Iwan Budihalim.
oNewDevice.Initialise(strDevicePath);
where the strDevicePath = "\\?\hid#vid_067e&pid_0100#6&369a9828&0&0000#{4d1e55b2-f16f-11cf-88cb-001111000030}"
I want to get data of barcode reader. Please help me!
Hoac Tai Thien
|
|
|
|
|
Same problem with same error as above.
I'm running WinXP and compiling with Visual C# 2008 Express.
Anybody found the reason????
|
|
|
|
|
I have had the same problems. I changed tehe parameters FILE_SHARE_READ | FILE_SHARE_WRITE in the CreatFile function.
Change the code in HIDDevice.cs and try one of this solutions:
// Create the file from the device path
// m_hHandle = CreateFile(strPath, GENERIC_READ | GENERIC_WRITE, 0,
// IntPtr.Zero, Win32Usb.OPEN_ALWAYS, FILE_FLAG_OVERLAPPED, IntPtr.Zero);
// NEW --- 1. Solution
m_hHandle = CreateFile(strPath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_WRITE | FILE_SHARE_READ, // changed from 0
IntPtr.Zero,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
IntPtr.Zero);
// NEW --- 2. Solution
m_hHandle = CreateFile(
DeviceName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
regards Jan
|
|
|
|
|
It's Working Thank you
|
|
|
|
|
|
Thank youuuuu!
|
|
|
|
|
How to get Product Name & Vendor Name of HID Device using C# ?
Thanks.
|
|
|
|
|
In the HIDDevice file there is a method called FindDevice(int nVid, int nPid, Type oType), in this method you will find code to get the product en vendor id. by using the GetDevicePath(hInfoSet, ref oInterface); will return a string with informatie like vendor and product id.
greetings wimar
|
|
|
|
|