GetMessageW блокирует вызывающий поток, не получая никаких сообщений

#c #windows #winapi #rust

#c #Windows #winapi #Ржавчина

Вопрос:

Я уже целый день борюсь с этим и не могу понять, что не так с моим кодом. Я пишу на Rust, но это скорее проблема, связанная с API Windows.

 // first, I'm installing a keyboard hook for the current thread
let hook = SetWindowsHookExW(
    WH_KEYBOARD_LL,
    Some(low_level_keyboard_proc), // just forwards the call with CallNextHookEx
    ptr::null_mut(),
    0,
);

assert!(!hook.is_null(), "Failed to install the hook");

let mut message: MSG = mem::zeroed();
GetMessageW(amp;mut message, ptr::null_mut(), 0, 0);

// The GetMessageW function is known to block the calling thread until a new message is sent.
// The thing is: my `low_level_keyboard_proc` handle *does get* called, so I know events are being received.
// I don't understand why the GetMessageW function never returns even though events are being processed.
// Note that my handler does not get called when I remove the GetMessageW function.

println!("Unreachable code...");

UnhookWindowsHook(hook);
 

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

Если я удаляю SetWindowsHookExW деталь, GetMessageW она по-прежнему блокирует поток, НО если я удаляю GetMessageW деталь и помещаю бесконечный цикл на ее место, обработчик больше не вызывается.

… итак, вот вопрос: почему GetMessageW функция никогда не возвращается? И если такое поведение является нормальным, то как я должен использовать сообщение, которое я предоставляю GetMessageW .

Я предполагаю, что я не очень хорошо понимаю взаимосвязь между GetMessageW и SetWindowsHookExW .

РЕДАКТИРОВАТЬ: я понимаю, что не могу перехватывать сообщения, отправленные на созданный мной хук клавиатуры. Теперь, как будет выглядеть «правильный» способ получения сообщений с клавиатуры? Потому что было бы очень удобно иметь возможность получать эти сообщения непосредственно из цикла сообщений вместо того, чтобы отправлять их обратно из функции обратного вызова в мой основной код с использованием статических структур.

Я пытаюсь создать цикл событий, который можно использовать независимо от контекста или фокуса окна. Идея заключается в том, чтобы извлекать эти сообщения непосредственно из цикла сообщений и отправлять их с помощью пользовательского пользовательского обработчика, который можно использовать с помощью кода safe rust.

Ответ №1:

В очереди сообщений потока, который устанавливает перехват клавиатуры, нет оконных сообщений или сообщений потока, отправляемых в очередь сообщений потока, который устанавливает перехват клавиатуры, поэтому нет никаких сообщений для GetMessageW() возврата К ВАМ.

Однако SetWindowsHookEx() использует свои собственные сообщения внутри, когда низкоуровневый хук клавиатуры пересекает границы потока / процесса. Вот почему вам не нужно реализовывать свой хук в DLL при подключении других приложений. Когда происходит действие с клавиатуры, в СИСТЕМУ отправляется личное сообщение, предназначенное для потока, который установил перехват.

Вот почему установочному потоку нужен цикл сообщений. Простого выполнения поиска сообщений в вашем коде достаточно, чтобы эти внутренние сообщения были отправлены должным образом, поэтому вызывается ваша функция обратного вызова. Вы просто не увидите эти личные сообщения, поэтому GetMessageW() блокирует ваш код.

То же самое происходит, когда вы SendMessage() переходите к окну через границы потока. Принимающему потоку необходим цикл сообщений, чтобы сообщение было отправлено в целевое окно, даже если сообщение не проходит через очередь сообщений принимающего потока. Это описано в SendMessage() документации:

Если указанное окно было создано вызывающим потоком, оконная процедура вызывается немедленно как подпрограмма. Если указанное окно было создано другим потоком, система переключается на этот поток и вызывает соответствующую оконную процедуру. Сообщения, отправляемые между потоками, обрабатываются только тогда, когда принимающий поток выполняет код извлечения сообщения.

Итак, что происходит с SetWindowsHookEx() тем, что он создает скрытое окно для себя, чтобы получать свои личные сообщения, отправляемые через SendMessage() , когда активность клавиатуры обнаруживается в другом потоке / процессе и должна быть перенаправлена обратно в ваш установочный поток. Это описано в LowLevelKeyboardProc документации:

Этот перехват вызывается в контексте потока, который его установил. Вызов выполняется путем отправки сообщения потоку, который установил перехват.Следовательно, поток, который установил перехват, должен иметь цикл сообщений.

Комментарии:

1. На самом деле это очень интересно. Значит ли это, что я должен создать свое собственное скрытое окно (например, с CreateWindowEx) для получения сообщений? Или есть способ перехватить эти сообщения, отправленные в систему?

2. @Gymore создание собственного окна не даст вам доступа к сообщениям, отправленным в другое окно. WH_CALLWNDPROC Перехват может видеть сообщения (я не пробовал). Но они являются частными для реализации перехвата, почему вы вообще пытаетесь получить к ним доступ? Они вам не нужны, вы не можете сделать с ними ничего значимого. Так что просто оставьте их в покое и двигайтесь дальше.

3. Я оговорился. Я хотел сказать, что я хотел использовать сообщения, отправляемые в мое собственное окно, то, которое я бы создал. Все идеи в сторону, реальный вопрос: «Как мне заставить Windows отправлять мне сообщения? Возможно, использование перехвата клавиатуры изначально не было хорошей идеей. Я редактирую свой первоначальный вопрос, чтобы сделать его более точным.

4. @Gymore если вы создадите свое собственное окно, то вам придется назначить ему свой собственный WndProc для получения сообщений. Если вы просто ищете ОС для отправки ей каких-либо сообщений, вы могли бы использовать SetTimer() , например. Но если вы специально ищете сообщения с клавиатуры, то есть WM_KEY(DOWN|UP) сообщения, когда ваше окно имеет фокус ввода. Но для захвата событий клавиатуры, когда у вас нет фокуса, вы должны использовать SetWindowsHookEx() или RegisterRawInputDevices() получать WM_INPUT сообщения. На самом деле непонятно, чего именно вы пытаетесь достичь. Какова ваша фактическая цель?

5. Хо, ты прав, теперь, когда я снова читаю свой пост, я вижу, что мои намерения не так ясны. Я снова редактирую сообщение x) Я пытаюсь создать минимальную реализацию цикла событий (как для событий клавиатуры, так и для событий мыши), которая работает независимо от фокуса окна или контекста.