0

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 doesnt 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 dont 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?

Thank you in advance for any information you have :)

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:

  • hwnd = HWND_BROADCAST = 0xffff = 65535
  • msg = WM_WININICHANGE = 0x001A = 26
  • wparam and lparam of 0

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

  • flags = SMTO_ABORTIFHUNG = 2
  • timeout = 1000 = 1 second
  • then 0 for result.
user1167662
  • 1,245
  • 3
  • 16
  • 25
  • PInvoke's is correct in terms of parameter sizes. See [Windows Data Types](http://msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx). – GSerg Jan 26 '12 at 16:14
  • I am aware that they likely "appear correct," however I know from using them that they don't work quite right haha. basically I want API declarations that others have used and they know work. – user1167662 Jan 26 '12 at 16:17
  • Are you actually passing `0`s, not `IntPtr.Zero`? – GSerg Jan 26 '12 at 16:34
  • what you said is true, I have never tried intptr.zero. Apart from passing "0" I have also tried "nothing". – user1167662 Jan 26 '12 at 16:44
  • If you never used IntPtr.Zero then you never used the *correct* declaration. – Hans Passant Jan 26 '12 at 19:56

1 Answers1

4

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.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • thank you SO much! that was the perfect explanation that I needed! Just want to clarify 2 things though: so "0" is very different from intptr.zero? and do you happen to know what the timeoutvalue that my computer uses is when the message appears as a result of regular system activity? – user1167662 Jan 26 '12 at 19:08
  • 1
    @user: Yes, `0` is an an integer. `IntPtr.Zero` is a field of the `IntPtr` type with its value set to 0. You could also write `New IntPtr(0)`, but I think it's clearer this way. It should have never compiled with passing `0` for the type `IntPtr`. – Cody Gray - on strike Jan 26 '12 at 19:12
  • What do you mean by "regular system activity"? For most messages, there is no time-out value. For *this* message, sent as a response to the user changing settings in Control Panel? I have no idea. I suspect it doesn't matter. Apps are either going to respond within 1 to 5 seconds, or they're not going to respond at all. It's just to prevent a hung app that is not processing messages from locking up *your* app sending the message. It's not really worth worrying about. – Cody Gray - on strike Jan 26 '12 at 19:13
  • The real worry is that lots of apps ignore the `WM_SETTINGCHANGE` message and don't bother to update their UI in response, making it appear to "not work", even though the code is 100% correct. There's no other option but to close those apps and restart them. In previous versions of Windows (I haven't tried recently), Explorer itself was sometimes one of the worst offenders. – Cody Gray - on strike Jan 26 '12 at 19:14
  • well the reason I want to know about the timeout, and reason I am even making this app is that I want to try to block this message from communicating with a separate program because the message is crashing my other program. it seems though, that if my program doesnt reply to the message for the timeout length, it does not crash. so I need to know then what the actual message reaching my computer will time out at. I have another question about the larger problem that I am trying to solve: http://stackoverflow.com/questions/8997216/how-can-i-stop-my-application-from-recieving-a-certain-message – user1167662 Jan 26 '12 at 19:25
  • @user: Hmm, uh... then this is the wrong way of doing it. This code sends the message to all top-level windows. It doesn't block anything. How could it? – Cody Gray - on strike Jan 26 '12 at 19:31
  • WM_WININICHANGE = 0x1a, WM_SETTINGCHANGE = 0x1a :) – Hans Passant Jan 26 '12 at 19:52
  • @Cody Gray: I know this isn't stopping it, I guess I wasn't clear at all in that comment: I am using this a diagnosis and testing tool, not to actually stop it. Using this app I can know that I am sending that message and ONLY that message to my problematic program. – user1167662 Jan 26 '12 at 19:56
  • @user: Ah, I wondered if you meant you were using it as a testing tool, but that wasn't how I read your comment. Makes more sense now. You can crank up the value as high as you want for testing purposes, the worst thing that happens is your machine freezes which is fairly common in debugging. As far as debugging the other problem, you need to step through the code and figure out where the WM_SETTINGCHANGE message is being handled and why that's causing a crash. – Cody Gray - on strike Jan 26 '12 at 20:03
  • @CodyGray: well half the issue is that my bug doesnt replicate itself in debug mode in visual studio 2008 express. However I just had the idea to step through it with VS2010 with Just My Code turned off, then I think I should be able to see what's going on? Though it may not even represent the problem accurately since it doesnt happen when debugging... worth a try though, thanks! – user1167662 Jan 26 '12 at 20:13