C ++ KeyBoard hook

0

Subject: заменить некоторые ключи на другое значение ключа.

Например, если я нажимаю 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 (когда нажата клавиша нажата и нажата). Также я пытаюсь добавить это утверждение, если (lParam >> 31) ^ 1, но это утверждение всегда ложно.

.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

Поэтому моя проблема: если вы пытаетесь загрузить настройки из файла, hook работает только в активном окне winapi. Он показывает MessageBox\etc, но только в активной форме winapi. Если заменить wParam == ArcherKey на wParam == 80, он работает глобально во всех приложениях. Я отлаживаю свое приложение, после загрузки из файла.ini у меня есть ArcherKey = 80. Так что я действительно не могу понять, какая у меня ошибка.

Теги:
winapi

3 ответа

1
Лучший ответ

Насколько я помню, DLL, содержащая HOOKPROC, загружается во все остальные процессы, если крючок является глобальным. Это означает, что у вас есть несколько экземпляров вашей 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 вызывает DllMain при загрузке DLL. В целях тестирования вы можете добавить MessageBeep (0); перед вызовом loadSettings (..), чтобы убедиться, что раздел кода выполнен.

Быстро взглянув на документацию SetWindowsHookEx (..), мои страхи сбылись: если вы собираете 32-битную DLL, вы не сможете перехватывать 64-битные процессы и наоборот. Для этого вам нужно реализовать 64-битную версию dll с иначе названным HOOKPROC.

  • 0
    О, я должен прочитать больше документации -.- Я просто понимаю это, когда добавляю MessageBox в функцию ввода DLL, каждый раз, когда я выполняю действия клавиатуры в новом окне, dll создает новый экземпляр (если это правильное значение). Спасибо, теперь я знаю, что мне делать.
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

Утечка памяти

0

Крючки "вводятся" в другие процессы, что означает, что вся ваша DLL будет загружена во все соответствующие процессы, как если бы сам процесс (например, Notepad.exe) вызвал LoadLibrary(). Таким образом, в этом контексте (в другом процессе, например Notepad.exe) ваши настройки не будут загружены, поэтому ArcherKey не будет инициализирован, поэтому окно сообщения не появится.

Таким образом, вы должны позволить вашей DLL инициализации, а не отдельному.exe. Вы можете либо инициализировать ArcherKey (загрузить свои настройки) через DllMain в DLL_PROCESS_ATTACH (хотя есть предостережения относительно того, какие API-интерфейсы являются безопасными в этот момент - в основном, любые вызовы, которые могли бы загружать другие DLL файлы, - нет-нет), или вы может добавить код примерно a la:

static DWORD initialized = 0;
static int ArcherKey;

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

    ...
}

Хотя этот код не рекомендуется вообще, поскольку длинные крючки работают как минимум в довольно плохом виде и могут вызывать проблемы (например, остановка этого процесса). Кроме того, вы можете разместить данные в известном общем месте. Изменение: есть несколько хороших предложений относительно способов обмена ценностями в принятом ответе на аналогичный вопрос.

  • 0
    Спасибо, я попробую это.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню