|
Hello man,
First, thank's for that article and component. It's a great job who help me a lot.
But, I still have a problem: I develop a WPF application, and the sniffer library component seems to be incompatible with wpf, as it require a winform. I dont' know how to deal with that...
Moreover, sniffer is very coupled with the winform. Have you ever think of creating an stand-alone component ?
Why not to place the winform inside the library and supply personalized events ?
Loïc
Loïc Berthollet
|
|
|
|
|
Thanks for the code, it's helping me find out more about HID.
What I'm trying to do is to detect a specific device based on PID and VID (in my case a barcode scanner) then when found I want to capture all input from that device regardless of whether my app has focus or not (ideally my app will run as a windows service).
Currently running your test app i can't even detect my particular device, when I enter the vid and pid (as found from running msinfo32.exe) it detects a device being connected and disconnected but not the specified device.
If you could offer any guidance I would be very grateful. Could you also get in touch if you are available for contract work?
Dan
danmoov2@googlemail.com
|
|
|
|
|
I was able to use this code and receive data from the USB barcode scanner.
What model of scanner are you using?
I'm using a product from Hand Held Products (model 3800g)
3800 General Purpose Linear Image Scanner[^]
The model that I have, by default is setup as a keyboard/wedge. I had to scan a programming barcode to make it a USB HID device. By doing this it also changed the Product ID from 02E1 to 02E7. There is an option to use a USB-COM emulator that allows the device to communicate with a virtual RS232 port.
|
|
|
|
|
hi man
great job!
i have been trying to catch the IR codes from my remote control for more than a month and i was too lazy to dig into HID myself.
but your application did the trick.
thanks again
|
|
|
|
|
Hi!
I'm trying to send data report to my USB device, but with no luck (i'm using your Sniffer example) It should control the LED blinking.
The SendData method doesn't work. Are you sure about writing to the USB device using FileStream ?
I hacked the USB Library and used HidD_SetFeature method and it worked. Can you please explain me how your report writing works ? I think it's a bit confusing.
Thx.
|
|
|
|
|
Hi
You have to write another method to handle the reports.
Steps For Handling Reports:
1) In win32usb.cs, add
HidD_SetOutputReport() for handling output reports and HidD_GetInputReport() for handling input reports.
-------------------------------------------------------------------------------------------
/// Send an output report to a HID device.
/// </summary>
/// <param name="HidDeviceObject">A handle to a Hid Device Object.</param>
/// <param name="ReportBuffer">The buffer of the output report to send to the device.</param>
/// <param name="ReportBufferLength">The size (in bytes) of ReportBuffer.</param>
/// <returns>'true' if successful.'false' otherwise.</returns>
[DllImport("hid.dll", SetLastError = true)] protected static extern bool HidD_SetOutputReport(IntPtr HidDeviceObject, byte[] ReportBuffer, int ReportBufferLength);
/// <summary>
/// Retrieve an input report from a HID device.
/// <param name=" HidDeviceObject">A handle to a Hid Device Object.</param>
/// <param name="ReportBuffer">The buffer that the input report should be placed into.The first byte of the buffer should be set to the report ID of the desired report.</param>
/// <param name="ReportBufferLength">The size (in bytes) of ReportBuffer. This value should be greater than or equal to the InputReportByteLength field as specified in the HIDP_CAPS structure for the device.</Param>
///<returns>'true' if successful.'false' otherwise. </returns>
[DllImport("hid.dll", SetLastError = true)] protected static extern bool HidD_GetInputReport(IntPtr HidDeviceObject, byte[] ReportBuffer, int ReportBufferLength);
-------------------------------------------------------------------------------------------
2) Write methods sendReport() and getReport() in HIDDevice.cs
protected void sendReport(OutputReport oOutRep)
{
try
{
// Call SetOutputReport to send this report buffer over the control pipe
HidD_SetOutputReport(m_hHandle, oOutRep.Buffer, oOutRep.BufferLength);
}
catch (Exception exx)
{
Console.WriteLine(exx.ToString());
}
}
protected void getReport(byte[] data,byte buffersize)
{
try
{
//Call GetInputReport to get the requested report buffer over the control pipe
HidD_GetInputReport(m_hHandle, data, buffersize);
}
catch (Exception exx)
{
Console.WriteLine(exx.ToString());
}
}
-------------------------------------------------------------------------------------------
3) In SpecifiedDevice.cs,
In class, public class SpecifiedDevice : HIDDevice write set_report() and get_report()
//set the request to send the report buffer over the control pipe.
public void set_report(byte[] data, byte buffersize)
{
SpecifiedOutputReport oRep = new SpecifiedOutputReport(this);
oRep.set_report(data, buffersize);
try
{
sendReport(oRep);
}
catch (HIDDeviceException ex)
{
}
}
//Receive the requested report buffer over the control pipe.
public void get_report(byte[] data,byte buffersize)
{
try
{
getReport(data,buffersize);
}
catch (HIDDeviceException ex)
{
}
}
-------------------------------------------------------------------------------------------
4) In SpecifiedOutputReport.cs,
public bool set_report(byte[] data, byte buffersize)
{
byte[] arrbuff = Buffer;
for (int i = 0; i < buffersize; i++)
{
arrbuff[i] = data[i];
}
if (arrbuff.Length < buffersize)
{
return false;
}
else
{
return true;
}
}
-------------------------------------------------------------------------------------------
5) In SpecifiedInputReport.cs,
public void get_report(byte[] inputbuffer)
{
inputbuffer = Buffer;
}
-------------------------------------------------------------------------------------------
Thank you,
|
|
|
|
|
hi mdeepa. thank u for your modifiation. i added this modification but i havent used them. do i have to do something else or is this enough??
and thx wimar for this
|
|
|
|
|
Hello,
I also tried to run the program, but as i debugged it, it seems it can't find my HID device.
My HID device vendor id and product id is correct still it returns false form "SetupDiEnumDeviceInterfaces" function.
In the "while" statement :
while (SetupDiEnumDeviceInterfaces(hInfoSet, 0, ref gHid, (uint)nIndex, ref oInterface))
{....}
It always returns false...
Please let me know solution for this asap.
Thanks.
Samir Karve
|
|
|
|
|
Add FILE_SHARE_READ | FILE_SHARE_WRITE in the CreateFile function:
m_handle = CreateFile(
DeviceName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0,
OPEN_EXISTING,
0,
0);
regards Jan
|
|
|
|
|
I managed to get the received data side of the USB HID component working with my AT89C5131 USB Development board but not the send data. I assume judging by all the "000 000 000 000" it puts in the send data field that it sends a string of data. Can anyone point me in the direction in the USB HID code to a spot where I can modify it so i only send one charactor of data, eg. if only I want to send the number 1?
|
|
|
|
|
This seems not to work on 64 bit Vista (and possibly XP x64, I have not tested). Any ideas how to get it to enumerate the devices? At the moment it does not error but enumerates to nothing?
|
|
|
|
|
Ok, here was the bug. I have not looked for other similar bugs.
In the definition of the struct DeviceInterfaceData, "Reserved" is given the type int. In the documentation it should correspond to ULONG_PTR. This means that UIntPtr (or maybe IntPtr) should be used instead. This is confirmed to work on 32 and 64 bit systems.
|
|
|
|
|
|
That is also my thread. I made it at about the same time as I posted the last comment.
Please look at my sourceforge project http://sourceforge.net/projects/libhidnet[^][^]. It runs on 32 and 64 bit Windows as well as Linux (and anything else with Mono and hiddev - I haven't checked which systems have hiddev). I haven't had any feedback on it yet or any suggestions but they are very welcome as I don't have much time to work on it.
|
|
|
|
|
Thanks again for the two fixes. Now that my code, mostly derived from this project, is working with 64-bit Windows I'm afraid I'm not motivated to do anything with your new project. But you are to be lauded for having done it and made it available. Thanks.
Rennie
|
|
|
|
|
Do you have a build with the 64-bit support you could provide me with? I've tried to download and build it myself but the result is a 18kb (compared to the builds in the projects builds) file which works with one device but not with another so I'm not sure the conversion made by visual 2008 was correct...
Thanks in advance // Magnus
|
|
|
|
|
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).
|
|
|
|
|