首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ KeyBoard钩

C++ KeyBoard钩
EN

Stack Overflow用户
提问于 2014-10-21 18:11:43
回答 3查看 1.4K关注 0票数 0

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

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

当我试图从.ini文件中加载键值时,钩子不再是全局的。它只有在对焦的情况下才能工作。

我的DLL代码:

代码语言:javascript
复制
    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,但是这个语句总是假的。

.exe代码:

代码语言:javascript
复制
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结构:

代码语言:javascript
复制
[HotKey]
Archer key=80

所以我的问题是:如果尝试从文件中加载设置,钩子只能在活动的winapi窗口中工作。它显示MessageBox\等等,但仅以活动的winapi形式显示。如果将wParam == ArcherKey改为wParam == 80,则在所有应用程序中,它都能在全球范围内工作。我调试我的应用程序,在从.ini文件加载之后,我的ArcherKey = 80。所以我真的不明白我到底犯了什么错。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2014-10-21 18:54:29

据我回忆,如果钩子是全局的,包含HOOKPROC的DLL将在所有其他进程中加载。这意味着内存中有多个DLL实例。因为您调用了loadSettings(.)在您的应用程序中,只为该进程初始化ArcherKey的值。这会导致你所观察到的行为。

要改变这一点,您应该修改DllMain(..)函数的作用如下:

代码语言:javascript
复制
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值,因为在加载DLL时,DllMain调用了DllMain。出于测试目的,您可以添加MessageBeep(0);在调用loadSettings(.)之前以验证代码部分是否已执行。

快速查看一下SetWindowsHookEx(.)的文档,我的担忧就成了现实:如果您正在编译一个32位的DLL,那么而不是将能够钩住64位进程,反之亦然。为了做到这一点,您必须用不同的名称为HOOKPROC实现dll的64位版本。

票数 1
EN

Stack Overflow用户

发布于 2014-10-21 18:38:32

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

因此,您必须让您的DLL进行初始化,而不是单独的.exe。您可以通过ArcherKey on DLL_PROCESS_ATTACH上的DllMain初始化DLL_PROCESS_ATTACH(加载设置)(尽管在这一点上需要注意哪些API是安全的--大多数情况下,导致加载其他DLL的调用都是no-no),或者您可以添加大致为la的代码:

代码语言:javascript
复制
static DWORD initialized = 0;
static int ArcherKey;

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

    ...
}

尽管这段代码根本不可取,因为长时间运行的钩子是最小的,相当糟糕的形式,并可能导致问题(例如,拖延该进程)。或者,您可以将数据放置在已知的共享位置。编辑:对于如何在对类似问题的公认答案中共享值,有一些很好的建议。

票数 0
EN

Stack Overflow用户

发布于 2014-10-22 07:48:39

代码语言:javascript
复制
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

内存泄漏

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/26493394

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档