为什么这种行为 - 我的全局钩子与消息循环一起工作但不是空循环?

时间:2013-09-30 09:22:33

标签: c++ windows winapi

我会先说这是我对win32编程的新手。

我正在使用.dll创建一个全局键盘钩子,我已经安装了卸载函数来处理实际设置和删除键盘钩子。

#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
static HHOOK hk=NULL;
//static CMyFile *pLF;
#pragma data_seg()

HINSTANCE hins = NULL;

__declspec( dllexport ) LRESULT Install(){
    std::cout << "Installing" << std::endl;
    hk = SetWindowsHookEx(WH_KEYBOARD_LL,EventAnalysis::KeystrokeAnalysis::KeyboardCallback,hins,0);
    if(hk == NULL){
        std::cout << "Hook creation failed! - " << GetLastError() << std::endl;
    }
    return 0;
}


 __declspec(dllexport) BOOL CALLBACK UnInstall()
{
    std::cout << "UnInstalling" << std::endl;
    return UnhookWindowsHookEx(hk);
}

BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        hins = (HINSTANCE) hModule;
        break;
    case DLL_THREAD_ATTACH:
        break;
    case DLL_THREAD_DETACH:
        break;
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

现在我有了.dll,我创建了一个简单的可执行文件来加载库并安装钩子:

int _tmain(int argc, _TCHAR* argv[])
{
    auto funHandle = LoadLibrary(L"C:\\Users\\tprodanov\\Documents\\visual studio 2010\\Projects\\HaveFun\\Release\\HaveFun.dll");
    if(funHandle == NULL){
        std::cout << "Library load failed! - " << GetLastError() << std::endl;
    }

    auto Install = (LRESULT(*)()) GetProcAddress(funHandle, "?Install@@YAJXZ");
    if(Install == NULL){
        std::cout << "Procedure load failed! - " << GetLastError() << std::endl;
    }
    Install();

    MSG Msg;

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    auto Uninstall = (BOOL(*)()) GetProcAddress(funHandle, "?UnInstall@@YGHXZ");
    if(Uninstall == NULL){
        std::cout << "Procedure load failed! - " << GetLastError() << std::endl;
    }
    Uninstall();
    return 0;
}

我觉得奇怪的是我的程序的行为在其当前状态下是预期的(并且如果不是消息循环,我只是弹出一个等待用户单击OK的MessageBox()),但它如果我使用std :: cin.get()或空while循环,则不起作用。有人可以解释为什么这是行为?

另外作为后续问题 - KeyboardCallback功能只是打印到控制台“Key Pressed”。

LRESULT CALLBACK EventAnalysis::KeystrokeAnalysis::KeyboardCallback(int nCode, WPARAM wParam, LPARAM lParam){
    std::cout << "Key pressed" << std::endl;
    return CallNextHookEx(NULL,nCode,wParam,lParam);
}

我预计只有在我专注于控制台窗口时按键才能打印,但即使我输入记事本,“Key Pressed”消息也会出现在我的可执行文件中,该消息称为.dll的Install函数。我不明白这一点,因为据我所知,动态库是在每个进程中独立加载的,所以每个进程都有自己的KeyboardCallback函数副本,它会尝试打印到前台窗口的控制台。

1 个答案:

答案 0 :(得分:2)

documentation清楚地说明了发生了什么:

  

在安装它的线程的上下文中调用此钩子。通过向安装了挂钩的线程发送消息来进行调用。因此,安装钩子的线程必须有一个消息循环。

通过对Install的调用安装挂钩。因此,在进行该调用的同一个线程中,您需要运行一个消息循环。

至于显示消息框影响事物的原因,消息框通过运行模态消息循环来操作。该消息循环将调度挂钩消息。

对于后续问题,documentation又有了答案:

  

每次新的键盘输入事件即将发布到线程输入队列时,系统都会调用此函数。

发布到线程输入队列时,它意味着任何线程输入队列

或者在SetWindowsHookEx文档的评论中查看各种钩子类型的列表及其范围。 WH_KEYBOARD_LL被列为全局钩子。

另请注意,WH_KEYBOARD_LL是一个低级别的钩子,因此不会导致DLL被注入另一个进程。实际上,您的代码过于复杂。你根本就不需要DLL。你可以从你的可执行文件调用SetWindowsHookEx来完成所有操作,该可执行文件在同一个可执行文件中传递一个回调函数。

这是一个演示所有这些的最小示例程序:

#include <Windows.h>
#include <iostream>

LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
    std::cout << "Key pressed" << std::endl;
    return CallNextHookEx(NULL, nCode, wParam, lParam);
}

int main(int argc, char* argv[])
{
    std::cout << "Installing" << std::endl;
    HHOOK hk;
    hk = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0);
    MSG Msg;
    while (GetMessage(&Msg, NULL, 0, 0))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    std::cout << "UnInstalling" << std::endl;
    UnhookWindowsHookEx(hk);
    return 0;
}