Почему все работает нормально, когда я щелкаю где-то вне формы приложения, но зависает, когда я нажимаю внутри формы?
Вы не отправляете сообщение в другие окна, когда нажимаетена них.Сначала вы должны спросить себя: «Что произойдет, если я отправлю сообщение в своем обратном вызове хука всем окнам, которые публикуют WM_LBUTTONUP
?».
Замените эту строку
PostMessage(FindWindow('TShowMouseClick', nil), MouseHookMessage, MsgID, 0);
вВаш код DLL, с этим:
PostMessage(PMouseHookStruct(Data).hwnd, MouseHookMessage, MsgID, 0);
Не имеет значения, будут ли другие приложения знать или нет, что такое MouseHookMessage, они будут игнорировать сообщение.Запустите ваше приложение и дико щелкните мышью на других окнах.Вообще ничего не случится.Если вы не нажмете в клиентской области любое приложение Delphi.Вы мгновенно заморозите его.
Ответ на этот вопрос заключается как в том, как работает цикл сообщений VCL, так и в том, как работает ловушка WH_MOUSE
.Цитата из MouseProc
документации функции обратного вызова .
[..] Система вызывает эту функцию всякий раз, когда приложение вызывает GetMessage или PeekMessage функция и есть сообщение мыши для обработки.
Предположим, что вы запускаете ваше приложение и мышь подключается, затем вы наводите мышь на форму и ждете, пока ваше приложение вызовет 'WaitMessage ', что он простаивает.Теперь нажмите в клиентской области, чтобы генерировать сообщения мыши.Что происходит, так это то, что ОС помещает сообщения в очередь сообщений основного потока вашего приложения.И что ваше приложение делает для удаления и отправки этих сообщений с PeekMessage
.Это где приложения отличаются.Сначала VCL вызывает PeekMessage с параметром PM_NOREMOVE, переданным в параметре wRemoveMsg, в то время как большинство других приложений либо удаляют сообщение с вызовом PeekMessage, либо делают то же самое, используя GetMessage.
сейчасПредположим, настала очередь 'WM_LBUTTONUP.Обратитесь к цитате выше.Как только вызывается PeekMessage
, ОС вызывает обратный вызов MouseProc
.Вызов происходит из 'user32.dll', то есть, когда ваш обратный вызов ловушки вызывается, оператор, следующий за 'PeekMessage', еще не выполнен.Также запомните цикл VCL, сообщение все еще находится в очереди, оно не было удалено.Теперь ваша функция обратного вызова отправляет сообщение в ту же очередь сообщений и возвращает.Выполнение возвращается к циклу сообщений VCL, и VCL снова вызывает PeekMessage, на этот раз для удаления и отправки сообщения, но вместо удаления WM_LBUTTONUP удаляет отправленное вами пользовательское сообщение.WM_LBUTTONUP остается в очереди.После отправки пользовательского сообщения, поскольку «WM_LBUTTONUP» все еще находится в очереди, снова вызывается «PeekMessage», и снова ОС вызывает обратный вызов, чтобы обратный вызов мог опубликовать другое пользовательское сообщение, которое будет удалено вместо сообщения мыши.Этот цикл эффективно останавливает приложение.
Чтобы решить эту проблему, либо отправьте ваше сообщение в другой поток, который имеет свой собственный цикл сообщений, который каким-то образом синхронизируется с основным потоком, либо я бы не стал особенно советовать, но вместо публикации сообщения send Это.В качестве альтернативы вы можете самостоятельно удалить сообщение «WM_LBUTTONUP» из очереди, если оно существует:
procedure TShowMouseClick.WndProc(var Message: TMessage);
begin
inherited WndProc(Message);
if (Message.Msg = HookCommon.MouseHookMessage) and
(Message.WParam = $202) then begin
if PeekMessage(Msg, Handle, WM_LBUTTONUP, WM_LBUTTONUP, PM_REMOVE) then
DispatchMessage(Msg); // or eat if you don't need it.
..
end;
Недостатком этого подхода является то, что сам PeekMessage, как упоминалось выше, будет вызывать другое пользовательское сообщениебудут размещены, так что вы будете получать их в парах.