#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-й результат 🙂