I am working on a program that is writing to an HID device and am getting the error 87, Invalid parameter on the WriteFile function. I got the functions from Jan Axelson's USB Complete so I'm not sure why I am getting the error.
I am using this to find my device:
private void USBInit()
{
IntPtr deviceInfoSet;
Int32 memberIndex = 0;
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
Int32 bufferSize = 0;
IntPtr detailDataBuffer;
Boolean success = false;
deviceFound = false;
HidD_GetHidGuid(ref hidGuid); // Get the GUID
deviceInfoSet = SetupDiGetClassDevs // Get pointer to a device info set
(ref hidGuid,
IntPtr.Zero,
IntPtr.Zero,
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
do
{
MyDeviceInterfaceData.cbSize = Marshal.SizeOf(MyDeviceInterfaceData); // Identify Device Interface
success = SetupDiEnumDeviceInterfaces
(deviceInfoSet,
IntPtr.Zero,
ref hidGuid,
memberIndex,
ref MyDeviceInterfaceData);
success = SetupDiGetDeviceInterfaceDetail // Request Structure with Device Path Name
(deviceInfoSet,
ref MyDeviceInterfaceData,
IntPtr.Zero,
0,
ref bufferSize,
IntPtr.Zero);
detailDataBuffer = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(detailDataBuffer, (IntPtr.Size == 4) ? (4 + Marshal.SystemDefaultCharSize) : 8);
success = SetupDiGetDeviceInterfaceDetail
(deviceInfoSet,
ref MyDeviceInterfaceData,
detailDataBuffer,
bufferSize,
ref bufferSize,
IntPtr.Zero);
IntPtr pDevicePathName = new IntPtr(detailDataBuffer.ToInt32() + 4);
devicePathName = Marshal.PtrToStringAuto(pDevicePathName);
Marshal.FreeHGlobal(detailDataBuffer);
/* Request Communications Handle */
deviceHandle = CreateFile
(devicePathName,
(GENERIC_WRITE | GENERIC_READ),
FILE_SHARE_READ | FILE_SHARE_WRITE,
IntPtr.Zero,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
0);
/* Get Vendor ID and Product ID */
DeviceAttributes.Size = Marshal.SizeOf(DeviceAttributes);
success = HidD_GetAttributes(deviceHandle, ref DeviceAttributes);
// Compare Vendor ID and Product ID.
if ((DeviceAttributes.VendorID == myVendorID) && (DeviceAttributes.ProductID == myProductID))
{
MessageBoxResult res = System.Windows.MessageBox.Show("Device Found", "K-IX", MessageBoxButton.OK);
deviceFound = true;
/* Get pointer to capabilities */
success = HidD_GetPreparsedData(deviceHandle, ref preparsedData);
/* Get Device Capabilities */
Int32 result = 0;
result = HidP_GetCaps(preparsedData, ref Capabilities);
return;
}
else
{
// Not my device
memberIndex++;
deviceHandle.Close();
if (memberIndex == 128)
{ break; }
}
} while (!deviceFound);
}
And this is the code I am using to try to send to the device:
private void SendUSB()
{
Int32 numberOfBytesWritten = 0;
Byte[] outputReportBuffer = null;
Boolean success;
Boolean success2;
// Set size of the Output report buffer.
Array.Resize(ref outputReportBuffer, Capabilities.InputReportByteLength);
// Store Report ID in first byte of header.
outputReportBuffer[0] = 0;
// Store report data following the Report ID.
outputReportBuffer[1] = 0x01;
//outputReportBuffer[2] = 0x02;
// outputReportBuffer[3] = 0x03;
// Send Report
success = WriteFile
(deviceHandle,
outputReportBuffer,
outputReportBuffer.Length,
ref numberOfBytesWritten,
IntPtr.Zero);
if (!success)
{
Int32 lastError = Marshal.GetLastWin32Error();
}
success2 = HidD_FreePreparsedData(preparsedData);
}
I have verified that my report length expected for the device is 2, but am not sure where to go from here as USB and HID programming are new for me.
You specified FILE_FLAG_OVERLAPPED in the CreateFile() call but pass a null for the lpOverlapped argument in the WriteFile() call. That's not legal.
Remove the FILE_FLAG_OVERLAPPED option.
Related
I'm trying to find the vendor id and the product id of a USB card. For this purpose, I use setupapi.dll. In my code, I use the SetupDiGetDeviceInterfaceDetail call twice and on the second time, the function returns true with no error, but I don't know what I can do after that.
My code:
result = HidD_GetHidGuid(ref HidGuid);
DeviceInfoSet = SetupDiGetClassDevs(ref HidGuid, IntPtr.Zero, IntPtr.Zero, 18);
do
{
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
MyDeviceInterfaceData.cbSize = 28;
result = SetupDiEnumDeviceInterfaces(DeviceInfoSet, IntPtr.Zero, ref HidGuid,
MemberIndex, ref MyDeviceInterfaceData);
if (result != 0)
{
result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, ref MyDeviceInterfaceData,
IntPtr.Zero, 0, out neededSize, IntPtr.Zero);
//int lastError = Marshal.GetLastWin32Error();
int requiredSize = (int)neededSize;
// build a DevInfo Data structure
SP_DEVINFO_DATA da = new SP_DEVINFO_DATA();
da.cbSize = Marshal.SizeOf(da);
SP_DEVICE_INTERFACE_DETAIL_DATA MyDeviceInterfaceDetailData = new
SP_DEVICE_INTERFACE_DETAIL_DATA();
if (IntPtr.Size == 8) // for 64 bit operating systems
MyDeviceInterfaceDetailData.cbSize = 8;
else
MyDeviceInterfaceDetailData.cbSize = 4 + Marshal.SystemDefaultCharSize;
// for 32 bit systems
IntPtr detailDataBuffer = Marshal.AllocHGlobal(requiredSize);
Marshal.StructureToPtr(MyDeviceInterfaceDetailData, detailDataBuffer, false);
result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, ref MyDeviceInterfaceData,
detailDataBuffer, requiredSize, out neededSize, IntPtr.Zero);
int lastError = Marshal.GetLastWin32Error();
When I try the following code, the result is "-" :
string instanceID = Marshal.PtrToStringAuto(detailDataBuffer);
How can I get the vendor ID and the product ID?
In the SP_DEVICE_INTERFACE_DETAIL_DATA struct you find the DevicePath, which is something like \?\hid#vid_045e&pid_00f0#7&13ac544b&0&0000#{GUID}. Just fiddle out the vid_045e and pid_00f0 part.
I'm trying to find the vendor id and the product id of a USB card. For this purpose, I use setupapi.dll. When I closed my program I have 0x80131623 (2146232797) appears. I don't know how I can fix it.
My code is :
result = HidD_GetHidGuid(ref HidGuid);
DeviceInfoSet = SetupDiGetClassDevs(ref HidGuid, IntPtr.Zero, IntPtr.Zero, 18);
do
{
SP_DEVICE_INTERFACE_DATA MyDeviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
MyDeviceInterfaceData.cbSize = 28;
result = SetupDiEnumDeviceInterfaces(DeviceInfoSet, IntPtr.Zero, ref HidGuid, MemberIndex, ref MyDeviceInterfaceData);
//int lastError = Marshal.GetLastWin32Error();
if (result != 0)
{
//SP_DEVINFO_DATA MyDeviceInfoData = new SP_DEVINFO_DATA();
//MyDeviceInfoData.cbSize = Marshal.SizeOf(MyDeviceInfoData);
//Premier appel pour obtenir la taille du buffer
result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, ref MyDeviceInterfaceData, IntPtr.Zero, 0, out neededSize, IntPtr.Zero);
//int lastError = Marshal.GetLastWin32Error();
int requiredSize = (int)neededSize;
SP_DEVICE_INTERFACE_DETAIL_DATA MyDeviceInterfaceDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
if (IntPtr.Size == 8) // for 64 bit operating systems
MyDeviceInterfaceDetailData.cbSize = 8;
else
MyDeviceInterfaceDetailData.cbSize = 4 + Marshal.SystemDefaultCharSize; // for 32 bit systems
IntPtr detailDataBuffer = Marshal.AllocHGlobal(requiredSize);
Marshal.StructureToPtr(MyDeviceInterfaceDetailData, detailDataBuffer, true);
result = SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, ref MyDeviceInterfaceData, detailDataBuffer, requiredSize, out neededSize, IntPtr.Zero);
int lastError = Marshal.GetLastWin32Error();
//string instanceID = Marshal.PtrToStringAuto(detailDataBuffer);
Marshal.PtrToStructure(detailDataBuffer, MyDeviceInterfaceDetailData);
if (MyDeviceInterfaceDetailData.DevicePath.IndexOf("vid_04fa&pid_9123") != -1)
{
Marshal.FreeHGlobal(detailDataBuffer);
Marshal.FreeHGlobal(DeviceInfoSet);
return true;
}
MemberIndex++;
Marshal.FreeHGlobal(detailDataBuffer);
}
else
{
//int lastError = Marshal.GetLastWin32Error();
}
if (MemberIndex>=16) { lastDevice = true; }
} while (lastDevice == false);
Marshal.FreeHGlobal(DeviceInfoSet);
return false;
}
I think the problem is caused by Marshaling.
I'm sending IOCTL's to my keyboard filter driver and the code is as follows:
Guid GUID_DEVINTERFACE_KBFILTER = new Guid(0x3fb7299d, 0x6847, 0x4490, 0xb0, 0xc9, 0x99, 0xe0, 0x98, 0x6a, 0xb8, 0x86);
IntPtr handle = SetupDiGetClassDevs(ref GUID_DEVINTERFACE_KBFILTER, IntPtr.Zero, IntPtr.Zero, (int)(DiGetClassFlags.DIGCF_PRESENT | DiGetClassFlags.DIGCF_DEVICEINTERFACE));
if (handle != INVALID_HANDLE_VALUE)
{
bool Success = true;
int i = 0;
while (Success)
{
SP_DEVICE_INTERFACE_DATA deviceInterfaceData = new SP_DEVICE_INTERFACE_DATA();
deviceInterfaceData.cbSize = Marshal.SizeOf(deviceInterfaceData);
// start the enumeration
Success = SetupDiEnumDeviceInterfaces(handle, IntPtr.Zero, ref GUID_DEVINTERFACE_KBFILTER, (uint)i, ref deviceInterfaceData);
if (Success)
{
// build a DevInfo Data structure
SP_DEVINFO_DATA devInfoData = new SP_DEVINFO_DATA();
devInfoData.cbSize = (uint)Marshal.SizeOf(devInfoData);
// build a Device Interface Detail Data structure
deviceInterfaceDetailData = new SP_DEVICE_INTERFACE_DETAIL_DATA();
deviceInterfaceDetailData.cbSize = 4 + Marshal.SystemDefaultCharSize;
// now we can get some more detailed information
uint nRequiredSize = 0;
int nBytes = BUFFER_SIZE;
if (SetupDiGetDeviceInterfaceDetail(handle, ref deviceInterfaceData, ref deviceInterfaceDetailData, (uint)nBytes, out nRequiredSize, ref devInfoData))
{
uint ptrPrevious;
CM_Get_Parent(out ptrPrevious, devInfoData.DevInst, 0);
// Now we get the InstanceID of the USB level device
IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(nBytes);
CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, nBytes, 0);
string InstanceID = Marshal.PtrToStringAuto(ptrInstanceBuf);
Marshal.FreeHGlobal(ptrInstanceBuf);
}
}
i++;
}
}
SetupDiDestroyDeviceInfoList(handle);
if (string.IsNullOrEmpty(deviceInterfaceDetailData.DevicePath))
{
return false;
}
The code after this works fine. The problem lies here and this works on a 32bit machine but does not work on 64 bit machines.In 64bit machines, deviceInterfaceDetailData.DevicePath is empty where as in 32 bit machines I get a valid device path.Is there something wrong in the build process?
I got the solution to this issue. The problem was the deviceInterfaceDetailData.cbSize value set. According to http://www.pinvoke.net/default.aspx/setupapi.setupdigetdeviceinterfacedetail I changed the cbSize value to 8 for 64 bit machines and then it works!
I am trying to tell if a hardware device (specifically multitouch digitizer) is disabled.
I can successfully enable/disable the device using the DisableHardware class from here.
I have found the DEVICE_CAPABILITIES struct with property HardwareDisabled. But if I call SetupDiGetDeviceRegistryPropertyW, to return SPDRP_CAPABILITIES, I am unable to convert the returned value (DWORD) to useful uint.
I believe my conversion is wrong from byte[] => uint, I always get 128.
private static bool IsDeviceHardwareDisabeld(IntPtr info, SP_DEVINFO_DATA devdata)
{
Trace.WriteLine("IsDeviceHardwareDisabeld");
uint SPDRP_CAPABILITIES = 0x0000000F;
uint CM_DEVCAP_HARDWAREDISABLED = 16384u;
uint propId = SPDRP_CAPABILITIES;
uint proptype, outsize;
IntPtr buffer = IntPtr.Zero;
try
{
uint buflen = 512;
buffer = Marshal.AllocHGlobal((int)buflen);
outsize = 0;
SetupDiGetDeviceRegistryPropertyW(
info,
ref devdata,
propId,
out proptype,
buffer,
buflen,
ref outsize);
Trace.WriteLine("OUTSIZE: " + outsize.ToString()); // 4
byte[] lbuffer = new byte[outsize];
Marshal.Copy(buffer, lbuffer, 0, (int)outsize);
int errcode = Marshal.GetLastWin32Error();
if (errcode == ERROR_INVALID_DATA) throw new Exception("ERROR_INVALID_DATA");
CheckError("SetupDiGetDeviceRegistryPropertyW", errcode);
uint capabilities = BitConverter.ToUInt32(lbuffer, 0); // always 128
uint bitwise = capabilities & CM_DEVCAP_HARDWAREDISABLED; // always 0
bool isHardwareDisabled = bitwise > 0;
Trace.WriteLine("HARDWAREDISABLED: " + isHardwareDisabled.ToString());
return isHardwareDisabled;
}
finally
{
if (buffer != IntPtr.Zero)
Marshal.FreeHGlobal(buffer);
}
}
My conversion always returns 128 for value of DEVICE_CAPABILITIES.
I have the following code to import the IExtractImage interface.
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1")]
public interface IExtractImage
{
[PreserveSig]
Int32 GetLocation([MarshalAs(UnmanagedType.LPWStr)] out StringBuilder pszPathBuffer,
int cch,
ref int pdwPriority,
SIZE prgSize,
int dwRecClrDepth,
ref int pdwFlags);
[PreserveSig]
Int32 Extract(out IntPtr phBmpThumbnail);
}
I also have the IShellFolder imported, which I am not mentioning here for brevity. My intention is to access the thumbnail of a file using the shellfolder. So here's my code to retrieve the thumbnail. But my call to IExtractImage.GetLocation() is failing with a NullReference exception, stating
"An exception of type 'System.NullReferenceException' occurred in CCDash.exe but was not handled in user code
Additional information: Object reference not set to an instance of an object."
Can someone please help me identify what is it that I am missing?
public Bitmap GetThumbNail(string mediaFileName)
{
IShellFolder shellDesktop;
SHGetDesktopFolder(out shellDesktop);
IntPtr pidlRoot;
uint attribute = 0;
string mediaPath = "C:\\Users\\<user>\\Videos"; // I have hard-coded the path for now
uint pchEaten = 0;
// Get the pidl of the media folder
shellDesktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaPath, ref pchEaten, out pidlRoot, ref attribute);
Guid mediaFolderGuid = new Guid("000214E6-0000-0000-C000-000000000046");
shellDesktop.BindToObject(pidlRoot, IntPtr.Zero, mediaFolderGuid, out shellMediaFolder);
IntPtr pidlMediaFolder;
// Get the pidl of the media file
shellMediaFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaFileName, ref pchEaten, out pidlMediaFolder, ref attribute);
Guid mediaFileImgGuid = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
uint rfgRes = 0;
IExtractImage extractImage;
shellMediaFolder.GetUIObjectOf(IntPtr.Zero, 1, out pidlMediaFolder, mediaFileImgGuid, ref rfgRes, out extractImage);
SIZE size = new SIZE
{
cx = 40,
cy = 40
};
int flags = 0x40 | 0x40;
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
IntPtr hBmp = IntPtr.Zero;
// Now get the image
extractImage.GetLocation(out location, location.Capacity, ref priority, size, requestedColourDepth, ref flags);
extractImage.Extract(out hBmp);
Bitmap thumbnail = Image.FromHbitmap(hBmp);
return thumbnail;
}
EDIT:
I have now modified my code as below. Not very different from the first version, but with a little more error handling and better documentation and variable names that can help us understand my code better. Here's the modified code:
public Bitmap GetThumbNail(string mediaFileName)
{
Bitmap thumbnail = null;
//Step 1: Use SHGetDesktopFolder to get the desktop folder.
IShellFolder shellDesktop;
SHGetDesktopFolder(out shellDesktop);
if (shellDesktop != null)
{
IntPtr pidlMediaFolder;
try
{
uint attribute = 0;
string mediaPath = Path.GetDirectoryName(mediaFileName);
uint pchEaten = 0;
// Step 2: Using the desktop's IShellFolder, pass the file's parent folder path name into ParseDisplayName to get its PIDL.
shellDesktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaPath, ref pchEaten, out pidlMediaFolder, ref attribute);
}
catch (Exception)
{
Marshal.ReleaseComObject(shellDesktop);
return null;
}
if (pidlMediaFolder != IntPtr.Zero)
{
Guid mediaFolderGuid = new Guid("000214E6-0000-0000-C000-000000000046");
IShellFolder shellMediaFolder;
// Step 3: Using the desktop's IShellFolder, pass the PIDL into the BindToObject method
// and get the IShellFolder interface of the file's parent folder.
try
{
shellDesktop.BindToObject(pidlMediaFolder, IntPtr.Zero, mediaFolderGuid, out shellMediaFolder);
}
catch (Exception)
{
Marshal.ReleaseComObject(shellDesktop);
return null;
}
if (shellMediaFolder != null)
{
IntPtr pidlMediaFile;
uint attribute = 0;
uint pchEaten = 0;
// Step 4: Using the parent folder's IShellFolder, pass the file name into ParseDisplayName to get its PIDL.
int ret = shellMediaFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, mediaFileName, ref pchEaten, out pidlMediaFile, ref attribute);
Guid mediaFileImgGuid = new Guid("BB2E617C-0920-11d1-9A0B-00C04FC2D6C1");
uint rfgRes = 0;
IExtractImage extractImage;
// Step 5: Using the parent folder's IShellFolder, pass the file's PIDL
// into the GetUIObjectOf. method to get the IExtractImage interface.
ret = shellMediaFolder.GetUIObjectOf(IntPtr.Zero, 1, out pidlMediaFile, mediaFileImgGuid, ref rfgRes, out extractImage);
SIZE size = new SIZE
{
cx = 40,
cy = 40
};
uint flags = 0x0200;
StringBuilder location = new StringBuilder(260, 260);
int priority = 0;
int requestedColourDepth = 0x20;
IntPtr hBmp = IntPtr.Zero;
// Now get the image
extractImage.GetLocation(out location, location.Capacity, ref priority, size, requestedColourDepth, ref flags);
extractImage.Extract(out hBmp);
thumbnail = Image.FromHbitmap(hBmp);
}
}
}
return thumbnail;
}
I see that at step 4, the pidlMediaFile is not retrieved correctly and it's value is still 0 after the ParseDisplayName() call. This is where the problem begins. I am not sure why the pidl for the filename is not retrieved whereas it is retrieved successfully for the file's parent folder.
It looks like extractImage is being returned null from GetUIObjectOf(). Try checking the HRESULT return value from GetUIObjectOf() to find out why a null value is being returned.
EDIT:
From http://msdn.microsoft.com/en-us/library/windows/desktop/aa378137%28v=vs.85%29.aspx:
E_INVALIDARG One or more arguments are not valid 0x80070057
According to the documentation, the third argument is an [in] argument, not an [out] argument.