2016-12-20 2 views
1

Следующий код зарегистрирует крючок нижнего уровня для отслеживания событий мыши по всему миру.
Это самый простой рабочий пример, который я могу получить.
Собран с VC++ 2010: cl test.cpp /link /entry:mainCRTStartup /subsystem:windowsWinApi: Может ли цикл сообщения прерываться вызовом процедуры Async?

#include <windows.h> 

HWND label1 ; 

//THE HOOK PROCEDURE 
LRESULT CALLBACK mouseHookProc(int aCode, WPARAM wParam, LPARAM lParam){ 
    static int msgCount = 0 ; 
    static char str[20] ; 
    SetWindowText(label1, itoa(++msgCount, str, 10)) ; 
    return CallNextHookEx(NULL, aCode, wParam, lParam) ; 
} 

int main(){ 
    /**/// STANDARD WINDOW CREATION PART ////////////////////////////////////////////////////// 
    /**/   
    /**/ WNDCLASSEX classStruct = { sizeof(WNDCLASSEX), 0, DefWindowProc, 0, 0, GetModuleHandle(NULL), NULL, 
    /**/       LoadCursor(NULL, IDC_ARROW), HBRUSH(COLOR_BTNFACE+1), NULL, "winClass", NULL } ; 
    /**/ RegisterClassEx(&classStruct) ; 
    /**/ 
    /**/ HWND mainWin = CreateWindow("winClass", "", 0, 200,200, 100,100, NULL, NULL, NULL, NULL) ; 
    /**/ ShowWindow(mainWin, SW_SHOWDEFAULT) ; 
    /**/ 
    /**/ label1 = CreateWindow("static", "0", WS_CHILD, 5,5, 80,20, mainWin, NULL, NULL, NULL) ; 
    /**/ ShowWindow(label1, SW_SHOWNOACTIVATE) ; 
    /**/ 
    /**/// END OF WINDOW CREATION PART //////////////////////////////////////////////////////// 

    //HOOK INSTALATION 
    HHOOK hookProc = SetWindowsHookEx(WH_MOUSE_LL, mouseHookProc, GetModuleHandle(NULL), 0) ; 

    //MESSAGE LOOP 
    MSG msg ; 
    while(GetMessage(&msg, NULL, 0, 0)){ 
     TranslateMessage(&msg) ; 
     DispatchMessage(&msg) ; 
    } 

    UnhookWindowsHookEx(hookProc) ; 
} 

Это основной один поток, одно окно, один из примеров сообщение насоса. За исключением крючка для мыши.

Мои сомнения в том, что то, что этот код делает противоречит две вещи я прочитал снова и снова в SO, MSDN, форумы, блоги и т.д ..

  1. Глобальные процедуры крюков должны находиться в DLL
    MSDN documentation for SetWindowsHookEx подтверждает это, говоря:

    Если dwThreadId параметр равен нулю, параметр lpfn MUST указывают на подключаемую процедуру в DLL

  2. Невозможно прервать нить GUI (одна с сообщением), так как состояние ожидания GetMessase не является предупреждающим. Это означает, что когда GetMessage блокирует больше сообщений, он не может получить сигнал, который прерывает его состояние ожидания.

Однако, нет никакого DLL в любом месте, чтобы увидеть здесь, а также подключаемая процедура должна прерывать нить, иначе программа не будет работать, и это (я предполагаю, что есть только один поток в эта программа).

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

В любом случае, я не знаю, что здесь происходит.

Не могли бы вы объяснить, как работает этот код.
Это однопоточная программа?
Является ли процедура крюка прерыванием нитки?
Действительно ли какие-либо из двух точек выше?

ответ

4

процедура крюка должна быть в DLL только в том случае, если крючок необходимо вводить в другой процесс. для WH_MOUSE_LL

Однако WH_MOUSE_LL крюк не введен в другой процесс. Вместо этого контекст переключается обратно на процесс, в котором установлен крюк , и он вызывается в его исходном контексте. Затем контекст переключается обратно в приложение, которое сгенерировало событие.

поэтому здесь не требуется DLL (для чего?) И процедура крючка также может быть помещена в EXE.

Это однопоточная программа?

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

Является ли процедура крюка прерыванием нитки?

из MSDN

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

так может сказать, что mouseHookProc вызван внутри GetMessage звонок. эта функция ждет в ядре сообщений. когда система хочет обработать крючком, он делает это через вызов KiUserCallbackDispatcher. «прерывание потока» - что вы имеете в виду под прерыванием?

1.) Это не так, как показано в этом примере. hook должна быть в DLL, только если hook должен быть вызван в контексте потока, который получил сообщение, поэтому в произвольном контексте процесса - в этом случае нам нужно ввести код в другой процесс - потому что это и DLL. если мы всегда называли в контексте самостоятельного процесса - не инъекции, не нужно DLL

2.) GetMessage действительно ждать не в состоянии извещающие, поэтому любой APC не может быть доставлен, когда код, ждать в GetMessage, но APC здесь абсолютное не связаны. APC и Windows передают различные функции. конечно, существуют и аналогичные точки. для APC и некоторых сообщений о доставке Windows поток должен ждать в ядре. для APC в аварийном состоянии, для сообщений Windows в GetMessage или PeekMessage. для вызова системы доставки APC KiUserApcDispatcher, для сообщений Windows KiUserCallbackDispatcher. как это обратные вызовы из режима ядра, но она называется в различных условиях


прерывание именно это чувствует, когда выполнение кода может быть прервано в произвольном месте и прерывания начинают выполняться. в этом смысле прерывание вообще не существует в пользовательском режиме Windows. Сообщения APC или Windows (сообщения об ошибках - это особый случай сообщений Windows) никогда не прерывают выполнение в произвольном месте, но вместо этого используют механизм обратного вызова. исключения - особый случай. thread должен сначала вызвать некоторый api для входа в пространство ядра (для обмена сообщениями Windows это GetMessage или PeekMessage, для APC - ожидание в состоянии готовности или ZwTestAlert). а затем ядро ​​может использовать обратный вызов для пользовательского пространства для доставки сообщения APC или Windows. в этот момент только 3 обратные вызовы указывают существуют из ядра в пространство пользователя (это не отличается от Win2000 до win10) KiUserApcDispatcher - используется для APC доставки, KiUserCallbackDispatcher - используется для оконной процедуры вызова или процедуры крюка KiUserExceptionDispatcher - используется для инфраструктуры исключений - это наиболее близким к прерывают по смыслу,

+0

Теперь все имеет смысл. Вы просто забыли связать страницу MSDN со всей этой информацией: https://msdn.microsoft.com/en-us/library/windows/desktop/ms644986.aspx – GetFree

+0

Итак, я полагаю, что 'DispatchMessage' решает, следует ли вызвать WindowProcedure или HookProcedure в зависимости от типа сообщения. – GetFree

+1

@GetFree: Нет. Процедура hook вызывается, ** перед ** очередь сообщений целевой нити даже видит входное событие. К моменту, когда ваш код вызывает 'DispatchMessage', процедура hook уже давно вернулась. – IInspectable

0

поведение явно документирована под LowLevelMouseProc:

Этот хук вызывается в контексте потока, который был установлен. Вызов выполняется путем отправки сообщения в поток, который установил hook.

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

Для этого не нужно ловить крючок в DLL. Все это делается до событие ввода мыши помещается в очередь ввода целевого потока, поэтому не требуется прерывание функции поиска сообщений. Все выполняется в последовательности:

  • Событие ввода выбирается из очереди ввода оборудования.
  • Сообщение отправлено всем крючкам низкого уровня.
  • Если какой-либо из крючков вернул ненулевое значение, отбросьте входное событие.
  • В противном случае поместите его в очередь сообщений целевой очереди.
  • Целевая нить может принимать входное событие, используя любую из функций поиска сообщений (GetMessage, PeekMessage и т. Д.).


Несколько замечаний о том, как это реализовано в приложении крюка:

Применения крючка необходимо запустить цикл обработки сообщений, так как система посылает ему сообщения, чтобы информировать его о входных событиях, должны быть помещены в очередь ввода целевого потока по Сырье входного потока. Звонок GetMessage (в приложении-крючке) выступает в качестве диспетчера в случае сообщений об ошибке и вызывает процедуру перехвата перед возвратом. Хотя GetMessage является блокирующим вызовом, его можно разбудить (без предупреждения 1)). Вероятно, это ожидание на Event object, которое получает сигнал, когда сообщение доступно для извлечения. В качестве обоюдного отказа, оба звонка на TranslateMessage и DispatchMessage не требуются в вашем приложении для подключения.


1)Ссылка: The alertable wait is the non-GUI analog to pumping messages.

+0

Чтобы быть абсолютно ясным, это второй шаг * 'Сообщение отправляется всем крючкам низкого уровня *, где вызывается мой« mouseHookProc'? – GetFree

+0

@GetFree: Я неправильно понял часть вашего вопроса. Я расскажу об этом в обновлении. – IInspectable

Смежные вопросы