【问题标题】:Globally installed keyboard hook prevents keyboard input to other applications全局安装的键盘挂钩可防止键盘输入到其他应用程序
【发布时间】:2018-05-17 23:46:52
【问题描述】:

我正在为键盘设置一个全局挂钩。当我向其他应用程序提供键盘输入时,应用程序没有收到输入并且挂起。当控制台停止时,应用程序恢复并且键盘输入一起发布。

DLL 源代码

#include <iostream>
#include <Windows.h>
#include <string>
using namespace std;
#define DLLEXPORT __declspec(dllexport)

DLLEXPORT bool installhook();
DLLEXPORT void unhook();
DLLEXPORT string TestLoaded();
DLLEXPORT LRESULT CALLBACK KeyboardProc ( int code, WPARAM wParam, LPARAM lParam );

static HHOOK kb_hook;
string test = "not loaded";
HINSTANCE hDLL;

DLLEXPORT LRESULT CALLBACK KeyboardProc ( int code, WPARAM wParam, LPARAM lParam )
{
    if(code == HC_ACTION)   // if there is an incoming action and a key was pressed
    {
        switch(wParam)
        {
        case VK_SPACE:
            printf("Space was pressed\n"); //tried without this also
            MessageBoxA(NULL, "Hi", "Space", MB_OK);
            break;
        }
    }
    return CallNextHookEx(NULL, code, wParam, lParam);
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
    test = "loaded";
    switch(ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
        hDLL = hModule;
        break;
    }
    printf("test str = %s \n", test.c_str());
    return TRUE;
}

bool installhook()
{
    kb_hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hDLL, NULL);
    if(!kb_hook)
    {
        return false;
    }
    return true;
}

void unhook()
{
    if(kb_hook)
    {
        UnhookWindowsHookEx(kb_hook);
    }
}

string TestLoaded()
{
    return test;
}

控制台应用程序来源:

#include <iostream>
#include <Windows.h>
#include <string>
#define DLLIMPORT __declspec(dllimport)
using namespace std;

DLLIMPORT void unhook();
DLLIMPORT bool installhook();
DLLIMPORT string TestLoaded();

int main()
{
    cout << TestLoaded() <<endl;
    installhook();
    for(int i = 1; i<=10 ; i++)
    {
        //Do some keyboard activities in this 10 secs
        Sleep(1000);
        cout << i<<endl;
    }

    unhook();
    cin.get();
    return 1;
}

我的怀疑是,由于 dll 将被加载到进程自己的地址空间中的每个进程中,并且控制台不会出现在其他应用程序中,因此它会变得无效并崩溃。所以我删除了控制台输出并替换为消息框。那么也没什么区别。

可能是什么问题?

更新:

我尝试在全局尝试之前对特定线程进行本地挂钩。但我在 setwindowshookex 收到 Parameter is incorrect 错误 87。以下是更新后的代码:

dll:

bool installhook(DWORD ThreadId) //exporting this function
{
    kb_hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, NULL, ThreadId); //tried with the dll module's handle also instead of NULL
    if(!kb_hook)
    {
        printf("SetWindowsHookEx failed : %d\n", GetLastError());
        return false;
    }
    return true;
}

控制台应用程序来源:

DWORD myThread()
{
    cout<< "Thread started\n";
    char str[250];
    cin>>str;
    return 0;
}

int main()
{
    cout << TestLoaded() <<endl;
    DWORD myThreadID;
    HANDLE myHandle = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)myThread, NULL, 0, &myThreadID);
    installhook(myThreadID);
    for(int i = 0; i<100 ; i++)
    {
        Sleep(100);
        if(i%10 == 0)
        {
            cout << i<<endl;
        }
    }
    unhook();
}

【问题讨论】:

  • 不要像 MessageBox 那样做任何阻塞回调或导致递归的事情。您通常只是看不到 printf() 输出,使用 /MT 构建 DLL 也很重要,因此它可以获得自己的 CRT 副本并且不会意外使用注入程序使用的 CRT。您可以安全地写入日志文件。首先通过将注入限制在您自己的一个程序中进行测试。请注意,除非您创建两个 DLL 和注入器并拥有足够的权限,否则无法在 64 位操作系统上注入所有这些。
  • @HansPassant 我从 dll 中删除了所有控制台写入和消息框并更改为 /MT,仍然阻止其他应用程序接收键盘输入。我的是 64 位操作系统,DLL 是 32 位的。我将尝试将其专门注入到 32 位进程的单个线程中。
  • @HansPassant 请检查更新。尝试挂钩到单个线程时,Setwindowshookex 失败。

标签: c++ windows hook setwindowshookex


【解决方案1】:

尝试使用 WH_KEYBOARD_LL。即使没有 dll 在您的进程中声明钩子函数,您也可以设置全局钩子。另外,您应该使用 PKBDLLHOOKSTRUCT 结构检测空间操作

LRESULT CALLBACK KeyboardProc ( int code, WPARAM wParam, LPARAM lParam )
{
   if ( code == HC_ACTION )  
   {
       switch ( wParam )
       {
          case WM_KEYDOWN:
          {
              // Get hook struct
              PKBDLLHOOKSTRUCT p = ( PKBDLLHOOKSTRUCT ) lParam;
              if ( p->vkCode == VK_SPACE)
              {
                MessageBoxA( NULL, "Hi", "Space", MB_OK );
              }
          }
       break;
       }
   }
   return CallNextHookEx( NULL, code, wParam, lParam );
 }

 ....
 // Somewhere in code
 kb_hook = SetWindowsHookEx( WH_KEYBOARD_LL, KeyboardProc, NULL, NULL );

【讨论】:

  • 试过了,但是钩子进程没有自己命中。
【解决方案2】:

感谢答案和 cmets 中的所有输入。

我发现了实际问题。我犯的错误是尝试使用没有任何消息队列的控制台窗口。

如果我理解正确,控制台窗口由 ​​conhost.exe 托管,并且它们没有任何消息泵。只有当安装它的应用程序有一个消息队列时,这个钩子才能正常工作(应该更多地探索为什么会这样)。请参阅下文了解如何使其发挥作用

如果您没有向控制台应用程序发布任何消息:

将控制台应用程序主中的 for 循环替换为:

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

如果您向控制台应用程序发布任何消息:

使用CreateWindowEx 创建一个窗口,还有一个仅消息窗口的选项。您必须创建一个类并分配一个 CALLBACK 进程。阅读here 了解更多详情。创建它并将句柄传递给钩子 dll 并将消息传递给句柄。使用循环获取味精并分派它(如上所述)。然后,您从钩子 dll 发布虚拟窗口的所有消息都可以使用 CALLBACK 窗口进程进行处理。

参考资料:

Why must SetWindowsHookEx be used with a windows message queue

CreateWindowEx MSDN

【讨论】:

    【解决方案3】:

    我遇到了同样的问题,在使用 QT 时,GUI 会被阻止(按计划),但只要它重新上线,它就会处理我的键盘和鼠标点击。

    我不确定这是否是最有效的处理方式,但为了解决这个问题,我分别处理了所有键盘和鼠标事件。如果某项任务正在进行中,我会忽略关键事件。

    否则我猜它只是排队等待轮到它!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多