C ++ KeyBoard钩子

时间:2014-10-21 18:11:43

标签: c++ winapi

主题:将某些键替换为另一个键值。

例如如果我按P它应该是F24。

当我尝试从.ini文件加载键值时,钩子不再是全局的。只有当winapi形成焦点时它才有效。

我的DLL代码:

    extern "C" __declspec(dllexport) LRESULT CALLBACK KeyboardHook(int, WPARAM, LPARAM);
    extern "C" __declspec(dllexport) void loadSettings(LPSTR);
    bool shouldUpdateKey = false;

    int ArcherKey;

    LRESULT CALLBACK KeyboardHook(int code, WPARAM wParam, LPARAM lParam)
        {

            if ((lParam >> 20))
            {
                if (wParam == ArcherKey) 
                {
                shouldUpdateKey = shouldUpdateKey ? false : true;
                if (shouldUpdateKey) 
                {
                MessageBox(NULL, L"ArcherKey", L"", MB_OK);
                keybd_event(0x87, 45, 1, 0); //press F24
                return 1; 
                }
                }
            }


            return CallNextHookEx(NULL, code, wParam, lParam);
        }

      LPSTR GetValueFromINI(LPSTR FileName, LPSTR Section, LPSTR Key)
        {
            char *key;
            key = (char *)malloc(256);
            GetPrivateProfileStringA(Section, Key, NULL, key, 256, FileName);
            return key;
            free(key);
        }

      void loadSettings(LPSTR FileName) 
        {
            ArcherKey = atoi(GetValueFromINI(FileName, "HotKey", "Archer key"));
        }

我使用 shouldUpdateKey 来避免x2回调(当按下并按下键时)调用。此外,我尝试添加此声明 if(lParam>>  31)^ 1 ,但此语句始终为false。

.exe代码:

LRESULT(*pKeybHook)(int, WPARAM, LPARAM);
HHOOK hhookMsg;
void(*loadSettings)(LPSTR);

int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPTSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
/* default code */

HMODULE dll = LoadLibrary(_T("MainHookDLL.dll"));
    if (dll)
    {

        pKeybHook = (LRESULT(*)(int, WPARAM, LPARAM)) GetProcAddress(dll, "_KeyboardHook@12");

        loadSettings = (void(*)(LPSTR)) GetProcAddress(dll, "loadSettings");

        loadSettings("C:\\Settings.ini");

        hhookMsg = SetWindowsHookEx(WH_KEYBOARD, (HOOKPROC)(pKeybHook), dll, 0);

    }

/* defult code */
    UnhookWindowsHookEx(hhookMsg); // unhook
    FreeLibrary(dll);
    return (int) msg.wParam;
}       

Settings.ini结构:

[HotKey]
Archer key=80

所以我的问题: 如果尝试从文件加载设置,则挂钩仅在活动的winapi窗口中起作用。它显示MessageBox \ etc,但仅以活动的winapi形式显示。 如果将 wParam == ArcherKey 替换为 wParam == 80 ,则在所有应用中都可以全局运行。 我调试我的应用程序,从.ini文件加载后我有ArcherKey = 80.所以我真的无法理解我实际上有什么错误。

3 个答案:

答案 0 :(得分:1)

据我所知,如果钩子是全局的,那么包含HOOKPROC的DLL将被加载到所有其他进程中。这意味着您在内存中有多个DLL实例。由于从应用程序中调用loadSettings(..),因此仅为该进程初始化ArcherKey的值。这导致您正在观察的行为。

要更改此设置,您应该将DllMain(..)函数修改为以下内容:

BOOL WINAPI DllMain(HINSTANCE hinstDLL,  // handle to DLL module
                    DWORD fdwReason,     // reason for calling function
                    LPVOID lpReserved )  // reserved
{

   switch( fdwReason ) 
   { 
      case DLL_PROCESS_ATTACH:
         loadSettings("C:\\Settings.ini");
         break;

      case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
         break;

      case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
         break;

      case DLL_PROCESS_DETACH:
         // Perform any necessary cleanup.
         break;
   }

   return TRUE;
}

这会为安装挂钩的所有进程初始化ArcherKey的值,因为Windows在加载DLL时调用了DllMain。出于测试目的,您可以添加MessageBeep(0);在调用loadSettings(..)之前验证代码段是否已执行。

快速查看SetWindowsHookEx(..)的文档让我的恐惧成真:如果您正在编译32位DLL,您将能够挂钩64位进程,反之亦然。为了做到这一点,你必须使用不同名称的HOOKPROC实现64位版本的dll。

答案 1 :(得分:0)

挂钩被“注入”其他进程,这意味着您的整个DLL将被加载到所有相关进程中,就好像进程本身(例如Notepad.exe)已调用LoadLibrary()一样。因此,在该上下文中(在其他过程中,例如Notepad.exe),您的设置将不会被加载,因此ArcherKey将不会被初始化,因此不会出现消息框。

所以你必须让你的DLL进行初始化,而不是单独的.exe。您可以通过DLL_PROCESS_ATTACH上的DllMain初始化ArcherKey(加载您的设置)(尽管有关于哪些API在那时是安全的警告 - 大多数任何会导致其他DLL加载的调用都是禁止的),或者您可以大致添加代码la:

static DWORD initialized = 0;
static int ArcherKey;

LRESULT CALLBACK KeyboardHook(int code, WPARAM wParam, LPARAM lParam)
{
    if (!initialized)
    {
        loadSettings();
    }

    ...
}

虽然这个代码根本不可取,但是因为长时间运行的钩子至少是相当糟糕的形式并且可能导致问题(例如,停止该过程)。或者,您可以将数据放在已知的共享位置。编辑:关于在the accepted answer to a similar question中分享价值的方法,有一些很好的建议。

答案 2 :(得分:0)

LPSTR GetValueFromINI(LPSTR FileName, LPSTR Section, LPSTR Key)
{
    char *key;
    key = (char *)malloc(256);
    GetPrivateProfileStringA(Section, Key, NULL, key, 256, FileName);
    return key;
    free(key); // <- will never happen
}

ArcherKey = atoi(GetValueFromINI(...)); // <- does not clean up

内存泄漏