Solved

vb.net windows messaging wParam errors

Posted on 2009-05-05
24
766 Views
Last Modified: 2012-05-06
Hi All,

I am trying to read windows messages being sent from an application written by a colleague not in .net. I have never tried this before and I may have gone down the wrong road. If I use
GlobalGetAtomName I get  'Attempted to read or write protected memory'. If I use GetClassName I get 'Arithmetic operation resulted in an overflow'.
Any help would be most appreciated.
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        Dim hwnd, wMsg, wParam, lParam As Int64 'Long

        Dim Notes As String

        hwnd = (m.HWnd.ToString)

        wMsg = (m.Msg.ToString)

        wParam = (m.WParam.ToString)

        lParam = (m.LParam.ToString)
 

        If wMsg > 50000 Then ' 50103

            Dim buflen As Long

            Dim buf As String

            buflen = 1024

            buf = Space$(buflen - 1)

            buflen = GlobalGetAtomName(wParam, buf, buflen)

            'buflen = GetClassName(wParam, buf, buflen)

            Notes = buf '"Class: " & buf & vbCrLf
 

            MsgBox("H: " + CStr(hwnd) + " M: " + CStr(wMsg) + " W: " + CStr(wParam) _

            + " L: " + CStr(lParam) + " rc: " + CStr(buflen) + " N: " + Notes)

        End If

        MyBase.WndProc(m)

    End Sub

Open in new window

0
Comment
Question by:robbie79
  • 14
  • 6
  • 3
24 Comments
 
LVL 39

Expert Comment

by:abel
Comment Utility
what message are you trying to intercept? Is it a registered message or a windows message? What data do you expect to be in the wParam? Have you checked your PInvoke declarations with www.pinvoke.net?
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
Most calls in VB.Net require the Integer type (32 bit)...you probably have the declarations for GlobalGetAtomName() and GetClassName() wrong...may we see them?
0
 

Author Comment

by:robbie79
Comment Utility
Hi Abel,
I am told that I am intercepting a Registered Message i.e. WM_TEST_MYMSG. This holds special parameters in the wParam value which I am told can be translated back to text by using GlobalAddAtom. I presume I will have to somehow register WM_TEST_MYMSG in my programme, where somehow I can then call WM_TEST_MYMSG. Are you suggesting that pinvoke can manage the whole thing on my side?
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
If wParam is holding a pointer to text, then you can arrange it with the correct pinvoke calls. If you need GlobalAddAtom, there must be something wrong, but in your code you use GlobalGetAtom, which seems more correct.

you seem to have many Int64's there, which is not according to the declaration for win32 messages. Instead, you should probably need to use IntPtr or IntPtr64
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
Here's a tiny example with a stringbuilder and how to declare the GlobalGetAtom: http://www.pinvoke.net/default.aspx/kernel32/GlobalGetAtomName.html
0
 

Author Comment

by:robbie79
Comment Utility
Declare Function GlobalGetAtomName Lib "kernel32" Alias "GlobalGetAtomNameA" _
        (ByVal nAtom As Int64, ByVal lpBuffer As String, ByVal nSize As Int64) As Int64

    Private Declare Function GetClassName Lib "user32" Alias _
        "GetClassNameA" (ByVal hwnd As Int64, ByVal lpClassName _
        As String, ByVal nMaxCount As Int64) As Int64
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
Don't use String. User StringBuilder instead and give the maximum amount of characters before you pass the stringbuilder object on to the GlobalGetAtomName.

Dim sb as new StringBuilder(200)
GlobalGetAtomName(yourAtom, sb, 200)

Now, you declared it as nAtom as Int64. That should be a Short instead. The nSize is also an Int64, but should be a normal Integer, and the return value is an unsigned int, so Integer is fine.
0
 

Author Comment

by:robbie79
Comment Utility
I have updated the attached code and I get an "OverflowException was unhandled"


Imports System.Text

Public Class ofMessages
 

    Declare Function RegisterWindowMessage Lib "user32" Alias _

      "RegisterWindowMessageA" (ByVal lpString As String) As Int64
 

    Declare Function GlobalGetAtomName Lib "kernel32" Alias "GlobalGetAtomNameA" _

        (ByVal nAtom As Short, ByVal lpBuffer As StringBuilder, ByVal nSize As Integer) As Integer
 

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        Dim hwnd, wMsg, wParam, lParam As Integer

        Dim Notes As String

        hwnd = (m.HWnd.ToString)

        wMsg = (m.Msg.ToString)

        wParam = (m.WParam.ToString)

        lParam = (m.LParam.ToString)
 

        If wMsg > 50000 Then
 

            Dim nSize As Integer

            nSize = 1024

            Dim AtomName As New StringBuilder(nSize)

            If wParam <> 0 Then

                MsgBox("Msg No: " + CStr(wMsg) + " W: " + CStr(wParam) + " AtomName: " _

                + AtomName.ToString + " nSize: " + CStr(nSize))

                nSize = GlobalGetAtomName(wParam, AtomName, nSize)

            End If

            Notes = AtomName.ToString

            MsgBox("H: " + CStr(hwnd) + " M: " + CStr(wMsg) + " W: " + CStr(wParam) _

            + " L: " + CStr(lParam) + " rc: " + CStr(nSize) + " N: " + Notes)

        End If

        MyBase.WndProc(m)

    End Sub

End Class

Open in new window

0
 
LVL 39

Expert Comment

by:abel
Comment Utility
sounds like a good exception ;-)

Did I missed something earlier or was I unclear? Apologies if so. You are using "Declare Function" but you are using VB.NET, aren't you? You should instead use PInvoke declarations, like the ones from pinvoke.net (in fact: it is good practice to always check your declarations there), the style you are using is deprecated unless you use VB6.

For instance, your RegisterWindowsMessage function should be declared so:

<DllImport("user32.dll", CharSet:=CharSet.Auto)> _ Private Shared Function RegisterWindowMessage( _      ByVal lpString As String) As UInteger End Function
which is, both in signature (UInteger instead of Int64) and in technique (PInvoke instead of Declare) quite different. The GlobalGetAtomName should be something like this (compiled from C#):

<DllImport("kernel32.dll", SetLastError := true, CharSet := CharSet.Auto)> _ Private Shared Function GlobalGetAtomName(nAtom As UShort, lpBuffer As StringBuilder, nSize As Integer) As UInteger
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
Ah, that should've been this:

<DllImport("kernel32.dll", SetLastError := true, CharSet := CharSet.Auto)> _
 Private Shared Function GlobalGetAtomName( _
    nAtom As UShort, _
    lpBuffer As StringBuilder,  _
    nSize As Integer) As UInteger
End Function
0
 

Author Comment

by:robbie79
Comment Utility
Yes sorry I misunderstood, this is the first time I've used PInvoke or tried to interpret windows messages. As you can see I have changed the code. I still get "OverflowException was unhandled"


Imports System.Runtime.InteropServices

Imports System.Text

Public Class ofMessages
 

    <DllImport("user32.dll", CharSet:=CharSet.Auto)> _

    Private Shared Function RegisterWindowMessage( _

         ByVal lpString As String) As UInteger

    End Function
 

    <DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _

    Private Shared Function GlobalGetAtomName( _

    ByVal nAtom As UShort, _

    ByVal lpBuffer As StringBuilder, _

    ByVal nSize As Integer) As UInteger

    End Function
 

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        Dim hwnd, wMsg, wParam, lParam As Integer

        Dim Notes As String

        hwnd = (m.HWnd.ToString)

        wMsg = (m.Msg.ToString)

        wParam = (m.WParam.ToString)

        lParam = (m.LParam.ToString)
 

        If wMsg > 50000 Then

            Dim nSize As Integer

            nSize = 1024

            Dim AtomName As New StringBuilder(nSize)

            If wParam <> 0 Then

                MsgBox("Msg No: " + CStr(wMsg) + " W: " + CStr(wParam) + " AtomName: " _

                + AtomName.ToString + " nSize: " + CStr(nSize))

                nSize = GlobalGetAtomName(wParam, AtomName, nSize)

            End If

            Notes = AtomName.ToString

            MsgBox("H: " + CStr(hwnd) + " M: " + CStr(wMsg) + " W: " + CStr(wParam) _

            + " L: " + CStr(lParam) + " rc: " + CStr(nSize) + " N: " + Notes)

        End If

        MyBase.WndProc(m)

    End Sub

End Class

Open in new window

0
Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 39

Expert Comment

by:abel
Comment Utility
I would love to know on what line you get that error.
0
 

Author Comment

by:robbie79
Comment Utility
MsgBox("Msg No: " + CStr(wMsg) + " W: " + CStr(wParam) + " AtomName: " _
                + AtomName.ToString + " nSize: " + CStr(nSize))
Is OK and returns e.g. Msg No: 50083 W: 340527 AtomName: nSize: 1024

                nSize = GlobalGetAtomName(wParam, AtomName, nSize)
OverflowException was unhandled
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
there's something wrong in that code, indeed. You are using a wParam (Integer) on the spot of a UShort. And that doesn't fit.

Check the information about the message how to go from a wParam to an Atom. This is not predefined or win32 behavior, so the application you are dealing with should somehow have documented that.

These are very old techniques and already at the times of the first win32 Windows NT Microsoft discouraged using atoms, because the global atom table is very small. Always make sure you delete the atom after it's used.

Check this note from MS on atoms, too:

The string returned for an integer atom (an atom whose value is in the range  0x0001 to 0xBFFF) is a null-terminated string in which the first character is a  pound sign (#) and the remaining characters represent the unsigned integer atom  value.
and this:

Using this function incorrectly might compromise the security of your program.  Incorrect use of this function includes not correctly specifying the size of the  lpBuffer parameter. Also, note that a global atom is accessible by  anyone; thus, privacy and the integrity of its contents is not assured.


0
 
LVL 39

Expert Comment

by:abel
Comment Utility
Let's check that wParam a bit further. Apparently, it contains 340527 in your example output. The AtomId can only be a value between 0 and 65535 inclusive. With a special meaning for any values up to 49151 (hex &hBFFF), which means: "numerical atom", that leaves only a few values for a string atom.

It is not uncommon to use the high word or the low word of a dword (which the wParam is) when you need only a word (ushort), and that you store additional information in the other half, or none at all The value 340527 however, definitely spans the word boundary and does not only have zeroes on the low or high word. So I would be guessing if I tried to understand what to use there.

Which means: back to the people that told you to use GlobalGetAtomName and ask them how you are supposed to get the USHORT / WORD / ATOM value out of the DWORD of the wParam.
0
 
LVL 85

Expert Comment

by:Mike Tomlinson
Comment Utility
This simple test worked for me:
(just to make sure the atoms were working as expected)

Imports System.Text
Public Class Form1

    Private Declare Function GlobalAddAtom Lib "kernel32.dll" Alias "GlobalAddAtomA" (ByVal lpString As String) As Short
    Private Declare Function GlobalFindAtom Lib "kernel32.dll" Alias "GlobalFindAtomA" (ByVal lpString As String) As Short
    Private Declare Function GlobalDeleteAtom Lib "kernel32.dll" (ByVal nAtom As Short) As Short
    Private Declare Function GlobalGetAtomName Lib "kernel32.dll" Alias "GlobalGetAtomNameA" (ByVal nAtom As Short, ByVal lpBuffer As System.Text.StringBuilder, ByVal nSize As Integer) As Integer

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim originalAtomName As String = "IdleAtom"
        Dim atomID As Short = GlobalAddAtom(originalAtomName)

        Dim atomName As New StringBuilder(255)
        GlobalGetAtomName(atomID, atomName, atomName.Capacity)
        Dim retrievedAtomName As String = atomName.ToString

        GlobalDeleteAtom(atomID)

        MessageBox.Show(originalAtomName & " = " & retrievedAtomName & " ?")
    End Sub

End Class

**********************************************************************************************

According to the docs, the max size for an atom name is 255 bytes:
http://msdn.microsoft.com/en-us/library/ms649056(VS.85).aspx

    "lpString - [in] Pointer to the null-terminated string to be added. The string can have a maximum size of 255 bytes."

So does this work any better?
    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

        Dim Notes As String = ""

        If m.Msg > 50000 Then

            Dim AtomName As New StringBuilder(255)

            If m.WParam.ToInt32 <> 0 Then

                GlobalGetAtomName(m.WParam.ToInt32, AtomName, AtomName.Capacity)

            End If

            Notes = AtomName.ToString

            MsgBox("Notes = " & Notes)

        End If

        MyBase.WndProc(m)

    End Sub

Open in new window

0
 
LVL 39

Expert Comment

by:abel
Comment Utility
> GlobalGetAtomName(m.WParam.ToInt32, AtomName, AtomName.Capacity)

intriguing that you didn't get an error on that, Idle_Mind, just reading up on atoms here (too) and it really only takes a USHORT... which is closer to Int16, actually.

on another page: do you by any chance, know whether it matters to use PInvoke syntax or VB6 syntax? I thought it differed in the marshalling, but seeing your working code, I doubt that now. Are they equivalent? (sorry if this clutters the thread, you do not have to answer of course).
0
 
LVL 85

Accepted Solution

by:
Mike Tomlinson earned 250 total points
Comment Utility
@abel...I've often wondered the same thing.  I prefer the older style...but that is just more of personal preference because it looks similar to what I'm accustomed to seeing from the VB6 days.

This article, titled "Walkthrough: Calling Windows APIs", compares the two methods and states:
http://msdn.microsoft.com/en-us/library/172wfck9(VS.71).aspx

    "The DllImport attribute provides a second way to call functions in DLLs without type libraries. DllImport is roughly equivalent to using a Declare statement but provides more control over how functions are called."
0
 
LVL 39

Assisted Solution

by:abel
abel earned 250 total points
Comment Utility
It keeps you busy,... Here, for the fun of it, just to find out whether there actually is something, you can list all available atoms this way. And mind you, on any running system there are many registered atoms at any given time, the list started for me with:

bfff: #49151c001: StdExitc002: StdNewDocumentc003: StdOpenDocumentc004: StdEditDocumentc005: StdNewfromTemplatec006: StdCloseDocumentc007: StdShowItemc008: StdDoVerbItem
And in the process, not intentionally, I managed to get the OverflowException (when I let the atomId run to &HFFFF, it got an overflow on the last iteration). Meaning: that overflow is indeed, and logically so, from your wParam which is way too high.

For atomId As UShort = &HBFFF To &HFFFF - 1

    Dim buffer As New StringBuilder(255)

    If GlobalGetAtomName(atomId, buffer, 255) <> 0 Then

        TextBox1.Text += atomId.ToString("x") & ": " & buffer.ToString() & vbCrLf

    End If

Next
 
 

' I used this declaration '

<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _

Private Shared Function GlobalGetAtomName( _

   ByVal nAtom As UShort, _

   ByVal lpBuffer As StringBuilder, _

   ByVal nSize As Integer) As UInteger

End Function

Open in new window

0
 
LVL 39

Expert Comment

by:abel
Comment Utility
@Idle_Mind: thanks for that explanation & link. i guess that, because I use C# a lot as well, that my preference went to the more verbose PInvoke/DllImport-attributed declaration.
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
@OP: if you try to list all atoms, look through them and check whether you can find your expected atom (do you know its name?) and check the hex number next to it. It is quite likely that we can help you deduct from that information how to get from the wParam to the ATOM id, if it indeed contains the info we are after.
0
 

Author Comment

by:robbie79
Comment Utility
I now fully understand that attempting to move an integer bigger than 65535 will not work. My confusion has mostly come from looking at old VB6 code.
Perhaps I should be asking a different question. I am attempting to replace a VB6 application with a vb.net / visual studio 2005 application. The VB6 was able to read messages sent from an application written in Visual Dataflex. The windows messages were easy to register and using GlobalGetAtomName(wParam, Buffer, 1024), string information could be passed back and forth.
What is the correct tool in vb.net for me to access the windows messages, register windows messages, latch onto and read the string information?
0
 
LVL 39

Expert Comment

by:abel
Comment Utility
It is probably best to stick to one question per thread at a time, see http://www.experts-exchange.com/help.jsp?hi=23, esp. in complex and long threads like this one.

However, a short answer seems appropriate: you do not need to change your approach, but if both applications are written in .NET and you can change the way they communicate, then there are many other ways to do it. There have been many books written about the subject, which does not make it easy to answer.

If you want to stick to the VB6 approach, which is basically a Win32 approach, you can. The way to deal with messages and register window messages still needs to be done through the win32 api.

A small note on the integer: in your VB6 code, high integers wouldn't have worked either. The Atoms just cannot be higher then 65535.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

In my previous article (http://www.experts-exchange.com/Programming/Languages/.NET/.NET_Framework_3.x/A_4362-Serialization-in-NET-1.html) we saw the basics of serialization and how types/objects can be serialized to Binary format. In this blog we wi…
This document covers how to connect to SQL Server and browse its contents.  It is meant for those new to Visual Studio and/or working with Microsoft SQL Server.  It is not a guide to building SQL Server database connections in your code.  This is mo…
It is a freely distributed piece of software for such tasks as photo retouching, image composition and image authoring. It works on many operating systems, in many languages.
When you create an app prototype with Adobe XD, you can insert system screens -- sharing or Control Center, for example -- with just a few clicks. This video shows you how. You can take the full course on Experts Exchange at http://bit.ly/XDcourse.

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

7 Experts available now in Live!

Get 1:1 Help Now