.netvb.netsendmessage

VB.NET compatible sendmessage and sendmessagetimeout APIs


Basically: I would like a sendmessage and sendmessagetimeout API for VB.NET that others have used and know works.

I need an API for sendmessage and sendmessagetimeout for my VB .net application. I have searched quite a bit, and everything I find doesn't seem to work: either the message just doesn't seem to be sent, or the message appears to be sent with the msg parameter always 0, and the wparam setting as what I enter for the msg setting. Pinvoke's also always seems to throw an AccessViolationException for I have no idea what reason. I tried playing around with maybe just where I put the variable, but unsurprisingly, there is not a simple logical switch of the variables.

I have tried pinvoke's:

<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

and allapi's:

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

among others, and they just don't seem to work.

So I would like to know of whatever APIs for sendmessage and/or sendmessagetimeout you know of that work! If there just aren't correct APIs for VB.net for these functions, what alternative functions that I could use to accomplish the same task as those 2?

EDIT:

Alternatively I may be sending the message incorrectly, so just to make sure that shouldn't be the issue:

I want to send WM_WININICHANGE, so I am using:

but I have tried with other values for wparam and lparam with no difference usually. When trying sendmessagetimeout, I also use:


Solution

  • Yes, the first one is correct. Don't use the old style Declare Function...Lib declarations; they're supported only for compatibility with VB 6. The new style P/Invoke definitions look like this:

    <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
    

    But sending WM_WININICHANGE is probably not; this message is long obsolete. The docs say explicitly:

    Note: The WM_WININICHANGE message is provided only for compatibility with earlier versions of the system. Applications should use the WM_SETTINGCHANGE message.

    So I recommend using WM_SETTINGCHANGE, even though the Windows headers technically define them to be the same value. That's not a contract, and your code will be much more readable if you use the correct names. Reading the documentation for that one, it says:

    Applications should send WM_SETTINGCHANGE to all top-level windows when they make changes to system parameters. (This message cannot be sent directly to a window.) To send the WM_SETTINGCHANGE message to all top-level windows, use the SendMessageTimeout function with the hwnd parameter set to HWND_BROADCAST.

    Good, at least you've got that part right already. Next order of business is writing the code to actually call the function and broadcast the message. Something like:

    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
    

    Notice that I'm checking the return value of the SendMessageTimeout function. I forget exactly how this works when you specify HWND_BROADCAST and if you get a meaningful error code, but it's always worth checking for success or failure.