调用SetWindowsHookEx与头文件中定义的方法 [英] Call SetWindowsHookEx with method defined in header file
问题描述
我试图在类中添加一个低级鼠标钩子。我可以这样做通过将此函数放在我的CPP文件:
I am attempting to add a low level mouse hook to a class. I am able to do so by placing this function in my CPP file:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
然后,我在类构造函数中设置钩子, p>
Then, I set up the hook in the class constructor like so:
HHOOK mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
这对拦截鼠标事件很有效,但由于回调函数没有在我的类中定义,没有访问我的任何类变量。
This works fine for intercepting mouse events, however since the callback function is not defined in my class, it does not have access to any of my class variables.
因此,我尝试在我的头文件中定义回调函数,如下所示:
Therefore, I tried defining the callback function in my header file like so:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
,并且在我的CPP文件中(TMainForm是类):
and in my CPP file like this (TMainForm being the class):
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
<
However, when I attempt to compile like this, I get the following errors:
[bcc32错误] MainFormU.cpp(67):E2034无法转换long(__stdcall *(_closure)(int,unsigned int,long )
[bcc32 Error] MainFormU.cpp(67): E2034 Cannot convert 'long (__stdcall * (_closure )(int,unsigned int,long))(int,unsigned int,long)' to 'long (__stdcall *)(int,unsigned int,long)'
我在这里做什么?这个方法现在有什么不同,因为我把它作为 TMainForm
类的一部分?
What exactly am I doing wrong here? How is the method now different since I have made it a part of my TMainForm
class?
推荐答案
您不能使用非静态类方法作为回调。非静态方法有一个隐藏的这个
参数,因此回调的签名不匹配 SetWindowsHookEx()
期待。即使编译器允许它(这只能通过转换),API将无法解释 this
参数。
You cannot use a non-static class methods as the callback. Non-static methods have a hidden this
parameter, thus the signature of the callback does not match the signature that SetWindowsHookEx()
is expecting. Even if the compiler allowed it (which can only be done with casting), the API would not be able to account for the this
parameter anyway.
如果你想让回调成为类的成员(所以它可以访问私有字段等),它必须声明为 static
删除这个
参数,但是你将不得不使用窗体的全局指针来在需要时到达它,例如:
If you want to make the callback be a member of the class (so it can access private fields and such), it has to be declared as static
to remove the this
parameter, but then you will have to use the form's global pointer to reach it when needed, eg:
class TMainForm : public TForm
{
private:
HHOOK hMouseHook;
static LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
void MouseHook(int nCode, WPARAM wParam, LPARAM lParam);
public:
__fastcall TMainForm(TComponent *Owner);
__fastcall ~TMainForm();
};
extern TMainForm *MainForm;
__fastcall TMainForm::TMainForm(TComponent *Owner)
: TForm(Owner)
{
hMouseHook = SetWindowsHookEx(WH_MOUSE_LL, &MouseHookProc, NULL, 0);
}
__fastcall TMainForm::~TMainForm()
{
if (hMouseHook)
UnhookWindowsHookEx(hMouseHook);
}
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
MainForm->MouseHook(nCode, wParam, lParam);
return CallNextHookEx(0, nCode, wParam, lParam);
}
void TMainForm::MouseHook(int nCode, WPARAM wParam, LPARAM lParam)
{
// my hook code here
}
据说,你应该考虑使用原始输入API ,而不是 SetWindowsHookEx()
。 LowLevelMouseProc
文档甚至说:
With that said, you should consider using the Raw Input API instead of SetWindowsHookEx()
. The LowLevelMouseProc
documentation even says so:
注意调试钩子无法跟踪这种类型的低级鼠标钩子。如果应用程序必须使用低级钩子,它应该在专用线程上运行钩子,该线程将工作传递给工作线程,然后立即返回。 在大多数情况下,应用程序需要使用低级钩子,它应该监视原始输入。这是因为原始输入可以比低级钩子更有效地异步监视针对其他线程的鼠标和键盘消息。有关原始输入的详情,请参见原始输入。
Note Debug hooks cannot track this type of low level mouse hooks. If the application must use low level hooks, it should run the hooks on a dedicated thread that passes the work off to a worker thread and then immediately returns. In most cases where the application needs to use low level hooks, it should monitor raw input instead. This is because raw input can asynchronously monitor mouse and keyboard messages that are targeted for other threads more effectively than low level hooks can. For more information on raw input, see Raw Input.
使用Raw Input,鼠标将发送 WM_INPUT
直接发送到您的窗口。
Using Raw Input, the mouse will send WM_INPUT
messages directly to your window.
如果使用VCL,可以覆盖虚拟 WndProc()
方法来处理 WM_INPUT
消息,需要的方法:
If you are using VCL, you can override the virtual WndProc()
method to handle the WM_INPUT
message, no static method needed:
class TMainForm : public TForm
{
protected:
virtual void __fastcall CreateWnd();
virtual void __fastcall WndProc(TMessage &Message);
};
void __fastcall TMainForm::CreateWnd()
{
TForm::CreateWnd();
RAWINPUTDEVICE Device = {0};
Device.usUsagePage = 0x01;
Device.usUsage = 0x02;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = this->Handle;
RegisterRawInputDevices(&Device, 1, sizeof(RAWINPUTDEVICE));
}
void __fastcall TMainForm::WndProc(TMessage &Message)
{
if (Message.Msg == WM_INPUT)
{
HRAWINPUT hRawInput = (HRAWINPUT) Message.LParam;
UINT size = 0;
if (GetRawInputData(hRawInput, RID_INPUT, NULL, &size, sizeof(RAWINPUTHEADER)) == 0)
{
LPBYTE buf = new BYTE[size];
if (GetRawInputData(hRawInput, RID_INPUT, buf, &size, sizeof(RAWINPUTHEADER)) != 0)
{
RAWINPUT *input = (RAWINPUT*) buf;
// use input->data.mouse or input->data.hid as needed...
}
delete[] buf;
}
}
TForm::WndProc(Message);
}
如果使用FireMonkey,则没有 WndProc ()
用于处理窗口消息的方法(FireMonkey不会将窗口消息分发给用户代码)。但是,您可以子类化FireMonkey在内部创建的窗口,以便您仍然可以接收 WM_INPUT
消息。需要一个静态方法,但是你不必依赖于一个全局指针,Form对象可以作为子类的参数传递:
If you are using FireMonkey, there is no WndProc()
method for handling window messages (FireMonkey does not dispatch window messages to user code at all). However, you can subclass the window that FireMonkey creates internally so you can still receive the WM_INPUT
message. A static method is needed, but you do not have to rely on a global pointer, the Form object can be passed as a parameter of the subclassing:
class TMainForm : public TForm
{
private:
static LRESULT CALLBACK SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);
protected:
virtual void __fastcall CreateHandle();
};
void __fastcall TMainForm::CreateHandle()
{
TForm::CreateHandle();
HWND hWnd = Platform::Win::WindowHandleToPlatform(this->Handle)->Wnd;
SetWindowSubclass(hWnd, &SubclassProc, 1, (DWORD_PTR)this);
RAWINPUTDEVICE Device = {0};
Device.usUsagePage = 0x01;
Device.usUsage = 0x02;
Device.dwFlags = RIDEV_INPUTSINK;
Device.hwndTarget = hWnd;
RegisterRawInputDevices(&Device, 1, sizeof(RAWINPUTDEVICE));
}
LRESULT CALLBACK TMainForm::SubclassProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
TMainForm *pThis = (TMainForm*) dwRefData;
switch (uMsg)
{
case WM_INPUT:
{
// ...
break;
}
case WM_NCDESTROY:
{
RemoveWindowSubclass(hWnd, &SubclassProc, uIdSubclass);
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
这篇关于调用SetWindowsHookEx与头文件中定义的方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!