使用头文件中定义的方法调用SetWindowsHookEx
Call SetWindowsHookEx with method defined in header file
我正在尝试添加一个低级鼠标钩子到一个类。我可以把这个函数放在我的CPP文件中:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
然后,我像这样在类构造函数中设置钩子:
HHOOK mousehook = SetWindowsHookEx(WH_MOUSE_LL, MouseHookProc, NULL, 0);
这可以很好地拦截鼠标事件,但是由于回调函数没有在我的类中定义,所以它不能访问我的任何类变量。
因此,我尝试在头文件中定义回调函数,如下所示:
LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam);
和在我的CPP文件中像这样(TMainForm是类):
LRESULT CALLBACK TMainForm::MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}
但是,当我尝试像这样编译时,我得到以下错误:
[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)'
[bcc32 Error] MainFormU.cpp(67): E2342 Type mismatch in parameter 'lpfn' (wanted 'long (__stdcall *)(int,unsigned int,long)', got 'void')
我到底做错了什么?自从我把它作为我的TMainForm
课的一部分以来,现在的方法有什么不同?
不能使用非静态类方法作为回调。非静态方法有一个隐藏的this
参数,因此回调的签名与SetWindowsHookEx()
期望的签名不匹配。即使编译器允许这样做(这只能通过强制转换完成),API也无法解释this
参数。
如果你想让回调是类的成员(所以它可以访问私有字段等),它必须被声明为static
,以删除this
参数,但随后你将不得不使用表单的全局指针到达它时需要,例如:
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
}
话虽如此,您应该考虑使用Raw Input API而不是SetWindowsHookEx()
。LowLevelMouseProc
文档甚至这样说:
注意调试钩子不能跟踪这种低级鼠标钩子。如果应用程序必须使用低级钩子,它应该在专用线程上运行钩子,该线程将工作传递给工作线程,然后立即返回。在大多数应用程序需要使用低级钩子的情况下,它应该监视原始输入而不是。这是因为原始输入可以比低级钩子更有效地异步监视针对其他线程的鼠标和键盘消息。有关原始输入的更多信息,请参见原始输入。
使用Raw Input,鼠标将直接发送WM_INPUT
消息到您的窗口。
如果您正在使用VCL,您可以覆盖虚拟WndProc()
方法来处理WM_INPUT
消息,不需要静态方法:
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对象可以作为子类的参数传递:
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);
}
我遇到了同样的问题,我发现对于我的特殊情况,最好的方法是创建一个指向我的类的静态指针数组。然后在静态钩子方法中,我只需遍历类指针并调用它们的钩子函数。
kb_hook.h
typedef KBDLLHOOKSTRUCT khookstruct;
typedef LRESULT lresult;
typedef void (*h_func)(uint64_t key_message, khookstruct* kbdhook);
typedef std::vector<kb_hook*> h_array;
class kb_hook
{
public:
kb_hook();
virtual ~kb_hook();
h_func hook_func;
private:
static h_array hook_array;
static lresult static_hook(int code, uint64_t key_message, khookstruct* kbdhook);
};
kb_hook.cpp
kb_hook::kb_hook() : hook_func(NULL)
{
this->hook_array.push_back(this);
}
lresult kb_hook::static_hook(int code, uint64_t key_message, khookstruct* kbdhook)
{
if(code == HC_ACTION)
{
for(auto it=kb_hook::hook_array.begin();it!=kb_hook::hook_array.end();it++)
{
if((*it)->hook_func) std::thread((*it)->hook_func, key_message, kbdhook).detach();
}
}
return CallNextHookEx(NULL, code, key_message, reinterpret_cast<LPARAM>(kbdhook));
}
我知道这是一个老问题,但我只是想提出我的观点。我希望这对大家有帮助。
- 如何强制从重写方法调用重写的方法基方法?
- C++:使用方法调用析构函数的顺序是什么?
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 使用 object 中的方法调用带有 std::bind 和 std::function.target 的 C 样式函数
- 指向类方法调用的指针
- 如何使用 SFINAE 在方法调用中有条件地定义变量?
- 是否有可以处理方法调用依赖关系的设计模式?
- 如何缩短C++中的方法调用?
- 从部分专用模板方法调用模板非静态方法
- 有没有办法禁止派生类中的基类方法调用?
- 为什么这C++只在编译器上编码一个不明确的方法调用Microsoft?
- 从父方法调用子方法
- 如何将子方法调用到父方法
- 虚拟函数在哪里使用 vpointer to vtable 来解析方法调用,非虚拟方法存储在哪里以及如何解析它们?
- 从静态方法调用静态函数指针
- 从同一类中的另一个方法调用方法时出错
- 方法调用意外地像 l 值一样起作用
- 无法从派生的一个方法调用基类方法
- 从类方法调用命名空间中名为 Same 的函数时,重载解析失败
- C 多线程JAVA JNI方法调用