DeviceIoControl assistance - c++

Using C++, trying to use DeviceIOControl to send mouse clicks directly through driver. Very little documentation seems to exist on this. Very annoying. This is all I figured out so far. Have no idea what goes into any parameter after dwIoControlNode. I've seen keyboard examples, but no mouse ones.
EDIT: Appreciate your help Remy, will try your suggestions tomorrow, been working on this all day and am braindead.
int i = 0;
GUID MouseyBoi;
HANDLE HIDD = NULL;
HIDD_ATTRIBUTES DeviceAttributes;
ULONG Needed, l;
HDEVINFO DeviceInfoSet;
SP_DEVICE_INTERFACE_DATA DevData;
PSP_INTERFACE_DEVICE_DETAIL_DATA DevDetail;
PSP_DEVICE_INTERFACE_DATA InfData;
DeviceInfoSet = SetupDiGetClassDevs(&GUID_DEVCLASS_MOUSE, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
SetupDiGetDeviceInterfaceDetail(DeviceInfoSet, &DevData, NULL, 0, &Needed, 0);
DevDetail = (SP_DEVICE_INTERFACE_DETAIL_DATA*)GlobalAlloc(GPTR, 1 + 4);
DevDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
HIDD = CreateFile(DevDetail->DevicePath, GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, 0, NULL);
HidD_GetAttributes(HIDD, &DeviceAttributes);
std::cout << DeviceAttributes.VersionNumber << std::endl;
std::cout << DeviceAttributes.ProductID;

First off, you need to escape \ characters in a string literal, eg:
LPCWSTR FileName = L"USB\\VID_046D&PID_C07E&MI_00\\6&361cde46&0&0000";
And second, this is not a valid file/device path that you can open with CreateFile() anyway. It should look more like this:
LPCWSTR FileName = L"\\\\?\\USB#VID_046D&PID_C07E&MI_00#6&361cde46&0&0000#{<guid>}";
You can use SetupDiGetDeviceInterfaceDetail() to get a device path that CreateFile() will accept.
That being said, once you do manage to open a handle to the USB device, the DeviceIoControl() call should look like the following, since you are just inserting input data, not getting any output back out (but I could be wrong, as there is no documentation for IOCTL_MOUSE_INSERT_DATA that I can find):
MOUSE_INPUT_DATA MouseInput = {};
MouseInput.UnitId = 0;
MouseInput.ButtonFlags = MOUSE_LEFT_BUTTON_DOWN;
// set other fields as needed ...
DeviceIoControl(hFile, IOCTL_MOUSE_INSERT_DATA, &MouseInput, sizeof(MOUSE_INPUT_DATA), NULL, 0, NULL, NULL);

Related

Windows C++ -- Using DeviceIoControl to return a product string

I am trying to return the "product string" for a HID using DeviceIoControl in Windows. Here is my code:
for (int i = 0; i < nDevices; i++)
{
wchar_t* productString = new wchar_t[POINTER_DEVICE_PRODUCT_STRING_MAX];
HANDLE potentialUsbDevice = CreateFile(devInfoDataBuffer[i]->DevicePath, GENERIC_READ,
NULL,
NULL,
OPEN_EXISTING,
0,
NULL);
if (potentialUsbDevice != INVALID_HANDLE_VALUE) {
DeviceIoControl(potentialUsbDevice, IOCTL_HID_GET_PRODUCT_STRING, 0, 0, productString, 0, 0, 0);
wprintf(L"%s", productString);
}
delete[] productString;
CloseHandle(potentialUsbDevice);
}
Unfortunately, I am not getting anything human-readable. Is there something wrong with my function call or is it an issue of formatting?
Thank you for your help and consideration on this topic.
You need to pass the size of the output buffer (in bytes) to DeviceIoControl. You also need to pass lpBytesReturned as non-NULL:
DWORD bytes_returned;
DeviceIoControl (potentialUsbDevice, IOCTL_HID_GET_PRODUCT_STRING, 0, 0,
productString, POINTER_DEVICE_PRODUCT_STRING_MAX * sizeof (wchar_t),
&bytes_returned, 0);
Also, check for errors (not shown in my code for brevity) and call CloseHandle inside your if statement.

DeviceIoControl() with IOCTL_DISK_GET_DRIVE_GEOMETRY is failing and returning error code 87. Why?

Relevant code is as follows:
std::wstring path = ApplicationData::Current->LocalFolder->Path->Data();
std::wstring testFileName = path + std::wstring(L"\\TestVariablySized");
this->hMappedFile = CreateFile2(
testFileName.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_ALWAYS,
NULL);
uint32_t checkF = GetLastError();
DISK_GEOMETRY geo = { 0 };
DWORD bReturned = 0;
bool controlCheck = DeviceIoControl(
(HANDLE)hMappedFile, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
(LPVOID)&geo, // output buffer
(DWORD)sizeof(geo), // size of output buffer
(LPDWORD)&bReturned, // number of bytes returned
NULL);
uint32_t check = GetLastError();
After this, controlCheck is false and check is ERROR_INVALID_PARAMETER. checkF is ERROR_ALREADY_EXISTS, which shouldn't be a problem here.
As far as I can tell, I've called DeviceIoControl() in a way consistent with the IOCTL_DISK_GET_DRIVE_GEOMETRY documentation.
, but clearly I'm missing something. Your help is most appreciated.
Edit:
Per responses received, I altered things to be as follows:
HANDLE hDevice = CreateFile2(
L"\\.\PhysicalDrive0",
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
OPEN_EXISTING,
NULL);
uint32_t checkF = GetLastError();
DISK_GEOMETRY geo = { 0 };
DWORD bReturned = 0;
bool controlCheck = DeviceIoControl(
hDevice, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
(LPVOID)&geo, // output buffer
(DWORD)sizeof(geo), // size of output buffer
(LPDWORD)&bReturned, // number of bytes returned
NULL);
uint32_t check = GetLastError();
CloseHandle(hDevice);
Which should be closer to being correct, even if it's not quite correct yet. checkF is ERROR_FILE_NOT_FOUND, which I found strange. I tried "\\.\PhysicalDrive1" and "\\.\PhysicalDrive2" as well, but receive the same result. controlCheck is still false, but check is now ERROR_INVALID_HANDLE.
As far as I can tell, I've called DeviceIoControl() in a way consistent with the IOCTL_DISK_GET_DRIVE_GEOMETRY documentation
Actually, you are not, because you did not pay attention to this tidbit of the documentation:
hDevice
A handle to the disk device from which the geometry is to be retrieved. To retrieve a device handle, call the CreateFile function.
You are not passing a handle to a disk device, you are passing a handle to a filesystem path instead.
When calling CreateFile2() to get a handle to a disk device, you need to specify a physical device in \\.\PhysicalDriveX format instead, not a filesystem path.
Also, as the CreateFile2() documentation says:
The following requirements must be met for such a call to succeed:
The caller must have administrative privileges. For more information, see Running with Special Privileges.
The dwCreationDisposition parameter must have the OPEN_EXISTING flag.
When opening a volume or floppy disk, the dwShareMode parameter must have the FILE_SHARE_WRITE flag.
You are using OPEN_ALWAYS instead of OPEN_EXISTING.
Please read the "Physical Disks and Volumes" section of the CreateFile2() documentation more carefully.
Try something more like this instead:
std::wstring path = L"\\\\.\\PhysicalDrive0";
DWORD errCode;
hMappedFile = CreateFile2(
path.c_str(),
GENERIC_READ | GENERIC_WRITE,
0,
OPEN_EXISTING,
NULL);
if (this->hMappedFile == INVALID_HANDLE_VALUE)
{
errCode = GetLastError();
// handle error as needed...
}
else
{
DISK_GEOMETRY geo = { 0 };
DWORD dwReturned = 0;
bool controlCheck = DeviceIoControl(
hMappedFile, // handle to device
IOCTL_DISK_GET_DRIVE_GEOMETRY, // dwIoControlCode
NULL, // lpInBuffer
0, // nInBufferSize
&geo, // output buffer
sizeof(geo), // size of output buffer
&dwReturned, // number of bytes returned
NULL);
if (!controlCheck)
{
errCode = GetLastError();
// handle error as needed...
}
else
{
// use drive as needed...
}
CloseHandle(hMappedFile);
}

Why does WriteFile not run more than once?

Here's my code in which I've got on an infinite loop (to my knowledge)
while(true) {
DWORD TitleID = XamGetCurrentTitleId();
std::ostringstream titleMessageSS;
titleMessageSS << "Here's the current title we're on : " << TitleID << "\n\n";
std::string titleMessage = titleMessageSS.str(); // get the string from the stream
DWORD dwBytesToWrite = (DWORD)titleMessage.size();
DWORD dwBytesWritten = 0;
BOOL bErrorFlag = FALSE;
HANDLE logFile = CreateFile( "Hdd:\\LOGFile.txt", GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
bErrorFlag = WriteFile(logFile, titleMessage.c_str(), dwBytesToWrite, &dwBytesWritten, NULL);
CloseHandle(logFile);
Sleep(30000);
}
return NULL;
Does anyone see a reason as to why this only writes just once? I've waited over 5 minutes to see if it does anything in the end to no avail.
The Flag CREATE_NEW in CreateFile prevents the update of the file because CreateFile fail with ERROR_FILE_EXISTS. Use OPEN_ALWAYS instead.
Also it will always truncate. Replace GENERIC_WRITE with FILE_APPEND_DATA if you want to add a new line at the end of your logfile.
The whole CreateFile line should be:
HANDLE logFile = CreateFile( "Hdd:\\LOGFile.txt", FILE_APPEND_DATA , 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
Read CreateFile documentation carefully, it worth it, because it has a central role in the windows IO universe:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
look also add:
https://stackoverflow.com/a/9891875/1922748
As Martin James mentioned, from MSDN:
CREATE_NEW
Creates a new file, only if it does not already exist.
If the specified file exists, the function fails and the last-error
code is set to ERROR_FILE_EXISTS (80).
If the specified file does not exist and is a valid path to a writable
location, a new file is created.
So it seems that the handle is invalid after the first call, and hence WriteFile() fails.

How to connect to the bluetooth low energy device

I am writing program for Win 8 tablet. I need to connect an external BLE device.
The device is already paired with Windows and I can see it in Device Manager. But I can not figure out how to connect it.
With SetupDiEnumDeviceInfo and SetupDiGetDeviceProperty I can get some information about the BLE-device, but to perform, e.g. BluetoothGATTGetServices
Handle device requires. I do not know where to take it. Perhaps i can use CreateFile, but it is not clear that the substitute as the first argument lpFileName.
Here's a piece of code with which I'm looking for my device.
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(
&BluetoothClassGUID, /* GUID_DEVCLASS_BLUETOOTH */
0, 0, DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return ;//1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER){
// Change the buffer size.
if (buffer) delete(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = new wchar_t[buffersize * 2];
}else{
// Insert error handling here.
break;
}
}
/* Here i just compare by name is this my device or not */
...
/* Here i just compare by name is this my device or not */
if (buffer) delete(buffer);
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return; //1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
return;// 0;
I moved a little further, but still i can't get the data from device.
To obtain "Device Interface Path" had to use the other functions:
SetupDiGetClassDevs, SetupDiEnumDeviceInterfaces and SetupDiGetDeviceInterfaceDetail.
Next, with CreateFile I get HANDLE BLE-device.
hComm = CreateFile(pInterfaceDetailData->DevicePath, GENERIC_WRITE | GENERIC_READ,NULL,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, NULL);
Next using WinAPI BluetoothGATTGetServices and BluetoothGATTGetCharacteristics I get the appropriate structures.
But when trying to get the property value with BluetoothGATTGetCharacteristicsValue, I get ERROR_ACCESS_DENIED.
And then I do not know what to do. What could be wrong?
Andrey, I think the problem is that your device is not connected and BluetoothGATTGetCharacteristicsValue is not triggering a connection.
Try manually to connect your device using Windows tools. I've the following flow that helps me: Unpair device, Pair device -> It should appear as connected ( it worked in my case ;) )
Anyway, If this is not helping, try to run "As Administrator", this helps in some cases.
Good luck!!!
Note: Would be very interested to know how to retrievethe device path for the BTLE device in order to call BluetoothGATTGetServices?
gattServiceGUID is any long form BLE UUID supported by your device.
"{00001803-0000-1000-8000-00805F9B34FB"} can be used to open a handle to the Link Loss service if supported by the device you are trying to open
HDEVINFO hDevInfo = SetupDiGetClassDevs(&gattServiceGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo != INVALID_HANDLE_VALUE)
{
SP_DEVICE_INTERFACE_DATA interfaceData;
ZeroMemory(&interfaceData,sizeof(SP_DEVICE_INTERFACE_DATA));
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
for (DWORD dwDeviceIndex = 0; SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &gattServiceGUID, dwDeviceIndex, &interfaceData); dwDeviceIndex++)
{
dwDeviceCount++;
SetupDiGetDeviceInterfaceDetail(hDevInfo, &interfaceData, NULL, 0, &dwBytesNeeded, NULL);
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pInterfaceDetail = (PSP_INTERFACE_DEVICE_DETAIL_DATA) new byte[dwBytesNeeded];
SP_DEVINFO_DATA spDeviceInfoData = { sizeof(SP_DEVINFO_DATA) };
ZeroMemory(pInterfaceDetail, sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA));
pInterfaceDetail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
// grab the interface detail
if (SetupDiGetDeviceInterfaceDetail(hDevInfo, &interfaceData, pInterfaceDetail, dwBytesNeeded, NULL, &spDeviceInfoData) == TRUE)
{
// request a handle to the GATT service path
m_hGattServiceHandle = CreateFile(pInterfaceDetail->DevicePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (m_hGattServiceHandle != INVALID_HANDLE_VALUE)
{
now you can drill down the characteristics and descriptors with the m_hGattServiceHandle
}
}
}
}
}

WCHAR to LPCWSTR

when i use CreateFile function like below ...it gives me valid handle
HANDLE hDevice = CreateFile (TEXT("\\\\.\\G:"),
0,FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
if( hDevice == INVALID_HANDLE_VALUE )
{
qDebug()<<"In valid handle";
}
else
{
qDebug()<<"valid handle";
}
when i use like below ...it gives me invalid handle..
WCHAR Drive[4];
qDebug ()<<QString::fromWCharArray ( Drive );
The above prints like "G:\"
HANDLE hDevice = CreateFile ( Drive,
0,FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
NULL, OPEN_EXISTING, 0, NULL);
if( hDevice == INVALID_HANDLE_VALUE )
{
qDebug()<<"In valid handle";
}
else
{
qDebug()<<"valid handle";
}
How can i change the wchar to LPCWSTR
Thank you
The problem is not the conversion of the string, but the contents of the string. You can't open a volume (I guess that's what you're trying to do) with "G:\". It needs to be in the same format as you used in the first example. From MSDN:
When opening a volume or floppy drive,
the lpFileName string should be the
following form: \\.\X:. Do not use a
trailing backslash, which indicates
the root directory of a drive.
Hint: Always use GetLastError() after API functions fail to get the reason for the failure.
Update:
MSDN Link
You can either use the toWCharArray function or try something like this:
handle = CreateFile((LPCWSTR) fileName.constData(), FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
or this:
handle = CreateFile((LPCWSTR) fileName.utf16(), FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
LPCWSTR is a pointer (LP) to a constant (C) wide character (W) string (STR). In other words, this is a const WCHAR*
WCHAR Drive[4]; is a wide character array, which can also be called a wide character string.
Any array of a certain type can implicitly convert to a pointer to that same type. Furthermore, a pointer of a certain type can implicitly convert to a constant pointer of the same type, especially in the case of a function call.
Thus passing Drive to that function implicitly converts to LPCWSTR.
Your problem in not in that conversion. Your problem is most likely in the contents of your strings, as humbagumba's answer already explained.