Я пытаюсь создать простое приложение для записи и воспроизведения серии команд клавиатуры и мыши (макросы).Прочитайте документацию и пришли к выводу, что наиболее подходящей реализацией (если не единственной) было бы установить хук записи журнала Windows (WH_JOURNALRECORD
) и воспроизвести его с воспроизведением журнала (WH_JOURNAL_PLAYBACK
).
Согласно документации, эти хуки не обязательно должны находиться в DLL, вместо этого они могут находиться в исполняемом файле (приложении).Итак, Visual Studio создала для меня простое приложение Win32.Это очень классическое приложение, регистрирующее класс окна, создающее окно и запускающее цикл обработки сообщений.В документации также упоминается, что процедуры ловушек для WH_JOURNALRECORD
/ WH_JOURNAL_PLAYBACK
ловушек выполняются в контексте потока, который их устанавливает.Тем не менее, в нем конкретно не упоминается, что должен делать этот поток, например, запустить цикл обработки сообщений, спящий режим в состоянии оповещения или что.Поэтому я просто установил ловушку и запустил цикл сообщений - это основной и единственный поток приложения.Это то, что некоторые примеры кода, которые я нашел, также делают, хотя они, кажется, не работают на данный момент, поскольку они довольно старые, и некоторые обновления безопасности Windows сделали вещи намного более сложными.
Я полагаю, что у меня естьпредпринял все необходимые шаги, которые я нашел в некоторых примерах и сообщениях:
- Установите для параметров манифеста " Уровень выполнения UAC " значение " requireAdministrator (/ level = 'requireAdministrator"')"и" Защита UAC Bypass UI Protection"до" Да (/uiAccess='true')".
- Создан и установлен сертификат -приложение подписано с ним после сборки.
- Исполняемый файл копируется в System32 (доверенная папка) и запускается оттуда «как администратор».Без вышеуказанных действий установка ловушки завершится неудачно с кодом ошибки 5 (доступ запрещен).
Мне удалось успешно (?) Установить крючок WH_JOURNALRECORD
(SetWindowsHookEx()
возвращает ненулевой дескриптор), однако процедура ловушки не вызывается.
Ниже приведен мой код (я пропустил регистрацию класса окна, создание окна, процедуру окна и материал диалога, так как ничего интересного нетили что-то особенное там - они делают только скелеты):
// Not sure if these are needed, found it in some code samples
#pragma comment(linker, "/SECTION:.SHARED,RWS")
#pragma data_seg(".SHARED")
HHOOK hhJournal = NULL;
#pragma data_seg()
// Not sure if the Journal proc needs to be exported
__declspec(dllexport) LRESULT CALLBACK _JournalRProc(_In_ int code, _In_ WPARAM wParam, _In_ LPARAM lParam)
{
Beep(1000, 30); // Clumsy way to trace the JournalRProc calls
return CallNextHookEx(NULL, code, wParam, lParam);
}
void AddKMHooks(HMODULE _hMod)
{
if (hhJournal) return;
MessageBox(NULL, "Adding Hooks", szTitle, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
hhJournal = SetWindowsHookEx(WH_JOURNALRECORD, _JournalRProc, _hMod, 0);
if (!hhJournal)
{
CHAR s[100];
wsprintf(s, "Record Journal Hook Failed!\nThe Error-Code was %d", GetLastError());
MessageBox(NULL, s, szTitle, MB_OK | MB_ICONSTOP | MB_TASKMODAL);
}
}
void RemoveKMHooks()
{
if (!hhJournal) return;
MessageBox(NULL, "Removing Hooks", szTitle, MB_OK | MB_ICONINFORMATION | MB_TASKMODAL);
UnhookWindowsHookEx(hhJournal);
hhJournal = NULL;
}
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: Place code here.
// Initialize global strings
LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadString(hInstance, IDC_KMRECORD, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// Perform application initialization:
if (!InitInstance(hInstance, nCmdShow)) return FALSE;
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_KMRECORD));
AddKMHooks(hInstance);
// Calling AddKMHooks(GetModuleHandle(NULL)) instead, delivers the same results
MSG msg;
// Main message loop:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
// Once the hook has is set the GetMessage() call above
// always returns a WM_TIMER message with a timer ID of 1,
// posted to the queue with the PostMessage() function,
// as the Spy++ tool reports
RemoveKMHooks();
return (int) msg.wParam;
}
Я контролировал приложение с помощью инструмента Spy ++ и обнаружил, что, когда хук установлен, приложение получает серию последовательных таймеров WM_TIMER
сообщения с идентификатором таймера 1 отправляются в очередь с помощью функции PostMessage()
(P в Spy ++).Сообщается, что дескриптор окна для этих сообщений принадлежит тому же приложению и потоку, что и основное окно, а его имя класса - «UserAdapterWindowClass».Мой код не создает таймеры и явно не создает никаких окон этого класса, поэтому, очевидно, они создаются системой.Кроме того, есть еще одно сообщение с идентификатором 0x0060 (Неизвестно!), Размещенное в том же окне только один раз, после 1-го или 2-го WM_TIMER
.
Приложение, похоже, каким-то образом «блокирует»система (запись, ожидание ресурса или что?), пока я не нажму Alt + Ctrl + Del, что, как сказано в документации, останавливает запись (я еще не реализовал какой-либо механизм для остановки записи / удаления ловушки по желанию, так что эточем я пока пользуюсь).Процедура ловушки, кажется, никогда не вызывается, и это проблема, с которой я сталкиваюсь в данный момент.Я рассмотрел проверку параметра code
в процедуре подключения и действую соответствующим образом, но я даже не близок к этому, поскольку процедура никогда не вызывается - и, следовательно, она не будет иметь никакого эффекта - поэтому я просто вызываю CallNextHookEx()
втам (и предположим, что это будет работать хорошо).
Примечания:
- Процедура перехвата может иногда вызываться только один раз, но это довольно редко (почти не- воспроизводимо) и определенно не соответствует, поэтому я не могу на это полагаться;параметр
code
равен 0 (HC_ACTION
).Протестировал это, хотя и возвращая либо ноль, либо ненулевое значение, либо вызывая CallNextHookEx()
, либо нет, вообще не имеет значения. - Я также пытался установить ловушку в другом потоке (созданном после того, как основной начал обрабатывать сообщения), который затем также запускает цикл обработки сообщений, но я получаю точно такое же поведение.
Может кто-нибудь объяснить, что здесь может быть не так?Также проверил некоторые другие посты, особенно эти: Сбой SetWindowsHookEx для WH_JOURNALRECORD в Vista / Windows 7 и WH_JOURNALRECORD в Windows (C ++) - Обратный вызов никогда не вызывался., однако не смог найти решение, и я должен отметить, что условия использования тоже отличаются.То, что я испытываю, ближе всего к этому SetWindowsHookEx (WH_JOURNALRECORD, ..) иногда зависает система , хотя в моем случае это происходит всегда, а не просто "иногда".
Я был бы очень признателенлюбая помощь.
Я загрузил решение (исходные файлы + файлы VS, но не файлы .exe, .obj., .pch, .pdb и т. д., поэтому требуется перестройка) здесь , если кто-то хотел бы взглянуть.
Заранее спасибо