Usb hid device insert/removal detection winapi - c++

I have created winapi application witch uses other .exe via createprocess to get/set reports. And now I need some kind of way to detect that this USB HID device was plugged/unplugged from computer when application is running. Hardest part of it is that in that app I know just VID and PID and I don't have any handles to that USB HID device. is there any way to solve this problem or I first need handle of the device?
Edit
If anyone is interested why I need it. I want to disable/enable controls of my app when i plug and unplug device.

at first you must register own window for receive WM_DEVICECHANGE message with DBT_DEVICEARRIVAL and DBT_DEVICEREMOVECOMPLETE for GUID_DEVINTERFACE_USB_DEVICE with RegisterDeviceNotification - windows will be not send this notification without registration!
case WM_CREATE:
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = {
sizeof(DEV_BROADCAST_DEVICEINTERFACE),
DBT_DEVTYP_DEVICEINTERFACE,
0,
GUID_DEVINTERFACE_USB_DEVICE
};
if (!(_Handle = RegisterDeviceNotification(hwnd, &NotificationFilter, DEVICE_NOTIFY_WINDOW_HANDLE)))
{
return -1;
}
break;
and unregister on destroy:
case WM_DESTROY:
if (_Handle) UnregisterDeviceNotification(_Handle);
break;
after this you will be receive notification. if
I know just VID and PID
you can search for L"#VID_????&PID_????#" in dbcc_name (where in place ? your actual vid and pidvalues)
case WM_DEVICECHANGE:
switch (wParam)
{
case DBT_DEVICEREMOVECOMPLETE:
case DBT_DEVICEARRIVAL:
{
PDEV_BROADCAST_DEVICEINTERFACE p = (PDEV_BROADCAST_DEVICEINTERFACE)lParam;
if (p->dbcc_devicetype == DBT_DEVTYP_DEVICEINTERFACE &&
p->dbcc_classguid == GUID_DEVINTERFACE_USB_DEVICE)
{
DbgPrint("%S\n", p->dbcc_name);
if (wcsstr(p->dbcc_name, L"#VID_****&PID_****#"))
{
DbgPrint("%s\n", wParam == DBT_DEVICEARRIVAL ? "arrival" : "removal");
}
}
}
break;
}
break;

Windows sends to all top-level windows the WM_DEVICECHANGE message when new devices or media becomes availables. Checks for the event DBT_DEVICEARRIVAL in wParam. With the event DBT_DEVICEARRIVAL lParam can be converted to a DEV_BROADCAST_HDR structure.
When this is done, you check dbch_devicetype from DEV_BROADCAST_HDR, and convert lParam again to DEV_BROADCAST_HANDLE, or DEV_BROADCAST_VOLUME if dbch_devicetype is equal to DBT_DEVTYP_HANDLEor DEV_BROADCAST_VOLUME, I'm not sure to remember which one.

Related

"SERVICE_CONTROL_SESSIONCHANGE" notification is never received in C++ service application in Windows 10

I have been trying to develop a service application to hook into the login/logout event of windows. My development environment is Windows 10. As it is a service application, based on suggestion from some of the existing posts in Stackoverflow and other dev platform, I have registered the service to get notified in different events. Below is the snippet I tried.
SERVICE_STATUS_HANDLE gSvcStatusHandle;
VOID WINAPI SvcCtrlHandlerEx(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
{
switch (dwControl)
{
case SERVICE_CONTROL_STOP:
writeEventLog(utils.GetDefaultTitle(), L"Service About to end");
ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);
// Signal the service to stop.
SetEvent(ghSvcStopEvent);
ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);
applicationLogger.LogWarning("Service stopped.");
break;
return;
case SERVICE_CONTROL_INTERROGATE:
writeEventLog(GetDefaultTitle(), L"Service=interrogate");
applicationLogger.LogWarning("Service Interrogate.");
break;
case SERVICE_CONTROL_PAUSE:
writeEventLog(GetDefaultTitle(), L"Service=paused");
applicationLogger.LogWarning("Service pasued.");
break;
case SERVICE_CONTROL_CONTINUE:
writeEventLog(GetDefaultTitle(), L"Service=continued");
applicationLogger.LogWarning("Service continued.");
break;
case SERVICE_CONTROL_SESSIONCHANGE:
writeEventLog(GetDefaultTitle(), L"Service=Session=Changed");
applicationLogger.LogWarning("Session changed");
if (WTS_SESSION_LOGOFF == (dwEvtype & WTS_SESSION_LOGOFF))
{
WTSSESSION_NOTIFICATION* pSessionNotification = static_cast<WTSSESSION_NOTIFICATION*>(pEvtData);
}
else if (WTS_SESSION_LOGON == (dwEvtype & WTS_SESSION_LOGOFF))
{
WTSSESSION_NOTIFICATION* pSessionNotification = static_cast<WTSSESSION_NOTIFICATION*>(pEvtData);
break;
default:
writeEventLog(GetDefaultTitle(), L"Service=Dfault");
applicationLogger.LogWarning("Service default.");
break;
}
}
VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR* lpszArgv)
{
gSvcStatusHandle = RegisterServiceCtrlHandlerEx(SVCNAME, reinterpret_cast<LPHANDLER_FUNCTION_EX>(SvcCtrlHandlerEx), NULL);
}
For the debug purpose, I am trying to log events name whenever service receives any event notification. However, the only notification I receive is then I try to stop the service, "case SERVICE_CONTROL_STOP" will be executed. I am interested in receiving "SERVICE_CONTROL_SESSIONCHANGE" event so that I can retrive user's login information.
Therefore, can you point the area that I have been doing wrong? Also is the method inside "SERVICE_CONTROL_SESSIONCHANGE" the correct way to get login/logout event from windows?
I tried signing in and out in windows10 system, but no logs were written in event viewer.
To receive SERVICE_CONTROL_SESSIONCHANGE notifications in your HandlerEx callback, you need to call SetServiceStatus() with the SERVICE_ACCEPT_SESSIONCHANGE flag enabled in the SERVICE_STATUS::dwControlsAccepted field.
Control code
Meaning
SERVICE_ACCEPT_SESSIONCHANGE 0x00000080
The service is notified when the computer's session status has changed. This enables the system to send SERVICE_CONTROL_SESSIONCHANGE notifications to the service.
As for your handler itself, you should not be using operator& with the dwEventType parameter, as it is not a bitmask. Use operator== instead (or a switch):
case SERVICE_CONTROL_SESSIONCHANGE:
writeEventLog(GetDefaultTitle(), L"Service=Session=Changed");
applicationLogger.LogWarning("Session changed");
if (dwEventType == WTS_SESSION_LOGOFF)
{
WTSSESSION_NOTIFICATION* pSessionNotification = static_cast<WTSSESSION_NOTIFICATION*>(lpEventData);
// ...
}
else if (dwEventType == WTS_SESSION_LOGON)
{
WTSSESSION_NOTIFICATION* pSessionNotification = static_cast<WTSSESSION_NOTIFICATION*>(lpEventData);
// ...
}
break;

Lock window position on display change

I have an application which has several windows and I want that some of them (which I will call CMyLockedFrameWndEx, because they derive from CFrameWndEx) stay placed where they were after changing system display area.
The Parent of all windows of my application is NULL.
I've managed to already catch the WM_DISPLAYCHANGE message when I drag the position of the 2nd monitor relatively to the first; and I also had come to catch the WM_DEVICECHANGE when I connect or disconnect the 2nd monitor's HDMI cable. I've intercepted them both at CMyLockedFrameWndEx::WindowProc.
The automatic window repositioning occurs after that. I noticed that bacause I've put breakpoints on CMyLockedFrameWndEx::OnWindowPosChanging and CMyLockedFrameWndEx::OnWindowPosChanged and they stop after the events I catched on WindowProc. This workflow seems to be unrelated to the catching of events I described as my WindowProc method is:
LRESULT CMyLockedFrameWndEx::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_DISPLAYCHANGE)
{
TRACE(_T("DISPLAY CHANGE"));
return 0L;
}
if (message == WM_SYSCOMMAND)
{
TRACE(_T("SYSCOMMAND"));
if (wParam == SC_MOVE)
{
return 0L;
}
}
if (message == WM_WININICHANGE)
{
TRACE(_T("WININICHANGE"));
if (wParam == SPI_SETWORKAREA)
{
return 0L;
}
}
return __super::WindowProc( message, wParam, lParam);
}
and when passing in OnWindowPosChanging or OnWindowPosChanged, the flow doesn't come from the specific cases of WindowProc handled by me. And that is a problem.
I tried to follow the call stack to see what window sent the WM_WINDOWPOSCHANGING or the WM_WINDOWPOSCHANGED message, but I didn't succeed. I have even tried to use Spy++64 to detect who was the the sender of the message, but I didn't succeed. The whole idea of seeing who was the sender was: if it is associated to a system's display change is to detect it beforehand and impeach the auto repositioning to even happen.
As i didn't succeed yet, what can I do to make the window immune to a system's display change?
Thanks.
The good news is: you are, obviously, in control of the placement of your own window.
The bad news - Windows will first move your window to its new position, then send you the WM_DISPLAYCHANGE and WM_SETTINGCHANGE, as seen in this Spy++'s log:
S WM_WINDOWPOSCHANGING lpwp:003CF9AC
S WM_GETMINMAXINFO lpmmi:003CF624
R WM_GETMINMAXINFO lpmmi:003CF624
R WM_WINDOWPOSCHANGING
S WM_WINDOWPOSCHANGED lpwp:003CF9AC
S WM_MOVE xPos:278 yPos:450
R WM_MOVE
R WM_WINDOWPOSCHANGED
S WM_SETTINGCHANGE wFlag:SPI_ICONVERTICALSPACING pszMetrics:0026E018
R WM_SETTINGCHANGE
S WM_DISPLAYCHANGE cBitsPerPixel:32 cxScreen:2560 cyScreen:1440
R WM_DISPLAYCHANGE
S WM_SETTINGCHANGE wFlag:SPI_SETWORKAREA pszMetrics:0026E018
R WM_SETTINGCHANGE
So - you would have to test the position changing to another screen by yourself. Like in this simplified example, where 2560 is my screen's width:
case WM_WINDOWPOSCHANGING:
{
WINDOWPOS* pWP = (WINDOWPOS*)lParam;
if ((pWP->flags & SWP_NOMOVE) == 0) // it's a move
{
if (pWP->x < 2560)
pWP->flags |= SWP_NOMOVE;
}
return 0;
}
The unhappy news are I can not do this on a preventive way, because the system moves the windows to the primary screen even after sending any useful messages.
The good news are I can react to the move by adding a
ON_WM_WINDOWPOSCHANGED()
line on the class's message map and supply it with its respective handler function:
CMyLockedFrameWndEx::OnWindowPosChanged(WINDOWPOS* lpwndpos)
{
__super::OnWindowPosChanged(lpwndpos);
CFrameWndEx* pFrame=(CFrameWndEx*)::AfxGetMainWnd();
VALIDATE_FPTR(pFrame);
CMyDoc* pDoc=(CMyDoc*)pFrame->GetActiveDocument();
VALIDATE_FPTR(pDoc);
POSITION p= pDoc->GetFirstViewPosition();
while(p)
{
CMyLockedView* pLockedView= dynamic_cast<CLockedView*>(pDoc->GetNextView(p));
if(!pLockedView)
continue;
if(pLockedView->GetParentFrame() == this)
{
this->SetWindowPos(NULL, m_Top, m_Left, 0, 0, SWP_NOSIZE);
break;
}
}
}ยด
Note, that it helps me that I was persisting the position where the window was in the m_Top and m_Left variables.

Windows Journal playback hook (WH_JOURNALPLAYBACK) ignores EVENTMSG HWND parameter

I am working a program to simulate keyboard and mouse clicks programmatically. It need to send the clicks to a target window handle (ex: notepad edit control). I am getting the handle of notepad window and generating generating WM_KEYDOWN, WM_SYSKEYDOWN, WM_KEYUP, WM_SYSKEYUP messages for that window. The events are stored in queue, and later on played using a WH_JOURNALPLAYBACK hook.
For the code snippet below, the target hwnd in the playback proc though set correctly, messages never reach to the target handle. If I bring the notepad to foreground, it does receive the messages.
I am not sure why WH_JOURNALPLAYBACK ignores the handle parameter. I would have liked to generate a series of automation messages for various handles and played it back so that even without bringing the window into focus we can send keyboard and mouse events.
Please let me know
if messages to various target handles can be sent using a journal
playback hook
why in code below hwnd is ignored
..
#include <queue>
#include <iostream>
#include <windows.h>
using std::cout;
using std::endl;
using std::error;
struct Event
{
UINT msg;
UINT wparam;
UINT lparam;
HWND hwnd;
Event(UINT m, UINT wp, UINT lp, HWND h)
:msg(m),
wparam(wp),
lparam(lp),
hwnd(h)
{}
};
HHOOK jhook= NULL;
std::queue<Event> events;
bool gotoNextMsg = false;
LRESULT CALLBACK JournalPlaybackProc(int code, WPARAM wParam, LPARAM lParam)
{
switch( code )
{
case HC_SKIP:
cout<<"skip: "<<endl;
if(!events.empty())
{
events.pop();
}
break;
case HC_GETNEXT:
{
cout<<"next: "<<events.size()<<endl;
gotoNextMsg = true;
EVENTMSG * evm = (EVENTMSG*) lParam;
Event e = events.front();
switch(e.msg)
{
case WM_KEYDOWN:
cout<<"WM_KEYDOWN"<<endl;
break;
case WM_KEYUP:
cout<<"WM_KEYUP"<<endl;
break;
case WM_SYSKEYDOWN:
cout<<"WM_SYSKEYDOWN"<<endl;
break;
case WM_SYSKEYUP:
cout<<"WM_SYSKEYUP"<<endl;
break;
}
cout<<"handle: "<<e.hwnd<<endl;
cout<<"handle1:"<<evm->hwnd<<endl;
evm->message = e.msg;
evm->paramL = e.wparam;
evm->paramH = e.lparam;
evm->hwnd = e.hwnd;
evm->time = ::GetTickCount();
}
break;
default:
if( code < 0 )
::CallNextHookEx(jhook, code, wParam, lParam);
break;
}
if(events.empty())
{
cout<<"uinstalled"<<endl;
::UnhookWindowsHookEx(jhook);
::PostMessage(NULL, WM_USER+100, 0, 0);
}
return 0;
}
A journal hook injects events into the system message queue. For keyboard and mouse messages, the system dispatches them to the current focused window, same as if the user had input them manually. The HWND you specify in the event is not used, it gets replaced during dispatching.
And if you consider that a recorded journal can be played multiple times, and its data can persist across application instances and even reboots, and that HWNDs can be reused for different things over time, it should make sense why a journal playback cannot make use of an event's HWND even if the system message queue were not involved.
So, you cannot use WH_JOURNALPLAYBACK to target a specific window that is not in the foreground. You would have to send the recorded messages yourself. But be aware of some caveats that Raymond Chen has blogged about:
You can't simulate keyboard input with PostMessage
Simulating input via WM_CHAR messages may fake out the recipient but it won't fake out the input system

Using DirectInput to receive signal after plugging in joystick

I have a C++ program that enumerates all the input devices (using direct input) at the start of the program. If the program is started, and then I plug in another controller, this controller won't be recognized until the program is restarted. Anyone know of an event I can use that will cause my program to enumerate all of the devices after a new one is plugged in?
This article discusses how to detect game pad changes. First of all, you can handle the WM_DEVICECHANGE message and check wParam for DBT_DEVICEARRIVAL or DBT_DEVICEREMOVECOMPLETE. It seems that in order to receive these as WPARAMs, though, you need to call RegisterDeviceNotification first.
The article's example of how to do this is as follows:
DEV_BROADCAST_DEVICEINTERFACE notificationFilter;
ZeroMemory(&notificationFilter, sizeof(notificationFilter));
notificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
notificationFilter.dbcc_size = sizeof(notificationFilter);
HDEVNOTIFY hDevNotify;
hDevNotify = RegisterDeviceNotification(m_hWnd, &notificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE |
DEVICE_NOTIFY_ALL_INTERFACE_CLASSES);
if(hDevNotify == NULL) {
// do some error handling
}
The only other thing to watch out for is that the minimum supported OS for this is XP, so you need to put in the appropriate #define for that before including the Windows headers.
Depending on what you want to do, you might not even have to call this function first. Instead, you can just check DBT_DEVNODES_CHANGED to not differentiate between a device being plugged or unplugged. That could save some code if you don't care.
Got it working. When any device is removed or added just dispose all 'IDirectInputDevice8' and re-create them. This avoids bugs and keeps things simple.
Hook WinProc method to watch for add/remove events
bool refreshInputDevices = false;
LRESULT SubWndProc(int code, WPARAM wParam, LPARAM lParam)
{
// invalid code skip
if (code < 0) return CallNextHookEx(NULL, code, wParam, lParam);
// check if device was added/removed
PCWPSTRUCT pMsg = PCWPSTRUCT(lParam);
if (pMsg->message == WM_DEVICECHANGE)
{
switch (pMsg->wParam)
{
case DBT_DEVNODES_CHANGED:
refreshInputDevices = true;
break;
case DBT_DEVICEARRIVAL:
refreshInputDevices = true;
break;
case DBT_DEVICEREMOVECOMPLETE:
refreshInputDevices = true;
break;
}
}
// continue as normal
return CallNextHookEx(NULL, code, wParam, lParam);
}
Here is how you can hook on the input thread
// hook WinProc to watch for device changes
HMODULE module = GetModuleHandleW(NULL);
DWORD threadID = GetCurrentThreadId();
HHOOK hook = SetWindowsHookExW(WH_CALLWNDPROC, (HOOKPROC)&SubWndProc, module, threadID);

Hook Keyboard to change the key code

I have buy this keyboard http://www.mobilitylab.eu/mini-design-touch-silver.html of 107 touch,
and I want a keypad to put it on my left hand.
but when we activate the numlock of the keypad, it activates the numlock on the keyboard.
So we have 456- instead of uiop.
I have found this program but it don't work on a 64 bits OS. http://www.bellamyjc.org/fr/systeme.html#knumlock.
So i want to do my own program with C++, but it don't work fine, the hook is allright (WH_GETMESSAGE) but i don't understand how we can change the keycode and how we can find if it's a key of the keypad or the keybord ?
Here this is my code where i try to change the message :
//-----------------Keyboard Hook Callback---------------//
Hookmsg_API LRESULT CALLBACK Hookmsg(int ncode,WPARAM wparam,LPARAM lparam){
//if(ncode>=0) //
if(ncode<0)
return CallNextHookEx(hook,ncode,wparam,lparam);
MSG *msg;
msg=(MSG *)lparam;
WORD newVK,oldVK;
WORD newSC,oldSC;
if(ncode==HC_ACTION)
{
if((msg->message == WM_KEYUP))//Check whether key was pressed(not released).)
{
oldVK=msg->wParam;
oldSC=SCANCODE(msg->lParam);
bool extendkey=false;
if(((HIWORD(msg->wParam) & 0x0100) == 0x0100))
{
extendkey=true;
}
if(!extendkey)
{
bool modif=true;
switch(oldVK)//wparam
{
case VK_INSERT: newVK=VK_NUMPAD0; break;
case VK_END: newVK=VK_NUMPAD1; break;
case VK_DOWN: newVK=VK_NUMPAD2; break;
case VK_NEXT: newVK=VK_NUMPAD3; break;
case VK_LEFT: newVK=VK_NUMPAD4; break;
case VK_CLEAR: newVK=VK_NUMPAD5; break;
case VK_RIGHT: newVK=VK_NUMPAD6; break;
case VK_HOME: newVK=VK_NUMPAD7; break;
case VK_UP: newVK=VK_NUMPAD8; break;
case VK_PRIOR: newVK=VK_NUMPAD9; break;
case VK_DELETE: newVK=VK_DECIMAL; break;
default: modif=false;
}
if(modif==true)
{
msg->wParam = VK_NUMPAD0;
UINT newSC=MapVirtualKey(VK_NUMPAD0,MAPVK_VK_TO_VSC);
msg->lParam &= 0xFF00;
msg->lParam += (newSC << 16 );
//MessageBox( NULL, TEXT("OK"), TEXT("Error!"), MB_OK);
}
}
}
}
return ( CallNextHookEx(hook,ncode,wparam,lparam) );//pass control to next hook in the hook chain.
}
cant understand u...
u have 2 keyboards? if yes, try to use Raw Input (raw data from USB HID device)
http://msdn.microsoft.com/en-us/library/windows/desktop/ms645543(v=vs.85).aspx
Lparam and wparam are not visible for other applications.
Keyboard input is much more than just windows messages. Modifying the messages will work in some cases, but is a vastly incomplete solution. You also need to consider driver state, GetKeyboardState, and others.
If you want to remap keys on your keyboard, you can create a new keyboard layout and assign it to a locale.
If keyboard layouts don't satisfy your needs, you will need to write a keyboard device driver.
If you only need this functionality in a specific application (not system globally), then you might be able to get lucky and only modify windows messages.