Question

I have this mii variable which allocates a MenuItemInfo structure to set (or get) items of the system menu.

Private mii As New MenuItemInfo

<System.Runtime.InteropServices.
StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential,
CharSet:=System.Runtime.InteropServices.CharSet.Auto)> _
Public Structure MENUITEMINFO
    Public cbSize As Integer
    Public fMask As Integer
    Public fType As Integer
    Public fState As Integer
    Public wID As Integer
    Public hSubMenu As IntPtr
    Public hbmpChecked As IntPtr
    Public hbmpUnchecked As IntPtr
    Public dwItemData As IntPtr
    Public dwTypeData As String
    Public cch As Integer
    Public hbmpItem As IntPtr
End Structure

The problem occurs when I try to use the variable to store the information about an existing menu item using the API function GetMenuItemInfo, the variable does not change anything, all structure members still empty.

This is the API function (notice that the Byref is properly set)

''' <summary>
''' Gets the handle of the form's system menu.
''' </summary>
''' <param name="hMenu">
''' The handle to the menu that contains the menu item.
''' </param>
''' <param name="uItem">
''' The identifier or position of the menu item to get information about.
''' The meaning of this parameter depends on the value of fByPosition.
''' </param>
''' <param name="fByPosition">
''' The meaning of uItem.
''' If this parameter is FALSE, uItem is a menu item identifier,
''' If this parameter is TRUE, it is a menu item position.
''' </param>
''' <param name="lpmii">
''' A pointer to a MenuItemInfo structure that specifies the information to retrieve.
''' Note that you must set the cbSize member to sizeof(MENUITEMINFO) before calling this function.
''' </param>
<System.Runtime.InteropServices.
DllImport("user32.dll")> _
Private Shared Function GetMenuItemInfo(
        ByVal hMenu As IntPtr,
        ByVal uItem As UInteger,
        ByVal fByPosition As Boolean,
        ByRef lpmii As MenuItemInfo) As Boolean
End Function

This is how I'm trying to retrieve the information of a clicked menu item, (check the comments inside please):

Protected Overrides Sub WndProc(ByRef m As Message)

    Select Case m.Msg

        Case &H112 ' WM_SYSCOMMAND

            mii = New MenuItemInfo
            mii.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(mii)
            ' NOTE:
            ' MSDN says that I need to put the dwTypeData to a null value,
            ' if I want to retrieve a TEXT type item so...
            mii.dwTypeData = Nothing

            ' NOTE:
            ' m.WParam contains the ID of the clicked Item,
            ' so I use True to set is an Identifier and not a position.
            Dim success? As Boolean = _
                GetMenuItemInfo(MenuHandle, m.WParam, True, mii)

            MsgBox(success)        ' Result: True
            MsgBox(mii.wID)        ' Result: 0 (empty)
            MsgBox(mii.dwTypeData) ' Result: (empty)

            MsgBox(m.wParam)       ' Result: (The expected item ID)

    End Select

    ' Return control to base message handler.
    MyBase.WndProc(m)

End Sub

How I can solve this?

UPDATE:

Now my code looks like this, it retrieves the ID of any item, but not the string:

    Dim mii As New MenuItemInfo()
    mii.cbSize = Marshal.SizeOf(GetType(MenuItemInfo))
    mii.fMask = Mask.ID Or Mask.TEXT ' &H40 Or &H2
    ' mii.fType = ItemType.TEXT ' &H0

    Dim success? As Boolean = GetMenuItemInfo(MenuHandle, m.WParam, False, mii)

    MsgBox(mii.wID) ' Result: (The Expected ID)
    MsgBox(mii.dwTypeData) ' Result: (empty string)
Was it helpful?

Solution

You have to set the fMask member to specify which data to retrieve, MIIM_STRING | MIIM_ID (&H40 Or &H2) to retrieve the text and ID members.

Retrieving the text member requires two steps: Retrieve the text length to allocate a String with appropriate length, followed by actually retrieving the text:

mii = New MenuItemInfo
mii.cbSize = Marshal.SizeOf(GetType(MenuItemInfo))
mii.dwTypeData = Nothing
mii.fMask = Mask.TEXT ' &H40
' Retrieve the text length member
GetMenuItemInfo(MenuHandle, m.wParam, True, mii)
' Account for terminating NUL character and allocate String
mii.cch += 1
mii.dwTypeData = Space(mii.cch)
' Specify MIIM_STRING | MIIM_ID to retrieve both text and ID
mii.fMask = Mask.TEXT Or Mask.ID ' &H40 Or &H2
' Retrieve data
GetMenuItemInfo(MenuHandle, m.wParam, True, mii)

Note that you have to allocate a string with the appropriate size and specify the buffer size in mii.cch. The code above uses the Strings.Space Method that returns a string consisting of the specified number of spaces. This is for convenience only, you could have used a StringBuilder as well.

If you are having problems with character encodings (text that looks like chinese characters) be specific when importing the Windows API function. Most Windows APIs come in two flavors: ANSI and UNICODE with a A and W postfix, respectively. In this case you should specify GetMenuItemInfoW.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top