C# - захват сообщений Windows из определенного приложения
Я пишу приложение на C#, которое должно перехватывать оконные сообщения, которые отправляет другое приложение. Компания, написавшая приложение, которое я отслеживала, прислала мне пример кода, однако он написан на C++, чего я на самом деле не знаю.
В приведенном мной примере кода C++ они используют следующий код:
UINT uMsg = RegisterWindowMessage(SHOCK_MESSAGE_BROADCAST);
ON_REGISTERED_MESSAGE(WM_SHOCK_BROADCAST_MESSAGE, OnShockStatusMessage)
LRESULT OnShockStatusMessage(WPARAM wParam, LPARAM lParam);
Насколько я понимаю, это извлекает идентификатор из Windows для конкретного сообщения, которое мы хотим прослушать. Затем мы просим C++ вызывать OnShockStatusMessage всякий раз, когда перехватывается сообщение, соответствующее Id.
После небольшого исследования я собрал следующее в C#
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
private IntPtr _hWnd; // APS-50 class reference
private List<IntPtr> _windowsMessages = new List<IntPtr>(); // APS-50 messages
private const string _className = "www.AuPix.com/SHOCK/MessageWindowClass";
// Windows Messages events
private const string _messageBroadcast = "www.AuPix.com/SHOCK/BROADCAST";
private const string _messageCallEvents = "www.AuPix.com/SHOCK/CallEvents";
private const string _messageRegistrationEvents = "www.AuPix.com/SHOCK/RegistrationEvents";
private const string _messageActions = "www.AuPix.com/SHOCK/Actions";
private void DemoProblem()
{
// Find hidden window handle
_hWnd = FindWindow(_className, null);
// Register for events
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageActions ) ) );
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageBroadcast ) ) );
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageCallEvents ) ) );
_windowsMessages.Add( new IntPtr( RegisterWindowMessage( _messageRegistrationEvents ) ) );
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
// Are they registered Windows Messages for the APS-50 application?
foreach (IntPtr message in _windowsMessages)
{
if ((IntPtr)m.Msg == message)
{
Debug.WriteLine("Message from specified application found!");
}
}
// Are they coming from the APS-50 application?
if ( m.HWnd == shock.WindowsHandle)
{
Debug.WriteLine("Message from specified application found!");
}
}
Как я понимаю, это должно делать то же самое, что и это:
- Находит приложение, которое я хочу отслеживать
- Регистрирует сообщения окна, которые я хочу перехватить
- Следит за всеми оконными сообщениями - затем отбирает те, которые мне нужны
Однако в моем переопределении метода WndProc() ни одна из моих проверок не перехватывает какие-либо конкретные сообщения или сообщения от приложения, которое я отслеживаю.
Если я Debug.WriteLine для всех сообщений, которые проходят через него, я вижу, что он отслеживает их. Однако это никогда не отфильтровывает сообщения, которые я хочу.
Запустив пример приложения для мониторинга, написанного на C++, я вижу, что Window Window отправляются и собираются - просто моя реализация C# не делает то же самое.
2 ответа
Оказывается, мне также нужно было отправить другому приложению сообщение PostMessage с просьбой отправить мое приложение сообщениям окна.
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_ACTIVE_CALLINFO, (int)_thisHandle);
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_ALL_REGISTRATIONINFO, (int)_thisHandle);
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_CALL_EVENTS, (int)_thisHandle);
PostMessage((int)_hWnd, _windowsMessages[0], SHOCK_REQUEST_REGISTRATION_EVENTS, (int)_thisHandle);
Не красивый код, но достаточно хороший, чтобы доказать, что он работает, и это все, что мне нужно сейчас:)
Я думаю, что проблема с вашим P/Invoke определением для RegisterWindowMessage()
, pinvoke.net предлагает использовать следующее:
[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
static extern uint RegisterWindowMessage(string lpString);
С помощью uint
в качестве возвращаемого значения вместо IntPtr
должен иметь значение. Как правило, вы хотите использовать IntPtr
когда возвращаемое значение является дескриптором (например, HWND
или же HANDLE
), но когда возвращаемое значение может быть непосредственно преобразовано в тип C#, лучше использовать этот тип.