Использование WH_JOURNALRECORD и отмены действительно возвращает WM_CANCELJOURNAL - PullRequest
1 голос
/ 16 февраля 2012

Я использую C # и у меня есть программа, успешно записывающая сообщения журнала, используя SetWindowsHookEx с WH_JOURNALRECORD.

Моя проблема возникает, когда приходит время остановиться. Документы показывают, что если пользователь нажал CTRL-ESC или CTRL-ALT-DELETE, будет опубликовано сообщение WM_CANCELJOURNAL, которое я могу посмотреть, чтобы узнать, когда остановиться. Мое приложение отсоединено, но я никогда не получаю WM_CANCELJOURNAL.

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

IntPtr hinstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);

JournalRecordProcedure = JournalRecordProc;
journalHook = SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProcedure, hinstance, 0);

GetMessageProcedure = GetMessageProc;
messageHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProcedure, hinstance, 0); 


------

public static int JournalRecordProc(int nCode, IntPtr wParam, IntPtr lParam)
{
    if (nCode < 0) return CallNextHookEx(journalHook, nCode, wParam, lParam);

    EventMsgStruct msg = (EventMsgStruct) Marshal.PtrToStructure(lParam, typeof (EventMsgStruct));

    script.Add(msg); //just a quick way to record for now

    return CallNextHookEx(journalHook, nCode, wParam, lParam);
}

public static int GetMessageProc(int code, IntPtr wParam, IntPtr lParam)
{

    //it comes here but how do I test if it's WM_CANCELJOURNAL ??
    //code always seems to be equal to zero.. I must be missing something


    return CallNextHookEx(journalHook, code, wParam, lParam);
}

1 Ответ

1 голос
/ 16 февраля 2012

Полагаю, вы ссылаетесь на этот раздел в документации :

Эта роль в качестве сигнала для остановки записи в журнале означает, что комбинация клавиш CTRL + BREAK сама по себе не можетбыть записанным.Поскольку комбинация клавиш CTRL + C не играет такой роли, как сигнал журналирования, ее можно записать.Есть две другие комбинации клавиш, которые не могут быть записаны: CTRL + ESC и CTRL + ALT + DEL.Эти две комбинации клавиш приводят к тому, что система прекращает все действия по ведению журнала (запись или воспроизведение), удаляет все перехватчики ведения журнала и отправляет сообщение WM_CANCELJOURNAL в приложение ведения журнала.

Проблема заключается в том, что WM_CANCELJOURNAL сообщение - это , а не , отправленное функции обратного вызова, установленной вами с SetWindowsHookEx.Но в отличие от других сообщений WM_*, оно также не предназначено для обработки оконной процедурой (WndProc в WinForms), поскольку оно отправляется в очередь сообщений потока и не связано ни с какимиконкретное окно.

Скорее всего, документация рекомендует обрабатывать его в основном цикле приложения или с помощью WH_GETMESSAGE hook :

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

[.,,]

Сообщение WM_CANCELJOURNAL имеет дескриптор окна NULL, поэтому его нельзя отправить в оконную процедуру.У приложения есть два способа увидеть сообщение WM_CANCELJOURNAL: если приложение выполняется в своем главном цикле, оно должно перехватить сообщение между вызовом GetMessage или PeekMessage и вызовом DispatchMessage.Если приложение не выполняется в своем основном цикле, оно должно установить подключаемую процедуру GetMsgProc (посредством вызова SetWindowsHookEx, указывающего тип подключения WH_GETMESSAGE), который отслеживает сообщение.

В управляемом коде WinForms вы, очевидно, не имеете доступа к главному циклу приложения или не можете его контролировать.Я не уверен, если добавление фильтра сообщений в ваше приложение позволит вам обработать это сообщение или нет: я не пробовал его.Если это произойдет, это, вероятно, тот маршрут, который вы хотите выбрать, учитывая альтернативный вариант: установить второй хук, WH_GETMESSAGE, а затем в этой процедуре хука прослушать сообщение WM_CANCELJOURNAL.


Обновление:

В функции обратного вызова GetMessageProc параметр code просто указывает, должна ли процедура подключенияобработать сообщениеПрактически все время это будет 0, что эквивалентно символической константе HC_ACTION.Если параметр code меньше 0, подключаемая процедура должна просто вызвать функцию CallNextHookEx без какой-либо дальнейшей обработки.По сути, это то же самое, что вы сделали для функции обратного вызова JournalRecordProc.

Сообщение окна будет найдено в MSG структуре , указатель на которую передаетсяфункция обратного вызова в качестве параметра lParam.Но это вещи Win32.Не связывайтесь с необработанными указателями в .NET, пусть маршалер P / Invoke обрабатывает все эти грязные вещи для вас.Собственная структура MSG эквивалентна управляемой System.Windows.Forms.Message структуре (то же самое, что используется методом WndProc), поэтому, если вы объявите свою функцию обратного вызова GetMessageProc следующим образом, все будетнамного проще:

public delegate int GetMessageProc(int code, IntPtr wParam, ref Message lParam);

Затем сообщение Windows будет найдено как Msg член структуры Message.Это значение, которое вы хотите сравнить с WM_CANCELJOURNAL:

public static int GetMessageProc(int code, IntPtr wParam, ref Message lParam)
{
    if (code >= 0)
    {
        if (lParam.Msg == WM_CANCELJOURNAL)
        {
            // do something
        }
    }

    return CallNextHookEx(messageHook, code, wParam, ref lParam);
}

Обратите внимание, что для того, чтобы вышеуказанный вызов CallNextHookEx работал, вам также нужно будет предоставить перегруженное определение CallNextHookEx функция, которая соответствует сигнатуре вашей GetMessageProc функции обратного вызова:

[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hHook, int nCode,
                                        IntPtr wParam, ref Message lParam);
...