API sendmessage и sendmessagetimeout, совместимые с VB.NET

В основном: я хотел бы API sendmessage и sendmessagetimeout для VB.NET, который другие использовали и знали, что он работает.

Мне нужен API для sendmessage и sendmessagetimeout для моего приложения VB .net. Я искал совсем немного, и все, что я нашел, похоже, не работает: либо сообщение просто не отправляется, либо сообщение отправляется с параметром msg всегда 0, а параметр wparam соответствует тому, что я ввожу для настройки сообщения. Pinvoke также всегда выдает исключение AccessViolationException, потому что я понятия не имею, по какой причине. Я попытался поиграться, может быть, только с тем, где я поместил переменную, но неудивительно, что нет простого логического переключения переменных.

Я пробовал pinvoke:

<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function SendMessageTimeout(ByVal windowHandle As IntPtr, ByVal Msg As Integer, ByVal wParam As IntPtr, ByVal lParam As IntPtr, ByVal flags As SendMessageTimeoutFlags, ByVal timeout As Integer, ByRef result As IntPtr) As IntPtr
End Function

и аллапи:

Declare Function SendMessageTimeout Lib "user32" Alias "SendMessageTimeoutA" (ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As String, ByVal fuFlags As Long, ByVal uTimeout As Long, lpdwResult As Long) As Long

среди прочего, и они просто не работают.

Поэтому я хотел бы знать, какие APIS для sendmessage и/или sendmessagetimeout вы знаете об этой работе! Если для этих функций просто нет правильных API для VB.net, какие альтернативные функции я мог бы использовать для выполнения той же задачи, что и эти 2?

Заранее благодарю за любую имеющуюся у вас информацию :)

ИЗМЕНИТЬ:

в качестве альтернативы я могу отправить сообщение неправильно, поэтому просто чтобы убедиться, что это не должно быть проблемой:

Я хочу отправить WM_WININICHANGE, поэтому я использую:

  • hwnd = HWND_BROADCAST = 0xffff = 65535
  • сообщение = WM_WININICHANGE = 0x001A = 26
  • wparam и lparam равны 0

но я пробовал с другими значениями для wparam и lparam обычно без разницы. при попытке sendmessagetimeout я также использую:

  • флаги = SMTO_ABORTIFHUNG = 2
  • тайм-аут = 1000 = 1 секунда
  • затем 0 для результата.

person user1167662    schedule 26.01.2012    source источник
comment
PInvoke верен с точки зрения размеров параметров. См. Типы данных Windows.   -  person GSerg    schedule 26.01.2012
comment
Я знаю, что они, вероятно, кажутся правильными, однако из их использования я знаю, что они работают не совсем правильно, ха-ха. в основном я хочу объявления API, которые использовали другие, и они знают, что они работают.   -  person user1167662    schedule 26.01.2012
comment
Вы на самом деле передаете 0s, а не IntPtr.Zero?   -  person GSerg    schedule 26.01.2012
comment
то, что вы сказали, верно, я никогда не пробовал intptr.zero. Кроме прохождения 0 я тоже ничего не пробовал.   -  person user1167662    schedule 26.01.2012
comment
Если вы никогда не использовали IntPtr.Zero, значит, вы никогда не использовали правильное объявление.   -  person Hans Passant    schedule 26.01.2012


Ответы (1)


Да, первый правильный. Не используйте объявления Declare Function...Lib в старом стиле; они поддерживаются только для совместимости с VB 6. Определения нового стиля P/Invoke выглядят следующим образом:

<DllImport("user32.dll", SetLastError:=True)> _
Public Shared Function SendMessageTimeout(ByVal hWnd As IntPtr,
                                          ByVal msg As Integer,
                                          ByVal wParam As IntPtr,
                                          ByVal lParam As IntPtr,
                                          ByVal flags As SendMessageTimeoutFlags,
                                          ByVal timeout As Integer,
                                          ByRef result As IntPtr) As IntPtr
End Function

Но отправлять WM_WININICHANGE, вероятно, нельзя; это сообщение давно устарело. В документах прямо сказано:

Примечание. Сообщение WM_WININICHANGE предоставляется только для совместимости с более ранними версиями системы. Приложения должны использовать сообщение WM_SETTINGCHANGE.

Поэтому я рекомендую использовать WM_SETTINGCHANGE, даже несмотря на то, что заголовки Windows технически определяют их как одно и то же значение. Это не контракт, и ваш код будет намного читабельнее, если вы будете использовать правильные имена. Читая документацию для этого, он говорит:

Приложения должны отправлять WM_SETTINGCHANGE всем окнам верхнего уровня, когда они вносят изменения в системные параметры. (Это сообщение нельзя отправить непосредственно в окно.) Чтобы отправить сообщение WM_SETTINGCHANGE всем окнам верхнего уровня, используйте SendMessageTimeout с параметром hwnd, для которого задано значение HWND_BROADCAST.

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

Public Shared ReadOnly HWND_BROADCAST As New IntPtr(&HFFFF)
Public Const WM_SETTINGCHANGE As Integer = &H001A

<Flags()> _
Public Enum SendMessageTimeoutFlags
    SMTO_NORMAL = 0
    SMTO_BLOCK = 1
    SMTO_ABORTIFHUNG = 2
    SMTO_NOTIMEOUTIFNOTHUNG = 8
End Enum  

Public Sub BroadcastChangeMessage()
    Dim returnValue As IntPtr = SendMessageTimeout(HWND_BROADCAST,
                                                   WM_SETTINGCHANGE,
                                                   IntPtr.Zero,
                                                   IntPtr.Zero,
                                                   SendMessageTimeoutFlags.SMTO_ABORTIFHUNG Or SendMessageTimeoutFlags. SMTO_NOTIMEOUTIFNOTHUNG,
                                                   5000,
                                                   IntPtr.Zero)
    If returnValue = IntPtr.Zero Then
        MessageBox.Show("Something went wrong and the function failed. Error code: " _
                        & Marshal.GetLastWin32Error().ToString())
    End If
End Sub

Обратите внимание, что я проверяю возвращаемое значение функции SendMessageTimeout. Я точно не помню, как это работает, когда вы указываете HWND_BROADCAST и если вы получаете осмысленный код ошибки, но всегда стоит проверить успех или неудачу.

person Cody Gray    schedule 26.01.2012
comment
большое спасибо! это было идеальное объяснение, в котором я нуждался! Просто хочу уточнить 2 вещи: так 0 сильно отличается от intptr.zero? а вы случайно не знаете, какое значение тайм-аута использует мой компьютер, когда сообщение появляется в результате обычной активности системы? - person user1167662; 26.01.2012
comment
@user: Да, 0 — это целое число. IntPtr.Zero — это поле типа IntPtr со значением, равным 0. Вы также можете написать New IntPtr(0), но я думаю, что так будет понятнее. Он никогда не должен был компилироваться с передачей 0 для типа IntPtr. - person Cody Gray; 26.01.2012
comment
Что вы подразумеваете под регулярной активностью системы? Для большинства сообщений значение тайм-аута отсутствует. Для этого сообщения, отправленного в ответ на изменение пользователем настроек в Панели управления? Я понятия не имею. Я подозреваю, что это не имеет значения. Приложения либо будут отвечать в течение 1-5 секунд, либо вообще не будут отвечать. Это просто для того, чтобы зависшее приложение, которое не обрабатывает сообщения, не блокировало ваше приложение, отправляющее сообщение. Волноваться особо не стоит. - person Cody Gray; 26.01.2012
comment
Реальное беспокойство вызывает то, что многие приложения игнорируют сообщение WM_SETTINGCHANGE и не утруждают себя обновлением своего пользовательского интерфейса в ответ, создавая впечатление, что оно не работает, хотя код на 100% правильный. Нет другого выхода, кроме как закрыть эти приложения и перезапустить их. В предыдущих версиях Windows (в последнее время я не пробовал) сам Проводник иногда был одним из худших нарушителей. - person Cody Gray; 26.01.2012
comment
хорошо, причина, по которой я хочу знать о тайм-ауте, и причина, по которой я даже делаю это приложение, заключается в том, что я хочу попытаться заблокировать это сообщение от связи с отдельной программой, потому что сообщение приводит к сбою моей другой программы. однако кажется, что если моя программа не отвечает на сообщение в течение времени ожидания, она не падает. поэтому мне нужно знать, когда истечет время ожидания фактического сообщения, достигающего моего компьютера. У меня есть еще один вопрос о более крупной проблеме, которую я пытаюсь решить: stackoverflow.com/questions/8997216/ - person user1167662; 26.01.2012
comment
@user: Хм, э... тогда это неправильный способ. Этот код отправляет сообщение всем окнам верхнего уровня. Он ничего не блокирует. Как это могло быть? - person Cody Gray; 26.01.2012
comment
WM_WININICHANGE = 0x1a, WM_SETTINGCHANGE = 0x1a :) - person Hans Passant; 26.01.2012
comment
@Cody Grey: Я знаю, что это не останавливает его, я думаю, я совсем не ясно выразился в этом комментарии: я использую этот инструмент для диагностики и тестирования, а не для того, чтобы остановить его. Используя это приложение, я могу знать, что отправляю это сообщение и ТОЛЬКО это сообщение моей проблемной программе. - person user1167662; 26.01.2012
comment
@user: Ах, мне было интересно, не имели ли вы в виду, что используете его в качестве инструмента тестирования, но я не так прочитал ваш комментарий. Теперь имеет больше смысла. Вы можете увеличить значение до нужного для целей тестирования, худшее, что может случиться, это зависание вашей машины, что довольно часто встречается при отладке. Что касается отладки другой проблемы, вам нужно пройтись по коду и выяснить, где обрабатывается сообщение WM_SETTINGCHANGE и почему это вызывает сбой. - person Cody Gray; 27.01.2012
comment
@CodyGray: ну, половина проблемы в том, что моя ошибка не воспроизводится в режиме отладки в Visual Studio 2008 Express. Однако у меня просто возникла идея пройти через это с VS2010 с отключенным Just My Code, тогда я думаю, что смогу увидеть, что происходит? Хотя это может даже не точно представлять проблему, так как это не происходит при отладке... хотя попробовать стоит, спасибо! - person user1167662; 27.01.2012