I'm using a low-level keyboard hook on windows. It works like a charme, despite of the fact that I'm currently unable to tell whether the key was initially pressed or pressed again. The documentation (+here) says, that bit 7 holds the transition state. But this seems only to be true when the key is being released. Bit 7 is sadly not set when I firstly press the key.
Is there any way to tell whether the key is pressed initially?
I happen to run into this problem recently. I can't find any good solutions, but I ended up using a flag and a GetAsyncKeyState before SetWindowHookEx.
BOOL wasDown;
LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
if (nCode == HC_ACTION) {
LPKBDLLHOOKSTRUCT key = (LPKBDLLHOOKSTRUCT) lParam;
if (key->vkCode == VK_SOMETHING) {
switch (wParam) {
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (!wasDown) {
// Processing on first key down
wasDown = true;
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
// Processing on key up
wasDown = FALSE;
break;
}
}
return CallNextHookEx(NULL, nCode, wParam, lParam);
}
wasDown = GetAsyncKeyState(VK_SOMETHING) < 0;
hHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);
Of course, this code only does it for one key. You can use an array of flags to do multiple keys. Depending on your application you could also unconditionally set the flag to false if you want the first press after your hook is set.
Related
I have a dialog box with a Tree-View control where the user can edit the item labels. I want the user to be able to cancel the label edit by pressing ESC key.
The problem is that pressing ESC closes the dialog window immediately.
I have tried getting the handle to the EditBox control by a TreeView_GetEditControl() call upon TVN_BEGINLABELEDIT message and subclassing it to trap the ESC key, but when I do that, typing in edit box becomes impossible.
What is the problem?
Here is the relevant code:
INT_PTR CALLBACK DlgProc(HWND hWnd, UINT message,
WPARAM wParam, LPARAM lParam) {
switch(message) {
//...
case WM_NOTIFY:
{
LPNMHDR pNmHdr = (LPNMHDR)lParam;
switch(pNmHdr->code) {
case TVN_BEGINLABELEDIT:
{
HWND hwndTV = (HWND)GetWindowLongPtr(hWnd, GWLP_USERDATA); // stored handle to Tree-View ctl
HWND hWndEditBox = TreeView_GetEditControl(hwndTV);
// subclass edit box
TreeViewGlobals::g_wpOrigEditBoxProc =
(WNDPROC)SetWindowLongPtr(hWndEditBox,
GWLP_WNDPROC, (LONG_PTR)EditBoxCtl_SubclassProc);
break;
}
case TVN_ENDLABELEDIT:
{
SetWindowLongPtr(hWnd, DWLP_MSGRESULT, (LONG)TRUE); // accept edit
return TRUE;
}
default:
break;
}
}
default:
break;
}
return FALSE;
}
INT_PTR CALLBACK EditBoxCtl_SubclassProc(HWND hWndEditBox, UINT message,
WPARAM wParam, LPARAM lParam) {
switch(message) {
HANDLE_MSG(hWndEditBox, WM_GETDLGCODE, EditBoxCtl_OnGetDlgCode);
HANDLE_MSG(hWndEditBox, WM_KEYDOWN, EditBoxCtl_OnKey); // does not receive WM_KEYDOWN for ESC unless I handle WM_GETDLGCODE above
default:
break;
}
return CallWindowProc(TreeViewGlobals::g_wpOrigEditBoxProc,
hWndEditBox, message, wParam, lParam);
}
UINT EditBoxCtl_OnGetDlgCode(HWND hWndEditBox, LPMSG lpmsg) {
if(lpmsg) {
if(lpmsg->message == WM_KEYDOWN && lpmsg->wParam == VK_ESCAPE) {
return DLGC_WANTMESSAGE;
}
}
return 0;
}
void EditBoxCtl_OnKey(HWND hWndEditBox, UINT vk, BOOL fDown,
int cRepeat, UINT flags) {
switch(vk) {
case VK_ESCAPE:
Beep(4000, 150); // never beeps
break;
default:
break;
}
}
P.S. I noticed that when I remove WM_GETDLGCODE handler in EditBoxCtl_SubclassProc(), it becomes possible to type in the edit box again, but then I can't trap WM_KEYDOWN for ESC key from that procedure.
Below is the solution that I found. The trick seems to be calling the original control proc with WM_GETDLGCODE intercepted in subclass proc, storing the return value and then returning it with DLGC_WANTALLKEYS or DLGC_WANTMESSAGE flag set to prevent system from further processing the keystroke.
The upside to this approach is that pressing ESC cancels editing and reverts the item label to its original text, and pressing ENTER while editing no longer just closes the dialog(which was another problem) without any additional code to handle those cases.
Here is the code that works:
INT_PTR CALLBACK EditBoxCtl_SubclassProc(HWND hWndEditBox, UINT message,
WPARAM wParam, LPARAM lParam) {
switch(message) {
//HANDLE_MSG(hWndEditBox, WM_GETDLGCODE, EditBoxCtl_OnGetDlgCode); // can't use this: need wParam and lParam for CallWindowProc()
case WM_GETDLGCODE: {
INT_PTR ret = CallWindowProc(TreeViewGlobals::g_wpOrigEditBoxProc,
hWndEditBox, message, wParam, lParam);
MSG* lpmsg = (MSG*)lParam;
if(lpmsg) {
if(lpmsg->message == WM_KEYDOWN &&
(lpmsg->wParam == VK_ESCAPE || lpmsg->wParam == VK_RETURN) )
{
return ret | DLGC_WANTALLKEYS;
}
}
return ret;
}
default:
break;
}
return CallWindowProc(TreeViewGlobals::g_wpOrigEditBoxProc,
hWndEditBox, message, wParam, lParam);
}
The problem is that the modal dialog has its own message loop and its own translation with IsDialogMessage. Using the MFC I would say, just use PreTranslateMessage but this isn't available in plain WinApi. You don't have access to the internal message loop and the keyboard interface.
So the Escape key is handled inside the message loop. And causes a WM_COMMAND message with IDCANCEL to be sent. (See the MSDN specs about dialogs)
Maybe the easiest way is to interrcept the WM_COMMAND message sent to the dialog, check if who has the focus and if the inplace edit control has the focus you just set the focus back to the tree control and eat forget the IDCANCEL and don't close the dialog.
you need remember the tree-view hwnd when you receive TVN_BEGINLABELEDIT (in class member, associated with dialog) and zero it when you receive TVN_ENDLABELEDIT. when user press esc or enter in modal dialog box - you receive WM_COMMAND with IDCANCEL (on esc) or IDOK( on enter). you need check saved tree-view hwnd and if it not 0 - call TreeView_EndEditLabelNow
switch (uMsg)
{
case WM_INITDIALOG:
m_hwndTV = 0;
break;
case WM_NOTIFY:
switch (reinterpret_cast<NMHDR*>(lParam)->code)
{
case TVN_BEGINLABELEDIT:
m_hwndTV = reinterpret_cast<NMHDR*>(lParam)->hwndFrom;
return TRUE;
case TVN_ENDLABELEDIT:
m_hwndTV = 0;
//set the item's label to the edited text
SetWindowLongPtrW(hwndDlg, DWLP_MSGRESULT, TRUE);
return TRUE;
}
break;
case WM_CLOSE:
EndDialog(hwndDlg, 0);
break;
case WM_COMMAND:
switch (wParam)
{
case IDCANCEL:
if (m_hwndTV)
{
TreeView_EndEditLabelNow(m_hwndTV, TRUE);
}
else
{
EndDialog(hwndDlg, IDCANCEL);
}
break;
case IDOK:
if (m_hwndTV)
{
TreeView_EndEditLabelNow(m_hwndTV, FALSE);
}
else
{
EndDialog(hwndDlg, IDOK);
}
break;
}
break;
}
Consider the following code which needs to call one of two functions in User32.dll.
if( IsWindowsVistaOrGreater() )
AddClipboardFormatListener(hWnd) ;
else
SetClipboardViewer(hWnd) ;
If I'm not mistaken, this program will fail to start on WinXP because AddClipboardFormatListener does not exist in User32.dll under XP.
One way to solve this is not calling AddClipboardFormatListener directly but rather get a pointer to it ourselves:
GetProcAddress(GetModuleHandle("User32.dll"), "AddClipboardFormatListener").
However, if I instruct the linker to delay-load User32.dll...
Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?
Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?
The case of User32.dll is particularly dramatic on the second point since most of the functions used in the program are know to exist in that DLL on all Windows versions.
I would guess that linking at load-time is more efficient than at run-time since the latter needs additional checks before each function call.
But I'm just guessing, hence the question.
Would this avoid loading that specific function under XP so that I don't need to call GetModuleHandle and GetProcAddress?
Yes. This is exactly the type of situation that delay-loading was invented for. your code can call a DLL function as if it were statically linked, but the executable will not load the DLL function pointer at runtime until the function is actually called for the first time. Internally, the delay-load mechanism uses LoadLibrary() and GetProcAddress() for you.
Is it recommended to delay-load a DLL when only a few functions need to be delay-loaded?
If a DLL is delay-loaded, ALL of its functions are delay-loaded, you cannot pick and choose which ones you want. So, if your app needs to use a lot of functions from the same DLL, like user32.dll, then static linking is usually more efficient, and then you can use GetProcAddress() manually for the few functions you really need to handle differently.
In this case, I would suggest getting rid of the OS check altogether and rely only on whether the DLL function actually exists or not, eg:
typedef BOOL (WINAPI *LPFN_ACFL)(HWND);
LPFN_ACFL lpAddClipboardFormatListener = (LPFN_ACFL) GetProcAddress(GetModuleHandle(TEXT("user32")), "AddClipboardFormatListener");
if( lpAddClipboardFormatListener != NULL )
lpAddClipboardFormatListener(hWnd);
else
SetClipboardViewer(hWnd);
Where delay-loading really shines is in its hooks. For instance, on earlier systems, you can use a delay-load failure hook to implement your own version of AddClipboardFormatListener(), and then your main code can just call AddClipboardFormatListener() unconditionally on all systems and it won't know the difference. For example (just a demo, not actually tested):
LRESULT CALLBACK ClipboardSubClassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass);
break;
case WM_CHANGECBCHAIN:
{
if (wParam == dwRefData)
SetWindowSubclass(hWnd, &ClipboardSubClassProc, uIdSubclass, lParam);
else if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
break;
}
case WM_DRAWCLIPBOARD:
{
SendMessage(hWnd, WM_CLIPBOARDUPDATE, 0, 0);
if (dwRefData != 0)
SendMessage((HWND)dwRefData, uMsg, wParam, lParam);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
BOOL WINAPI My_AddClipboardFormatListener(HWND hWnd)
{
HWND hWndNext = SetClipboardViewer(hWnd);
if ((!hWndNext) && (GetLastError() != 0))
return FALSE;
if (!SetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, (DWORD_PTR)hWndNext))
{
DWORD dwErr = GetLastError();
ChangeClipboardChain(hWnd, hwndNext);
SetLastError(dwErr);
return FALSE;
}
return TRUE;
}
BOOL WINAPI My_RemoveClipboardFormatListener(HWND hWnd)
{
DWORD_PTR dwRefData;
if (!GetWindowSubclass(hWnd, &ClipboardSubClassProc, 1, &dwRefData))
{
SetLastError(ERROR_NOT_FOUND);
return FALSE;
}
RemoveWindowSubclass(hWnd, &ClipboardSubClassProc, 1);
return ChangeClipboardChain(hWnd, (HWND)dwRefData);
}
FARPROC WINAPI MyDliFailureHook(unsigned dliNotify, PDelayLoadInfo pdli)
{
if ((dliNotify == dliFailGetProc) && (pdli->dlp.fImportByName))
{
if (strcmp(pdli->dlp.szProcName, "AddClipboardFormatListener") == 0)
return (FARPROC) &My_AddClipboardFormatListener;
if (strcmp(pdli->dlp.szProcName, "RemoveClipboardFormatListener") == 0)
return (FARPROC) &My_RemoveClipboardFormatListener;
}
return NULL;
}
__pfnDliFailureHook2 = &MyDliFailureHook;
...
LRESULT CALLBACK MyWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE:
AddClipboardFormatListener(hWnd);
break;
case WM_DESTROY:
RemoveClipboardFormatListener(hWnd);
break;
case WM_CLIPBOARDUPDATE:
// do all of your clipboard processing here...
break;
...
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
i want to stop my win32 window to be minimized programatically , so I did below but its not working. please let me know any other solution or if i am doing something wrong here.
for testing I am minimizing my window after 2 sec& trying to catch that that and maximize my window again. but its not working as expected.
below is the code.
case WM_SYSCOMMAND:
if ((wParam & 0xFFF0) == SC_MINIMIZE)
{
ShowWindow(hWnd, SW_SHOWMAXIMIZED);
UpdateWindow(hWnd);
}
else
DefWindowProc(hWnd, message, wParam, lParam);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
//show & hide for every 2sec
VOID CALLBACK timerelapsed(HWND hWnd, UINT, UINT_PTR, DWORD)
{
ShowWindow(hWnd, SW_SHOWMINIMIZED);
}
Read the documentation for WM_WINDOWPOSCHANGING. In particular:
An application can prevent changes to the window by setting or clearing the appropriate bits in the flags member of WINDOWPOS.
OK so I have my main window procedure (simplified):
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_CREATE:
{
if(!loadFiles())
SendMessage(hwnd, WM_CLOSE, 0, 0);
}
break;
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
loadFiles() is a function to read some text files. If the files are missing, it posts an error message and then returns false. I handle this by sending the WM_CLOSE message, which sends WM_DESTROY? The program calls PostQuitMessage(0) and.. nothing. The process is still sitting in background, now using 100% CPU on core 1. Here is my message loop :
MSG Msg;
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
return Msg.wParam;
Very simple, I've looked around and seen people using all kinds of different loops, most with PeekMessage() function.. its worth noting I have several child windows containing different controls, I show and hide them when the user selects different tabs, but I don't think it matters because PostQuitMessage(0) is supposed to post WM_QUIT to the message queue, which destroys the child windows aswell right? Any help? Thanks
According to MSDN WM_CREATE is sent during CreateWindow. So you are destroying the window right during creation. I am not sure that this is supposed to work. Also, why so complicated? The documentation says
If an application processes this message, it should return zero to
continue creation of the window. If the application returns –1, the
window is destroyed and the CreateWindowEx or CreateWindow function
returns a NULL handle.
So just return -1 in case loadFiles() fails and handle CreateWindow returning NULL accordingly.
I'm working on a windows application where I'm implementing the whole event loop and everything like that myself (there's a reason for that). In one place, I need to execute some code AFTER a window is shown. Normally, when the window is created, I do some initialisation when WM_CREATE message is received. WM_SHOWWINDOW is sent just BEFORE the window is displayed. However I need to get some code executed right AFTER the window is displayed for the first time. I can't seem to find a notification message sent AFTER the window is shown. Could it be that there isn't one?
Of course, I can keep a boolean - FirstRun - indicating whether or not I had performed my logic, and then execute the code when WM_ACTIVATE is received, provided the boolean is TRUE, then set FirstRun to FALSE so that the code is not execute the next time I am receive WM_ACTIVATE, but this seems somewhat unnatural to me.
It's been ages since I did win32 programming on this level, so can't remember much of it. What is the best approach here?
There is no special notification, but in many cases you can use this trick:
LRESULT CALLBACK MainWndProc(
HWND hwnd, // handle to window
UINT uMsg, // message identifier
WPARAM wParam, // first message parameter
LPARAM lParam) // second message parameter
{
switch (uMsg)
{
case WM_USER + 100:
//window is just displayed, do some actions
return DefWindowProc(hwnd, uMsg, wParam, lParam);
case WM_CREATE:
PostMessage(hwnd, WM_USER + 100, 0, 0);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
There isn't one because that's what WM_SHOWWINDOW is for. Once that mesage is passed to the default message handling procedure, the window will be shown. The best thing you could do is poll with IsWindowVisible via some sort of timer.
Your program design seems flawed though to have to rely on something like this. What are you trying to do?
As long as you are implementing the whole event loop and everything like that yourself, you can handle this directly in WinMain() like this:
HWND hWnd = CreateWindow(...);
if (!hWnd) return 0;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
OnWindowJustDisplayed(); // here
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}