I am having issues with creating a modeless dialog from a DLL file. My dialog has nothing special on it, just an OK button and an edit box. I have looked at this Microsoft KB Article (http://support.microsoft.com/kb/233263) and have implemented its solution to create a window hook to grab and process messages.
The method provided by Microsoft solves the tab key problem, however, it creates another problem. When I type into the edit box on the dialog, whatever I press is duplicated 4 times. For example, if I press 'a' on the keyboard, 'aaaa' will show up in the edit box.
If I disable the Window Hook, then the edit box works correctly and only displays one 'a'.
What do I need to do to the Window Hook procedure to solve this problem?
Any help is greatly appreciated.
- - EDIT - -
As per request, my Window Hook Procedure Code: (It's the same as the KB article)
LRESULT FAR PASCAL GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) {
LPMSG lpMsg = (LPMSG) lParam;
if (nCode >= 0 && PM_REMOVE == wParam) {
// Don't translate non-input events.
if ((lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST)) {
if (IsDialogMessage(hwndDllDlg, lpMsg)) {
// The value returned from this hookproc is ignored,
// and it cannot be used to tell Windows the message has been handled.
// To avoid further processing, convert the message to WM_NULL
// before returning.
lpMsg->message = WM_NULL;
lpMsg->lParam = 0;
lpMsg->wParam = 0;
}
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
My Dialog Callback Procedure:
BOOL CALLBACK DllDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_INITDIALOG:
hHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, GetCurrentThreadId());
return TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) {
DestroyWindow(hwndDlg);
hwndDllDlg = NULL;
}
return TRUE;
case WM_DESTROY:
UnhookWindowsHookEx(hHook);
return FALSE;
}
return FALSE;
}
}
Both hHook and hwndDllDlg are defined as HHOOK and HWND respectively.
HHOOK hHook;
HWND hwndDllDlg = CreateDialog(0, MAKEINTRESOURCE(DLG_MAIN), 0, DllDlgProc);
I looked at the KB article. It sounds reasonable. There is some point where you was not enough accurate while following the instructions from KB. Post your code. This may help.
If you have control over the message pump of the executable and can add IsDialogMessage there, then you do not need any hook. Code from the dll is part of the code of the process. Window handles are in the common space either.
Other approach is starting your own UI thread. If you create your dialog on this thread, then you will have your own message pump. The hook will not be needed in this case either.
Well, this is more of a question to author of the post..
I have the tab key issue and am trying to understand the microsoft article better.
So my dialog is shipped out as Dll and the application which I don't have access to is launching dialog from my dll.
HWND hwndDllDlg = CreateDialog(0, MAKEINTRESOURCE(DLG_MAIN), 0, DllDlgProc);
I don't understand what dialog the code refers to when they said hwndDllDlg in the article. Should I point my dialog creation to this variable ?
Related
I am using the Windows API on Windows7 with Visual Studio 2019.
I'm trying to set a low level keyboard hook and read the key input to the console, to do so I use the function: HHOOK SetWindowsHookExA( [in] int idHook, [in] HOOKPROC lpfn, [in] HINSTANCE hmod, [in] DWORD dwThreadId )
with the argument: WH_KEYBOARD_LL so I can monitor keyboard events.
This is my main function:
int main()
{
...
HHOOK keyBoardHook = SetWindowsHookExW(WH_KEYBOARD_LL, keyboardHook, NULL, 0);
MSG message;
while (true)
{
while(PeekMessage(&message, NULL, 0, 0, PM_REMOVE) != 0)
{
DispatchMessage(&message);
}
}
...
UnhookWindowsHookEx(keyBoardHook);
return 0;
}
And this is the callback implementation:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
{
fileHandler.addStr(keyHandler.translate(keyData,true,topWindowThreadId));
}
else if (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)
{
fileHandler.addStr(keyHandler.translate(keyData,false,topWindowThreadId));
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
How both variables work is not the issue here but just know that
in the callback keyHandler translates the message and fileHandler prints the result to a file.
Here comes the issue. The callback receives:
a key down event when I press a key
a key up event when I release
a repeated key down event when I hold a key
a control key up event VK_LCONTROL when focused window changes
Why does that happen ? This behavior is never mentioned in the MSDN documentation as far as I know. If I switch from console window or any window to any other window this event is generated and a [left control up] message is printed by my keyHandler !
Now to make sure it's not a problem with my code I changed the Callback to this:
LRESULT CALLBACK keyboardHook(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION)
{
KBDLLHOOKSTRUCT* keyData = (KBDLLHOOKSTRUCT*)lParam;
if (keyData->vkCode == VK_LCONTROL)
{
printf("CTRL");
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
And still CTRL is printed to the consol whenever I change the focused window, Note that only VK_LCONTROL is generated and wParam is always WM_KEYUP
This is not supposed to happen!
I tried analyzing and modifying my code, tried removing everything from the main function until it looked just like in the example above even rebooted the computer to check if it's an internal issue, nothing helps. and when searching the web for a similar post with a similar issue I found absolutely none, which makes it even more confusing.
Update
I tried compiling someone else's keyboard-hook related code from GitHub, same issue.. is this normal ? Why is it not documented ?
According to the description of the SetWindowsHookExA in the official Microsoft documentation.
The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in libraries. Global hooks should be restricted to special-purpose applications or to use as a development aid during application debugging. Libraries that no longer need a hook should remove its hook procedure.
HHOOK SetWindowsHookExA(
[in] int idHook,
[in] HOOKPROC lpfn,
[in] HINSTANCE hmod,
[in] DWORD dwThreadId
);
For desktop apps, if this parameter(dwThreadId) is zero, the hook procedure is associated with all existing threads running in the same desktop as the calling thread. That's why after you changed the focused window, CTRL still printed to the console. So it is recommended that you use hook in DLL.
I'm using Windows 7 and VC++. The business is to know how many seconds my system has been set into screen saver mode or monitor screen off. To achieve this, I'm trying to catch the events WM_SYSCOMMAND and SC_SCREENSAVE, SC_MONITORPOWER. So I have created a Win32 project in Visual Studio 2008 and I'm receiving the events in WndProc function:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
int wmId, wmEvent;
PAINTSTRUCT ps;
HDC hdc;
switch (message)
{
case WM_SYSCOMMAND:
{
switch (LOWORD(wParam))
{
case SC_SCREENSAVE:
{
FILE *fl = fopen("this_is_a_event_test.txt","a");
fputs("SC_SCREENSAVE\n",fl);
fclose(fl);
}
break;
case SC_MONITORPOWER:
{
FILE *fl = fopen("this_is_a_event_test.txt","a");
fputs("SC_MONITORPOWER\n",fl);
fclose(fl);
}
break;
default:
{
}
}
}
break;
}
}
It works fine when dialog is in foreground, but in background (or if I comment ShowWindow function) it only works if I manually send the events:
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_SCREENSAVE, (LPARAM)2);
or
SendMessage(HWND_BROADCAST, WM_SYSCOMMAND, SC_MONITORPOWER, (LPARAM)2);
So, it is not working when system power configuration sets the screen saver after 2 minutes of inactivity, and the same thing with automatic monitor screen off. Thats the real thing I want, know when the system is turning off the screen or setting the screen saver, with a background monitoring program.
I have also tried to use hook events with extern dll. I have followed this example http://www.codeproject.com/Articles/1037/Hooks-and-DLLs adding in the CALLBACK msghook() function the same switch code above in WndProc. It doesn't work even using the SendMessage.
After several days stuck with this issue, searching in the Internet, forums... I don't know what else I can do. Can anyone help me?
I were not using hooks properly, but it has been rare. Firstly, about setWindowsHookEx function, I have read WH_CALLWNDPROC or WH_SYSMSGFILTER must be used to get WM_SYSCOMMAND sent messages, and then get SC_SCREENSAVE wParam. In this case, I don't know why and maybe I'm wrong, but thats seems not to be true.
After use every possible message to SetWindowsHookEx, I realised WH_GETMESSAGE is the only one who sends SC_SCREENSAVE wParam, at least in this hook example in Windows 7.
HHOOK hook;
HHOOK hook = SetWindowsHookEx(WH_GETMESSAGE,
(HOOKPROC)msghook,
hInst,
0);
Secondly, listening for every message catched in hook function, WM_SYSCOMMAND were appeared with LPMSG. I have read also that wParam must to be combined to 0xFFF0 to be compared. But wParam & 0xFFF0 == SC_SCREENSAVE didn't work and wParam == SC_SCREENSAVE neither. In this case the only way is using LPMSG for both WM_SYSCOMMAND and SC_SCREENSAVE.
static LRESULT CALLBACK msghook(UINT code, WPARAM wParam, LPARAM lParam)
{
if(code > 0)
{
CallNextHookEx(hook, code, wParam, lParam);
return 0;
}
LPMSG msg = (LPMSG)lParam;
if(msg->message == WM_SYSCOMMAND)
{
if (msg->wParam == SC_SCREENSAVE)
{
MessageBoxA(NULL,L"SC_SCREENSAVE",L"SC_SCREENSAVE",MB_OK);
}
if (msg->wParam == SC_MONITORPOWER)
{
MessageBoxA(NULL,L"SC_MONITORPOWER",L"SC_MONITORPOWER",MB_OK);
}
}
return CallNextHookEx(hook, nCode, wParam, lParam);
}
And using FILE to test the events was a very bad idea, I think using MessageBox is not much better but I don't know how to test ir correctly.
I am currently trying to grab all of the user input to the windows calculator app. It seems the way to do this is to use Win32 to intercept all of the keyboard and mouse inputs that are intended for the calculator window. I have read the MSDN page on subclassing a window at the link below and have done some research on subclassing.
I have the syntax for subclassing a window, but I am not sure how to tell the program which window I am looking to subclass.
the code that I have so far is listed below. My problem right now is that I am not sure how the variable "hWndEdit" is assigned. I am pretty new to Win32 programming so any help is appreciated.
(link)
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633570(v=vs.85).aspx
WNDPROC wpOrigEditProc;
wpOrigEditProc = (WNDPROC) SetWindowLong(hWndEdit,GWL_WNDPROC,(long) WndEditProc);
LRESULT CALLBACK WndEditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CHAR:
case WM_KEYUP:
case WM_KEYDOWN:
if (hWnd == hWndEdit)
return 0;
break;
case WM_DESTROY:
// Remove the subclass from the edit control.
SetWindowLong(hWndEdit, GWL_WNDPROC, (LONG) wpOrigEditProc);
break;
default:
return CallWindowProc((WNDPROC ) wpOrigEditProc, hWnd, message, wParam, lParam);
}
return CallWindowProc((WNDPROC ) wpOrigEditProc, hWnd, message, wParam, lParam);
}
To find a window, first use Spy++ (A tool that gets installed with Visual Studio) to find the class name and the window name of the calculator main window. Then, in your application, use the FindWindow API:
hWndEdit = FindWindow(className, windowName);
Although, I'm not sure that subclassing is the right method here since the Calculator window is not owned by your application. You should do this with hooks.
I want to detect everytime I paste something. It's just something to make some data entry work simpler.
I set a global hook and then "wait" for the wm_paste. This is part of the code I have:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0)//Do not process the message
return CallNextHookEx(msg_hook,nCode,wParam,lParam);
LPMSG m=(LPMSG)lParam;
if(m->message == WM_PASTE)
{
OutputString("Paste detected!\n");
}
if(m->message == WM_PASTE)
{
OutputString("Paste detected!\n");
}
return CallNextHookEx(msg_hook,nCode,wParam,lParam);
}
//DLL_ATTACH:
...
if(strstr(ProcName, LOADERNAME))
{
InitCommonControls();
if(!(msg_hook=SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hinstDLL, 0)))
{
ErrorExit(TEXT("SetWindowsHookEx"));
//MessageBox(0, "WH_GETMESSAGE", 0, 0);
//return -1;
}
}
WM_PASTE debug string never gets printed. I'm aware that not all applications use WM_PASTE. But at least notepad should work.
Any suggestions?
Thanks!
In GetMsgProc, the wParam parameter is not the message being intercepted, but a flag that indicates whether or not the message in lParam and been removed from the message queue.
You should be using m->wParam instead.
Wm_paste message is fired only in combo box and edit control. There is no easy way to capture paste, but you can get copy message by creating a tiny window and adding this window to the chain of clipboard viewers.
I am trying to subclass the currently focused window on a Windows system using a global CBT hook. This is related to what happens in this question, but the bug is different.
What happens when this subclassing is in effect, is that Opera's (version 10.50) main window is prevented from displaying. Opera has a "splash screen" where you are required to click "Start" for the main window to show that appears after Opera has not shut down properly. Whenever this window pops up, Opera's main window won't show. If Opera was shut down properly, and this splash screen does not show, the main window displays as it should.
HHOOK hHook;
HWND hWndSubclass = 0;
void SubclassWindow(HWND hWnd)
{
Unsubclass();
FARPROC lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc);
SetProp(hWnd, L"PROP_OLDWNDPROC", lpfnOldWndProc);
hWndSubclass = hWnd;
}
void Unsubclass()
{
if (hWndSubclass != 0 && IsWindow(hWndSubclass))
{
FARPROC lpfnOldWndProc = (FARPROC)GetProp(hWndSubclass, L"PROP_OLDWNDPROC");
RemoveProp(hWndSubclass, L"PROP_OLDWNDPROC");
SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc);
hWndSubclass = 0;
}
}
static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MOVING)
{
// do something irrelevant
}
else if (message == WM_DESTROY)
{
Unsubclass();
}
FARPROC lpfnOldWndProc = (FARPROC)GetProp(hWndSubclass, L"PROP_OLDWNDPROC");
return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam);
}
static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_SETFOCUS && hWndServer != NULL)
{
SubclassWindow((HWND)wParam);
}
if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
return 0;
}
BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD Reason,
LPVOID Reserved
)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
hInst = hInstance;
return TRUE;
case DLL_PROCESS_DETACH:
Unsubclass();
return TRUE;
}
return TRUE;
}
My suspicion is that Opera's main window is somehow already subclassed. I imagine the following is happening:
The window is created with it's own basic WndProc, and is given focus
My application subclasses the window, storing the original WndProc
Opera subclasses its own window
When the window loses focus, I restore the original WndProc, thus ignoring the second WndProc
Can this really be the case? Are there any other explanations?
This can happen, as Raymond Chen writes:
Consider what would happen if somebody else had subclassed the window during the "... do stuff ..." section. When we unsubclassed the window, we would have removed two subclasses, the one we installed, and the one that was installed after us. If the other subclass allocated memory (which is very common), then that memory got leaked, in addition to the subclass failing to do whatever it was trying to do.
He continues with a solution:
This is quite a cumbersome process, so the shell team wrote some helper functions to do all this for you. The SetWindowSubclass function does all the grunt work of installing a subclass procedure, remembering the previous one, and passing reference data to the subclass procedure you provide. You use the DefSubclassProc function to forward the message to the previous subclass procedure, and when you're done, you use the RemoveWindowSubclass function to remove yourself from the chain. RemoveWindowSubclass does all the work to do the right thing if you are not the window procerure at the top of the chain.