ОШИБКА_PROC_NOT_FOUND при вызове PInvoke для отправки Messagew

#c# #windows #pinvoke

#c# #Windows #pinvoke

Вопрос:

Мне нужно автоматизировать стороннюю программу, и единственный способ действий — имитировать нажатие на некоторые кнопки.

Я делаю это, находя дескриптор HWND кнопки с помощью EnumChildWindows . Когда я нашел «окно» (кнопку), я пытаюсь отправить BM_CLICK в него с помощью SendMessageW . Это работает, мои тесты показывают, что кнопка действительно думает, что она была нажата.

Проблема возникает при моей обработке ошибок. В BM_CLICK сообщении нет обратной связи, поэтому я действительно не знаю, было ли на него нажато. Я подумал, что должен быть прилежным и проверять наличие любых кодов ошибок, хотя с Marshal.GetLastWin32Error .

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

Я импортирую SendMessageW следующим образом:

 [DllImport("User32.dll", 
           CharSet = CharSet.Unicode, 
           CallingConvention = CallingConvention.Winapi, 
           SetLastError = true)]
public static extern IntPtr SendMessageW(
     HandleRef hWnd, 
     UInt32 Msg, 
     UIntPtr wParam, 
     IntPtr lParam);
  

Код, выполняющий вызов, является:

 User32.SendMessageW(
    buttonHandle,
    (uint)ButtonControlMessages.BM_CLICK, // value of BM_CLICK = 0x00F5.
    UIntPtr.Zero,
    IntPtr.Zero);

int error = Marshal.GetLastWin32Error();
if (error != ErrorCodes.ERROR_SUCCESS) // love the name of this error code.
    throw new Win32Exception(error);
  

Мои тесты просто используют простые Windows Forms с прикрепленным элементом управления button. Таким образом, я могу получить дескриптор через button.Handle . На нее нажимают; может быть, эта ошибка совершенно не связана?

Конечно, было бы неплохо избавиться от этого, хотя я хотел бы каким-то образом быть уверенным, что вызов SendMessageW , по крайней мере, не завершился ошибкой.

Я на Windows 7 x86-32 с .NET 4.

Ответ №1:

Соглашение о вызове должно быть Stdcall , но поскольку это значение по умолчанию, вы можете просто удалить его.

Я думаю, что ваш P / Invoke SendMessage выглядит немного странно, но, вероятно, причина проблемы не в этом. В любом случае я бы сделал это следующим образом:

 [DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
  

Я думаю, что здесь происходит то, что SendMessage() работает, но не присваивает последнюю ошибку. Единственное, что в документации для SendMessage() упоминается об ошибках, это:

Когда сообщение блокируется UIPI, последней ошибке, полученной с помощью GetLastError, присваивается значение 5 (доступ запрещен).

Возвращаемое значение для SendMessage() зависит от отправленного сообщения. В случае BM_CLICK очевидно, что сообщение не отправлено. Другими словами, вы просто не получаете никакой обратной связи.

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

1. Спасибо за помощь, не знал, что это добавило W автоматически. К сожалению, другие изменения не исправили это, оно демонстрирует точно такое же поведение. SetLastError = False также не имеет значения. Может быть, Windows Forms делает что-то странное?

2. Я запускаю тесты через ReSharper xUnit runner, и это Marshal.FreeHGlobal совершенно странно в режиме отладки, так что, возможно, это еще одна странность из-за среды. Я узнаю больше, когда смогу запустить это вне тестов.

3. Я обновил свой ответ, думаю, у меня есть лучшее представление о том, что происходит.

4. Хорошо, спасибо, это кажется правдоподобным. Это просто ничего не делает, и я получаю резидентный код ошибки, верно?

5. Спасибо. Вот ваш 15-й результат 🙂