调用SetWindowsHookEx与头文件中定义的方法 [英] Call SetWindowsHookEx with method defined in header file

查看:888
本文介绍了调用SetWindowsHookEx与头文件中定义的方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在类中添加一个低级鼠标钩子。我可以这样做通过将此函数放在我的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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆