pass lambda as Windows CALLBACK function failed - c++11

static std::function<LRESULT(int nCode, WPARAM wParam, LPARAM lParam)> keyProc =
[](int nCode, WPARAM wParam, LPARAM lParam)->LRESULT {
return 1;
};
keyHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyProc, theApp.m_hInstance, NULL);
Error C2664 'HHOOK SetWindowsHookExW(int,HOOKPROC,HINSTANCE,DWORD)':
cannot convert argument 2 from 'std::function' to
'HOOKPROC' MFCTest d:\test\mfctest2\mfctest\keymaphooker.cpp 62
PS: Why did I use such a long expression rather than auto? Because I want to it be static.
I changed it to
auto keyProc =
[this](int nCode, WPARAM wParam, LPARAM lParam)->LRESULT {
return 1;//return KeyboardProc(keyHook, keyMap, nCode, wParam, lParam);
};
It doesn't work again.
error C2664: 'HHOOK SetWindowsHookExW(int,HOOKPROC,HINSTANCE,DWORD)':
cannot convert argument 2 from
'KeyMapHooker::StartHook::'
to 'HOOKPROC'

A stateless lambda (such as the one you are using) can indeed be converted to a pointer to function, and thus can be used as CALLBACK. However, std::function cannot be converted in this manner.
Note that the type of a lambda expression is not std::function, it's an unnamed class type (the closure type of the lambda expression). You can capture the correct type using auto (which has no effect on storage duration):
static auto keyProc =
[](int nCode, WPARAM wParam, LPARAM lParam)->LRESULT {
return 1;
};
keyHook = SetWindowsHookEx(WH_KEYBOARD_LL, keyProc, theApp.m_hInstance, NULL);

Related

Encapsulate Win32 in a class - pass class pointer in CreateWindow

I viewed many tutorials and posts on this subject, and they all say I pass the class instance pointer (this) in my CreateWindowEx() function, and then store it in the window procedure function, when the WM_NCCREATE message is sent. I guess this is because WM_NCCREATE is supposedly the first message that gets sent to the window procedure since a window is created.
A few questions/notes:
From debugging I came to know that actually WM_GETMINMAXINFO is the first sent message, before WM_NCCREATE (at least on my machine). Does this mean I should listen for this message instead of WM_NCCREATE?
According to this popular article, the reason everyone calls SetWindowLongPtr() after the message WM_NCCREATE is received is because
If the value does not exist by the time WM_NCCREATE is called then,
by some mysterious Windows behavior that I still don't understand, the
value never gets inserted.
I tried to do exactly that (that is, call SetWindowLongPtr() after CreateWindowEx()). It turns out to be just fine. The application runs okay. Below is my code, please tell me if there's something wrong with this approach.
void GLWin32::CreateWindow(...)
{
...
_hwnd = CreateWindowEx(NULL, _wndclassex.lpszClassName, title.c_str(), WS_OVERLAPPEDWINDOW, x, y, width, height, NULL, NULL, _hinstance, NULL);
SetWindowLongPtr(_hwnd, GWL_USERDATA, reinterpret_cast<LONG_PTR>(this));
...
}
//static window procedure for all instances of this class
LRESULT CALLBACK GLWin32::_msgRouter(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
LONG l = GetWindowLongPtr(hwnd, GWLP_USERDATA);
GLWin32* thisPtr = reinterpret_cast<GLWin32*>(l);
if (thisPtr)
return thisPtr->_winProc(msg, wparam, lparam);
else
return DefWindowProc(hwnd, msg, wparam, lparam);
}
LRESULT GLWin32::_winProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CLOSE:
{
PostQuitMessage(0);
return 0;
}
}
return DefWindowProc(_hwnd, msg, wparam, lparam);
}
Why is my approach not used instead of the popular approach?
The problem with this approach is that if you want to use the instance when processing any of the window creation messages (including "ordinary" messages that are sent as part of the creation process), you won't have access to it.
Suppose you want to create a button when processing WM_CREATE (typical scenario) and you want to set the button text to some instance member value. You want something like:
LRESULT GLWin32::_winProc(UINT msg, WPARAM wparam, LPARAM lparam)
{
switch (msg)
{
case WM_CREATE:
{
CreateWindow("BUTTON", this->buttonText, WS_VISIBLE | WS_CHILD,
10, 10, 50, 30, this->hwnd, NULL, this->hInstance, NULL);
return 0;
}
}
return DefWindowProc(_hwnd, msg, wparam, lparam);
}
Problem is, when WM_CREATE is processed (before CreateWindowEx returns), SetWindowLongPtr wasn't called yet and the instance pointer is missing, so _winProc isn't being called at all.

How to pass arguments to SetWindowsHookEx's callback? (if even possible)

I'm using SetWindowsHookEx with WH_CALLWNDPROC to catch all WndProc messages - and it works fine.
I was wondering if there is a way to store a parameter somewhere where the callback function can read and use it.
I assume that because the callback function is inside the "other" process, there's no connection to the process calling the SetWindowsHookEx, so just storing a value in a static variable wont do any good..
For example:
void Hook(DWORD dwThread, int randomNumber)
{
MagicallyStore(randomNumber);
SetWindowsHookEx(WH_CALLWNDPROC, hookProc, GetModuleHandle(L"WNDProcHooks"), dwThread);
...
...
}
LRESULT CALLBACK hookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
int randomNumber = MagicallyRestore();
DoSomthing(randomNumber);
return CallNextHookEx(0, nCode, wParam, lParam);
}
I already thought about having the "MagicallyStore" function write the parameter to a file and reading it once in the "MagicallyRestore" function - but there must be a better way..
Thanks in advance :)

How to read the data from lParam (the pointers value)

I made a small application that sends text to notepad via SendMessage and EM_REPLACESEL.
Now I’m trying to hook notepad to get the EM_REPLACESEL value (the lParam value and in this case the “GET THIS TEXT” text).
EDIT: See this picture: http://i.stack.imgur.com/8scNL.jpg
The hook works fine, my problem is to listen for the EM_REPLACESEL message and grab the value from the lParam.
This code works fine, when messages are sent to notepad:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
Beep (2000,100);
return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}
So now I want to intercept EM_REPLACESEL messages. This do not work:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == EM_REPLACESEL)
{
Beep (2000,100);
}
return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}
1) How to listen for the EM_REPLACESEL message?
2) When I have gotten the message how to grab the lParam value and e.g. show it in a MessageBox. Something like this:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == EM_REPLACESEL)
{
MSG *lpMsg;
lpMsg = (MSG *) lParam;
MessageBox(NULL,(LPCWSTR)lpMsg,NULL,NULL);
}
return(CallNextHookEx(g_hHook, nCode, wParam, lParam));
}
Thanks
EM_REPLACESEL is a sent message, not a posted message, so you need to use a WH_CALLWNDPROC hook instead of a WH_GETMESSAGE hook, eg:
LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION) {
CWPSTRUCT* cwps = (CWPSTRUCT*)lParam;
if (cwps->message == EM_REPLACESEL) {
Beep (2000,100);
// etc..
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
... = SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, ...),
Your GetMsgProc() callback is coded wrong. Carefully read the linked MSDN page to see what the arguments of the callback mean. The nCode argument is not the message number, it specifies whether or not you should process the message. You want to use the passed lParam to recover the message that you intercepted. Make it look similar to this instead:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HC_ACTION) {
MSG* pmsg = (MSG*)lParam;
if (pmsg->message == WM_LBUTTONDOWN) {
Beep (2000,100);
// etc..
}
}
return CallNextHookEx(g_hHook, nCode, wParam, lParam);
}
Do note that you appear to use the WH_GETMESSAGE hook. It only works for messages that are posted to the message queue with PostMessage(). But EM_REPLACESEL is a message that's sent with SendMessage(). That requires a different hook, WH_CALLWNDPROC or WH_CALLWNDPROCRET.
This is how I normally do it.
LRESULT CALLBACK GetMsgProc(MSG nCode, WPARAM wParam, LPARAM lParam)
{
while(GetMessage(&nCode, NULL, 0, 0) > 0)
{
if(nCode.message == EM_REPLACESEL)
{
//Do something
}
else
DispatchMessage(&nCode);
}
return 0;
}

How to use DialogBox in Windows API

I'm learning windows api for some weeks, now I got a problem, my code is like this
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
switch(message){
case(...)
DialogBox(hInstance,MAKEINTRESOURCE(IDD_MYDIALOG),hwnd,(DLGPROC)MyDialogProc);
return 0;
}
bool MyDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam){
switch (message){
...
}
return false;
}
I don't know what else should I do before I use DialogBox where should I place EndDialog(). IDD_MYDIALOG is a resource file created by myself. I don't understand what is hInstance and how to get it, I think I just need a simple example, than I can know how use DialogBox. Thank you for helping!

WM_MOUSEHOVER in a global hook

I have implemented custom tooltips for a couple of controls in my application (MFC) but I would like to make it general for all the controls.
Right now I am calling TrackMouseEvent in WM_MOUSEMOVE and then catching WM_MOUSEHOVER (both in the overwritten function WindowProc of the control). But this way I have to duplicate code for every control. So my intention was to set a global hook for mouse events and there ask the control for the message to display in the tooltip.
The problem is that I am not able to catch WM_MOUSEHOVER in a global hook. This is the code:
hMouseHook = SetWindowsHookEx( WH_MOUSE,
CallWndMouseProc,
NULL,
AfxGetThread()->m_nThreadID);
hMainHook = SetWindowsHookEx( WH_CALLWNDPROC,
CallWndProc,
NULL,
AfxGetThread()->m_nThreadID);
LRESULT CALLBACK CallWndMouseProc( int nCode,
WPARAM wParam,
LPARAM lParam )
{
if(nCode == HC_ACTION)
{
MOUSEHOOKSTRUCT* pwp = (MOUSEHOOKSTRUCT*)lParam;
TRACE( _T("message: %x hwnd: %x x: %d y: %d\n"),
wParam,
pwp->hwnd,
pwp->pt.x,
pwp->pt.y);
TRACKMOUSEEVENT eventTrack;
eventTrack.cbSize = sizeof(TRACKMOUSEEVENT);
eventTrack.dwFlags = TME_HOVER;
eventTrack.dwHoverTime = HOVER_DEFAULT;
eventTrack.hwndTrack = pwp->hwnd;
_TrackMouseEvent(&eventTrack);
if(wParam == WM_MOUSEHOVER)
{
AfxMessageBox(_T("CallWndMouseProc: WM_MOUSEHOVER"));
}
}
// let the messages through to the next hook
return CallNextHookEx( hMouseHook,
nCode,
wParam,
lParam);
}
LRESULT CALLBACK CallWndProc( int nCode,
WPARAM wParam,
LPARAM lParam )
{
if(nCode == HC_ACTION)
{
CWPSTRUCT *pData = (CWPSTRUCT*)lParam;
if(pData->message == WM_MOUSEHOVER)
{
AfxMessageBox(_T("CallWndProc: WM_MOUSEHOVER"));
}
}
// let the messages through to the next hook
return CallNextHookEx( hMainHook,
nCode,
wParam,
lParam);
}
Both hooks are being call for the rest of messages and I am sure WM_MOUSEHOVER is being sent because it's capture in the WindowProc function. For instance this is the WindowProc function for a custom CListBox:
LRESULT CMyListBox::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
if(message == WM_MOUSEHOVER)
{
AfxMessageBox(_T("WindowProc: WM_MOUSEHOVER"));
}
return CListBox::WindowProc(message, wParam, lParam);
}
Ok. So what I am missing? It's just not possible to capture this message in a global hook? Is there other way to make this without having to put the same code in every single control?
Thanks.
Javier
WM_MOUSEHOVER is posted to the thread's message queue, so you won't see it with WH_CALLWNDPROC (that's for sent messages). WH_MOUSE does get posted messages, so I find it a little strange that you aren't seeing it... Perhaps the hook only gets low level mouse input messages? Try a WH_GETMESSAGE hook.

Resources