EDN Admin
Well-known member
I have a requirement to control, individually, the state of the NUM LOCK lamp on each of several USB keyboards connected to one PC (please dont ask why on earth Id want to do such an odd thing - but it is a real requirement on a real project for my company).
I am using a code sequence comprising GetRawInputDeviceList(...) and GetRawInputDeviceInfo(...) to obtain the name (e.g. "\?HID#VID_413C&PID_2107# etc etc") of each of the USB Keyboard devices connected to the PC, and using that name in a CreateFile(...) call to get a handle to each device.
So far, so good. But, I am now trying to send the NUM LOCK key code to a particular keyboard using either DeviceIOControl(...) or WriteFile(...) and both calls are returning ERROR_INVALID_FUNCTION.
Clearly I am going wrong at this point - would anybody be able to help please? My detailed code is below...int dwSize = sizeof( RAWINPUTDEVICELIST );
if ( GetRawInputDeviceList( NULL, &deviceCount, ( UINT )dwSize ) == 0 )
{
PRAWINPUTDEVICELIST pRawInputDeviceList =
( PRAWINPUTDEVICELIST )malloc( dwSize * deviceCount );
GetRawInputDeviceList( pRawInputDeviceList, &deviceCount, ( UINT )dwSize );
// Iterate through the list, discarding undesired items
// and retrieving further information on keyboard devices
for ( UINT i = 0; i < deviceCount; i++ )
{
UINT pcbSize = 0;
RAWINPUTDEVICELIST rid = *( pRawInputDeviceList + i );
GetRawInputDeviceInfo( rid.hDevice, RIDI_DEVICENAME, NULL, &pcbSize );
if ( pcbSize > 0 )
{
std::vector<TCHAR> data(pcbSize);
LPTSTR pData = &data[0];
GetRawInputDeviceInfo( rid.hDevice, RIDI_DEVICENAME, pData, &pcbSize );
LPCTSTR lpctstrName = pData; // For CreateFile() later
CString deviceName = ( char* )pData;
// Drop the "root" keyboard and mouse devices used for Terminal
// Services and the Remote Desktop
if ( deviceName.MakeUpper().Find( "ROOT" ) != -1 )
{
continue;
}
// If the device is identified in the list as a keyboard or HID
// device, create a DeviceInfo object to store information about it
if ( rid.dwType == RIM_TYPEKEYBOARD || rid.dwType == RIM_TYPEHID )
{
// Check with the Registry that this IS a Keyboard...
// First, remove the common "\?" crap from the Device Name
CString longDeviceName = deviceName;
deviceName = deviceName.Right( deviceName.GetLength() - 4 );
// Next, compile a Registry access string using the
// three # divided bits (we ditch the class GUID)
CString registryString = "SYSTEM\CURRENTCONTROLSET\ENUM\";
// 1) Class code
int hash1 = deviceName.Find( # );
if ( hash1 != -1 )
{
registryString += deviceName.Left( hash1 ) + "\";
// 2) SubClass code
hash1++;
int hash2 = deviceName.Find( #, hash1 );
if ( hash2 != -1 )
{
registryString += deviceName.Mid( hash1, hash2 - hash1 ) + "\";
// 3) Protocol code
hash1 = hash2 + 1;
hash2 = deviceName.Find( #, hash1 );
if ( hash2 != -1 )
{
registryString += deviceName.Mid( hash1, hash2 - hash1 );
}
}
}
// Get the device class from the registry
HKEY Key;
int result = RegOpenKeyEx( HKEY_LOCAL_MACHINE, registryString, 0, KEY_READ, &Key );
char deviceClass[ 80 ] = {0};
DWORD deviceClassLength = sizeof( deviceClass );
result = RegQueryValueEx( Key, "Class", NULL, NULL,
( LPBYTE )deviceClass, &deviceClassLength );
RegCloseKey( Key );
// So, is it a keyboard?
if ( _stricmp( deviceClass, "Keyboard" ) == 0 )
{
// Yes, get a handle to it...
int lastErr = 0;
HANDLE hFile = CreateFile( lpctstrName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // FILE_ATTRIBUTE_NORMAL?
NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
lastErr = GetLastError();
lastErr = lastErr;
}
DWORD bytesReturned = 0;
OVERLAPPED overlapStruct1 = {0};
if ( !DeviceIoControl( hFile, 0x90, NULL, 0, NULL, 0, &bytesReturned, &overlapStruct1 ) )
{
lastErr = GetLastError();
lastErr = lastErr;
}
char buf[1] = { 0x90 };
DWORD bytesWritten = 0;
OVERLAPPED overlapStruct2 = {0};
if ( !WriteFile( hFile, buf, 1, &bytesWritten, &overlapStruct2 ) )
{
lastErr = GetLastError();
lastErr = lastErr;
}
}
}
}
}
}
Regards, Pennant Mike.
View the full article
I am using a code sequence comprising GetRawInputDeviceList(...) and GetRawInputDeviceInfo(...) to obtain the name (e.g. "\?HID#VID_413C&PID_2107# etc etc") of each of the USB Keyboard devices connected to the PC, and using that name in a CreateFile(...) call to get a handle to each device.
So far, so good. But, I am now trying to send the NUM LOCK key code to a particular keyboard using either DeviceIOControl(...) or WriteFile(...) and both calls are returning ERROR_INVALID_FUNCTION.
Clearly I am going wrong at this point - would anybody be able to help please? My detailed code is below...int dwSize = sizeof( RAWINPUTDEVICELIST );
if ( GetRawInputDeviceList( NULL, &deviceCount, ( UINT )dwSize ) == 0 )
{
PRAWINPUTDEVICELIST pRawInputDeviceList =
( PRAWINPUTDEVICELIST )malloc( dwSize * deviceCount );
GetRawInputDeviceList( pRawInputDeviceList, &deviceCount, ( UINT )dwSize );
// Iterate through the list, discarding undesired items
// and retrieving further information on keyboard devices
for ( UINT i = 0; i < deviceCount; i++ )
{
UINT pcbSize = 0;
RAWINPUTDEVICELIST rid = *( pRawInputDeviceList + i );
GetRawInputDeviceInfo( rid.hDevice, RIDI_DEVICENAME, NULL, &pcbSize );
if ( pcbSize > 0 )
{
std::vector<TCHAR> data(pcbSize);
LPTSTR pData = &data[0];
GetRawInputDeviceInfo( rid.hDevice, RIDI_DEVICENAME, pData, &pcbSize );
LPCTSTR lpctstrName = pData; // For CreateFile() later
CString deviceName = ( char* )pData;
// Drop the "root" keyboard and mouse devices used for Terminal
// Services and the Remote Desktop
if ( deviceName.MakeUpper().Find( "ROOT" ) != -1 )
{
continue;
}
// If the device is identified in the list as a keyboard or HID
// device, create a DeviceInfo object to store information about it
if ( rid.dwType == RIM_TYPEKEYBOARD || rid.dwType == RIM_TYPEHID )
{
// Check with the Registry that this IS a Keyboard...
// First, remove the common "\?" crap from the Device Name
CString longDeviceName = deviceName;
deviceName = deviceName.Right( deviceName.GetLength() - 4 );
// Next, compile a Registry access string using the
// three # divided bits (we ditch the class GUID)
CString registryString = "SYSTEM\CURRENTCONTROLSET\ENUM\";
// 1) Class code
int hash1 = deviceName.Find( # );
if ( hash1 != -1 )
{
registryString += deviceName.Left( hash1 ) + "\";
// 2) SubClass code
hash1++;
int hash2 = deviceName.Find( #, hash1 );
if ( hash2 != -1 )
{
registryString += deviceName.Mid( hash1, hash2 - hash1 ) + "\";
// 3) Protocol code
hash1 = hash2 + 1;
hash2 = deviceName.Find( #, hash1 );
if ( hash2 != -1 )
{
registryString += deviceName.Mid( hash1, hash2 - hash1 );
}
}
}
// Get the device class from the registry
HKEY Key;
int result = RegOpenKeyEx( HKEY_LOCAL_MACHINE, registryString, 0, KEY_READ, &Key );
char deviceClass[ 80 ] = {0};
DWORD deviceClassLength = sizeof( deviceClass );
result = RegQueryValueEx( Key, "Class", NULL, NULL,
( LPBYTE )deviceClass, &deviceClassLength );
RegCloseKey( Key );
// So, is it a keyboard?
if ( _stricmp( deviceClass, "Keyboard" ) == 0 )
{
// Yes, get a handle to it...
int lastErr = 0;
HANDLE hFile = CreateFile( lpctstrName,
GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // FILE_ATTRIBUTE_NORMAL?
NULL );
if ( hFile == INVALID_HANDLE_VALUE )
{
lastErr = GetLastError();
lastErr = lastErr;
}
DWORD bytesReturned = 0;
OVERLAPPED overlapStruct1 = {0};
if ( !DeviceIoControl( hFile, 0x90, NULL, 0, NULL, 0, &bytesReturned, &overlapStruct1 ) )
{
lastErr = GetLastError();
lastErr = lastErr;
}
char buf[1] = { 0x90 };
DWORD bytesWritten = 0;
OVERLAPPED overlapStruct2 = {0};
if ( !WriteFile( hFile, buf, 1, &bytesWritten, &overlapStruct2 ) )
{
lastErr = GetLastError();
lastErr = lastErr;
}
}
}
}
}
}
Regards, Pennant Mike.
View the full article