gpt4 book ai didi

c++ - 使用头文件中定义的方法调用 SetWindowsHookEx

转载 作者:行者123 更新时间:2023-11-28 00:11:13 30 4
gpt4 key购买 nike

我正在尝试向类中添加低级鼠标 Hook 。我可以通过将这个函数放在我的 CPP 文件中来做到这一点:

LRESULT CALLBACK MouseHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
//my hook code here
return CallNextHookEx(0, nCode, wParam, lParam);
}

然后,我在类构造函数中设置钩子(Hook),如下所示:

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 错误] MainFormU.cpp(67): E2034 无法将“long (__stdcall * (_closure)(int,unsigned int,long))(int,unsigned int,long)”转换为“long ( __stdcall *)(int,unsigned int,long)'

[bcc32 错误] MainFormU.cpp(67): E2342 参数 'lpfn' 中的类型不匹配(需要 'long (__stdcall *)(int,unsigned int,long)',得到 'void')

我到底做错了什么?自从我将它作为我的 TMainForm 类的一部分以来,该方法现在有何不同?

最佳答案

您不能使用非静态类方法作为回调。非静态方法有一个隐藏的 this 参数,因此回调的签名与 SetWindowsHookEx() 期望的签名不匹配。即使编译器允许它(这只能通过转换来完成),API 也无法解释 this 参数。

如果你想让回调成为类的成员(这样它就可以访问私有(private)字段等),它必须声明为 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 documentation甚至这样说:

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.

使用原始输入,鼠标将发送 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);
}

关于c++ - 使用头文件中定义的方法调用 SetWindowsHookEx,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33024712/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com