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. Так что я действительно не могу понять, какая у меня ошибка.
Насколько я помню, 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.
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
Утечка памяти
Крючки "вводятся" в другие процессы, что означает, что вся ваша 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();
}
...
}
Хотя этот код не рекомендуется вообще, поскольку длинные крючки работают как минимум в довольно плохом виде и могут вызывать проблемы (например, остановка этого процесса). Кроме того, вы можете разместить данные в известном общем месте. Изменение: есть несколько хороших предложений относительно способов обмена ценностями в принятом ответе на аналогичный вопрос.