Raw input winapi in c, can't get device info - c

I'm messing around with a USB RFID scanner and trying to read input with raw input, so far I have this
#define _WIN32_WINNT 0x0601
#include <windows.h>
#include <stdio.h>
int main(void)
{
PRAWINPUTDEVICELIST pRawInputDeviceList;
PUINT puiNumDevices, pcbSize;
UINT cbSize = sizeof(RAWINPUTDEVICELIST);
char *pData[1000];
GetRawInputDeviceList(NULL, puiNumDevices, cbSize);
pRawInputDeviceList = malloc(cbSize * *puiNumDevices);
GetRawInputDeviceList(pRawInputDeviceList, puiNumDevices, cbSize);
// gives a correct RIM_TYPE for all devices 0-7 (GetRawInputDeviceList returns 8 devices for me)
printf("%I32u\n", pRawInputDeviceList[0].dwType);
GetRawInputDeviceInfo(pRawInputDeviceList[1].hDevice, RIDI_DEVICENAME, pData, pcbSize);
// gives a huge number (garbage?), should be the length of the name
printf("%u\n", pcbSize);
// "E" in my case
printf("%s\n", pData);
// error 87, apparently ERROR_INVALID_PARAMETER
printf("%I32u\n", GetLastError());
return 0;
}

When you call GetRawInputDeviceInfo, it expects pcbSize to be a pointer. You have it as a pointer, but its not pointing to anything. Try this:
Get rid of pcbSize (everywhere).
Create a variable UINT cbDataSize = 1000. This is the size of your pData array.
For the last argument of GetRawInputDeviceInfo, use &cbDataSize. This takes the address of cbDataSize, the address is a pointer.
Change printf("%u\n", pcbSize); to printf("%u\n", cbDataSize);.
See how that works for you.
[edit]
Also, you should do the same thing for puiNumDevices. Instead, create a UINT called uiNumDevices. Use &uiNumDevices where the functions expect pointers.

I am going to go out on a limb here and guess that this thing may actually be a HID device. Do you know if it is?
HID Devices are actually pretty easy to talk to; you connect to them via CreateFile() -- the same way that you would open a COM port -- and then just ReadFile() to get data.
Most of the problem is figuring out the correct path to connect to. It's actually a value called DevicePath that you get from SetupDiGetDeviceInterfaceDetail().
A rough map of it looks like this:
HidD_GetHidGuid() to get the HID guid
SetupDiGetClassDevs() to get the list of dev
Looping through devs, until you find yours:
SetupDiEnumDeviceInterfaces() to enum the device interfaces
SetupDiGetDeviceInterfaceDetail() to get the detail
CreateFile() to open the device with the DevicePath from the detail.
HidD_GetAttributes to get the vendorid and productid to see if it's your device.
If it is, remember it, and use ReadFile() to get data.

Related

What is the purpose of hci_get_route()

Looking through code examples, it looks like hci_get_route(NULL) is generally used to get a device id for a Bluetooth device on the local machine. That's fine, and I can understand that. My confusion, I suppose, would only matter on a system that has more than one Bluetooth device, and where the argument to hci_get_route() is non-NULL. The source code for hci.c shows the following implementation:
int hci_get_route(bdaddr_t *bdaddr)
{
int dev_id;
dev_id = hci_for_each_dev(HCI_UP, __other_bdaddr,
(long) (bdaddr ? bdaddr : BDADDR_ANY));
if (dev_id < 0)
dev_id = hci_for_each_dev(HCI_UP, __same_bdaddr,
(long) (bdaddr ? bdaddr : BDADDR_ANY));
return dev_id;
}
I won't copy the whole code of hci_for_each_dev(), __other_bdaddr(), or __same_bdaddr() here, but in short this function, if provided with a non-NULL bdaddr_t*, will try first to find a device with an address that does not match the address provided, and then, only after that fails, selects the device with the matching address. I looked back at the commit that introduced this function, and it's slightly different, but still uses __other_bdaddr:
int hci_get_route(bdaddr_t *bdaddr)
{
if (bdaddr)
return hci_for_each_dev(HCI_UP, __other_bdaddr, (long) bdaddr);
else
return hci_for_each_dev(HCI_UP, NULL, 0);
}
Even this initial version appears, very intentionally, to do the same thing, it just doesn't include the backup check using __same_bdaddr.
Seeing as there is no documentation for this function (that I can find), it seems that a novice user of this library would expect the bdaddr_t* argument to be used to select the device, so my question is, why does it do the exact opposite, preferring any other device before selecting the device with the matching address?

Calling Chicken Scheme function from SDL2 audio callback function hangs

I am trying to embed Chicken Scheme into a C program, to generate sounds to be played with SDL2's audio system. I would have liked to use the sdl2 egg, but it does not seem to support Audio yet (despite the documentation mentioning the 'audio flag for the init! function).
At first, I was using SDL_QueueAudio from C, passing it a buffer that I had allocated in C and then filled in Scheme. This worked fine, passing a Sint16 * and size_t into Scheme, then using pointer-s16-set! from Scheme to fill it and returning a size_t to note how many cells were filled.
Then, when I realised that using the callback api for generating the audio was much better suited to this, I tried switching to it (having already used it before in C), only for the Scheme function to never be entered. Logging something in the callback function before the Scheme call worked, but logging directly within the Scheme function, or after the Scheme call, never happened.
I can only imagine that this is due to SDL2's audio callback running on a separate thread, and that messing with calling through to Scheme somehow. With this in mind, I tried calling CHICKEN_run(C_toplevel); from within the callback function, the first time that it was called, but that only resulted in a bus error.
So my question is: is there a way of calling embedded Chicken Scheme from SDL2's audio callback?
I am on macOs 10.13.6 High Sierra, with SDL2 and chicken both installed and up-to-date through Homebrew.
I compile with (as I said, this works fine when using the queue audio api):
csc code.c codescm.scm -embedded -o code -L -lSDL2
My simplified code is below:
#include <chicken.h>
#include "SDL2/SDL.h"
extern size_t fill_sound_buffer(Sint16 *buffer, size_t buffer_length);
void fill_sound_callback(void *user_data, Uint8 *stream, int stream_length)
{
// Logging here prints to the console
fill_sound_buffer((Sint16 *)stream, stream_length / 2);
// Logging here does not print to the console
}
void play(void)
{
SDL_AudioSpec audio_want;
SDL_zero(audio_want);
audio_want.freq = 44100;
audio_want.format = AUDIO_S16SYS;
audio_want.channels = 1;
audio_want.samples = 2048;
audio_want.callback = fill_sound_callback;
SDL_AudioSpec audio_have;
SDL_AudioDeviceID audio_device = SDL_OpenAudioDevice(NULL, 0, &audio_want, &audio_have, 0);
SDL_PauseAudioDevice(audio_device, 0);
SDL_Delay(5000);
// Logging here shows up after 5 seconds, but the program then continues to wait
SDL_CloseAudioDevice(audio_device);
}
int main(int argc, char *argv[])
{
SDL_Init(SDL_INIT_AUDIO);
CHICKEN_run(C_toplevel);
play();
SDL_Quit();
return 0;
}
(import (chicken format)
(chicken foreign)
(chicken memory)
(chicken platform))
(define-external (fill_sound_buffer ((c-pointer short) buffer) (size_t buffer_length)) size_t
; This never prints when using the callback api
(printf "In Scheme~%")
; Removed the code that calculates a sine wave and fills the buffer with it, which works
0)
(return-to-host)

Struggling with conversion of an LPBYTE to char* or anything alike in C (Chinese output?)

I've got a C project that I'm working on and I'm having a problem.
The program reads a string that is echoed by a .php page. It uses this code
to read the data and appoint it to a variable, which get sent to the Commands() function:
LPSTR szBuffer=(LPSTR)chunk+0x1000;
DWORD dwRead;
if (CWA(_HttpSendRequestA, wininet, hHttpRequest, szHeaders, lpstrlenA(szHeaders), szReq, lpstrlenA(szReq)) != 0)
{
CWA(_InternetReadFileA, wininet, hHttpRequest, szBuffer, 0x400, &dwRead);
if (dwRead)
Commands((LPBYTE)szBuffer, dwRead);
}
As you can see the data is sent to the Commands() function, which receives the LPBYTE szBuffer (named "command" in the function) and the DWORD dwRead (named "size" in the function).
In the Commands() function, it's supposed to read the string that it read from the .php page. However, since the data seems to be stored as LPBYTE, I've done a lot of things trying to get that to a char*. When I thought I had finally got it however, I tried outputting it using a MessageBox (to see if it displays the string it should have read). However, this returns me Chinese characters (while the original string should be this:
"TASKci=0C73CCFD206BBD011E7087CE0806E6F6E69630,job=dlex,ti=AD62A5950B76F3812C542C24040EACE9,pr=https,ur=//test.com/test.txt,cl=".
Screenshot of what it returns me: http://prntscr.com/h0p5iw
How the code inside Commands() looks:
BOOL Commands(LPBYTE command, DWORD size) {
LPTSTR x = (LPTSTR)((char*)command);
{
int msgboxID = MessageBox(
NULL,
x,
(LPCWSTR)L"Woop",
MB_ICONWARNING | MB_CANCELTRYCONTINUE | MB_DEFBUTTON2 );
}
CWA(Sleep, kernel32, 100);
return 1; }
I'm new at C (I've only written stuff in C# before) so I am sorry if I am asking any dumb questions, I've really tried my best but I cannot seem to find any solution by myself.
Also, keep in mind that everything except for the stuff inside the Commands() function is not coded by me but by someone who is way more experienced. That code should be fine and I am sure that it is reading the data from the page, it's probably just me screwing up a conversion somewhere.
A narrow string (char*) tends to look like Chinese when you use it somewhere that expects a wide UTF-16 Unicode string.
You cannot just cast the string to change its type, you need to call MultiByteToWideChar.

How do I get the disk drive serial number in filter driver?

I write a driver in windows, and I need disk drive serial number, for user mode I found this ansver.
My question is it possible to translate the above code to kernel mode, and how? Is WMI query available in filter driver? Sample code can greatly help.
EDIT:
I found here this code, but how I rewrite him for get serial number?
void GetSmbios()
{
NTSTATUS status;
GUID smbiosGUID = SMBIOS_DATA_GUID; // defined in wmiguid.h
PVOID wmiObject = NULL;
PWNODE_ALL_DATA dataBuffer;
ULONG bufferSize;
int TAG_SMBIOS = 'smbi';
//
// Get a WMI block handle to the SMBIOS_DATA_GUID
//
status = IoWMIOpenBlock((GUID *)&smbiosGUID, WMIGUID_QUERY,
&wmiObject);
if (!NT_SUCCESS(status))
{
return status;
}
//
// Determine how much space is required for the data
//
status = IoWMIQueryAllData(wmiObject, &bufferSize, NULL);
if (status != STATUS_BUFFER_TOO_SMALL)
{
ObDereferenceObject(wmiObject);
return status;
}
//
// Allocate the necessary storage. This space must come out of NP-pool
//
dataBuffer = ExAllocatePoolWithTag(
NonPagedPool,
bufferSize,
TAG_SMBIOS);
if (dataBuffer == NULL)
{
ObDereferenceObject(wmiObject);
return STATUS_INSUFFICIENT_RESOURCES;
}
}
After allocating memory, I believe you need to call IoWMIQueryAllData() again, this time passing dataBuffer.
SMBIOS doesn't seem related to disk drives, so you'll want a different GUID to pass to IoWMIOpenBlock(). Perhaps this one ({BF253431-1E4D-4F57-00E7-64B2CACC801E}), since your user-mode example and others query Win32_PhysicalMedia to get SerialNumber.
However, this references a (presumably user-mode) DLL that is the provider for Win32_PhysicalMedia. So this may not be accessible in kernel-mode.
But it also gives a hint how to get the information from kernel-mode: IOCTLs. It mentions IOCTL_SMART_GET_VERSION, which should be just SMART_GET_VERSION, and here's an example:
(in user-mode, but you should be able to do similar from kernel-mode using ZwDeviceIoControlFile()). Note it follows up with another ioctl command, SMART_RCV_DRIVE_DATA, to get the serial number.
Another ioctl that sounds promising (and more general) is IOCTL_STORAGE_QUERY_PROPERTY, with the input STORAGE_PROPERTY_QUERY.PropertyId set to StorageDeviceProperty, so the output will be a STORAGE_DEVICE_DESCRIPTOR structure, which has field SerialNumberOffset:
Specifies the byte offset from the beginning of the structure to a null-terminated ASCII string that contains the device's serial number. If the device has no serial number, this member is zero.
FILE_FS_VOLUME_INFORMATION contains field VolumeSerialNumber. This data structure might be retrieved with ZwQueryVolumeInformationFile(... FileFsVolumeInformation).
That requires a handle to the volume or a file/directory in the volume. If that's not feasible, but you have a DEVICE_OBJECT, you might try building your own IRP with IRP_MJ_QUERY_VOLUME_INFORMATION and sending it with IoCallDriver(), though I don't know if that's sanctioned -- the docs say such a "request is sent by the I/O Manager."

SetProp problem

Can anybody tell me why the following code doesn't work? I don't get any compiler errors.
short value = 10;
SetProp(hCtl, "value", (short*) value);
The third parameter is typed as a HANDLE, so IMO to meet the explicit contract of the function you should save the property as a HANDLE by allocating a HGLOBAL memory block. However, as noted in the comments below, MSDN states that any value can be specified, and indeed when I try it on Windows 7 using...
SetProp(hWnd, _T("TestProp"), (HANDLE)(10)); // or (HANDLE)(short*)(10)
...
(short)GetProp(hWnd, _T("TestProp"));
... I get back 10 from GetProp. I suspect somewhere between your SetProp and GetProp one of two things happens: (1) the value of hWnd is different -- you're checking a different window or (2) a timing issue -- the property hasn't been set yet or had been removed.
If you wanted to use an HGLOBAL instead to follow the specific types of the function signature, you can follow this example in MSDN.
Even though a HANDLE is just a pointer, it's a specific data type that is allocated by calls into the Windows API. Lots of things have handles: icons, cursors, files, ... Unless the documentation explicitly states otherwise, to use a blob of data such as a short when the function calls for a HANDLE, you need a memory handle (an HGLOBAL).
The sample code linked above copies data as a string, but you can instead set it as another data type:
// TODO: Add error handling
hMem = GlobalAlloc(GPTR, sizeof(short));
lpMem = GlobalLock(hMem);
if (lpMem != NULL)
{
*((short*)lpMem) = 10;
GlobalUnlock(hMem);
}
To read it back, when you GetProp to get the HANDLE you must lock it to read the memory:
// TODO: Add error handling
short val;
hMem = (HGLOBAL)GetProp(hwnd, ...);
if (hMem)
{
lpMem = GlobalLock(hMem);
if (lpMem)
{
val = *((short*)lpMem);
}
}
I would create the short on the heap, so that it continues to exist, or perhaps make it global, which is perhaps what you did. Also the cast for the short address needs to be void *, or HANDLE.

Resources