简体   繁体   中英

need help in windows API InsertMenuItem

I want to insert a new menu into other process. But I get an error:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

code for button:

    Mmenuhandle = GetMenu(mainhandle) 
    Mmenucount = GetMenuItemCount(Mmenuhandle)
    Smenuhandle = GetSubMenu(Mmenuhandle, 0)
    Smenucount = GetMenuItemCount(Smenuhandle)  
    With mii  
        .cbSize = Len(mii)  
        .fMask = MIIM_STATE Or MIIM_ID Or MIIM_STRING Or MIIM_FTYPE  
        .fType = MFT_STRING  
        .fState = MFS_ENABLED  
        .wID = MENUID  
        .dwTypeData = "My Menu"  
        .cch = Len(.dwTypeData)  
    End With  
    InsertMenuItem(Smenuhandle, Smenucount + 1, True, mii) ' ERROR here  
    DrawMenuBar(mainhandle)  

declare for InsertMenuItem :

Private Declare Function InsertMenuItem Lib "user32" Alias "InsertMenuItemA" _
    (ByVal hMenu As Integer, ByVal uItem As Integer, ByVal fByPosition As Boolean, ByVal lpmii As MENUITEMINFO) As Integer

declare for MENUITEMINFO :

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 Integer
    Public hbmpChecked As Integer
    Public hbmpUnchecked As Integer
    Public dwItemData As Integer
    Public dwTypeData As String
    Public cch As Integer
    Public a As Integer  
End Structure

How do I fix this error?

The P/Invoke code is incorrect ... It looks to be copied from a VB 6 source, and the data types of equivalent names have very different semantic meanings in VB 6 than they do in VB.NET.

In addition, handles/pointers are declared using fixed integer types, which will not work properly in 64-bit environments. These types of values should always be declared using the IntPtr type specifically designed for this purpose.

And, pointers to structures need to be passed ByRef in VB.NET. You can't pass them ByVal .

You need to use the tools found in the System.Runtime.InteropServices namespace and the .NET marshaller to help you out.

This is yet another reason why you should never just copy and paste code that you find online without understanding what it means and what it does.

The declarations should look like this:

Imports System.Runtime.InteropServices

Public NotInheritable Class NativeMethods

   Public Const MIIM_STATE As Integer = &H1
   Public Const MIIM_ID As Integer = &H2
   Public Const MIIM_STRING As Integer = &H40
   Public Const MIIM_BITMAP As Integer = &H80
   Public Const MIIM_FTYPE As Integer = &H100

   Public Const MFT_STRING As Integer = &H0

   Public Const MFS_ENABLED As Integer = &H0

   <StructLayout(LayoutKind.Sequential, CharSet:=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
      <MarshalAs(UnmanagedType.LPTStr)> Public dwTypeData As String
      Public cch As Integer
      Public hbmpItem As IntPtr
   End Structure

   <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=False)> _
   Public Shared Function GetMenu(ByVal hWnd As IntPtr) As IntPtr
   End Function

   <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
   Public Shared Function GetMenuItemCount(ByVal hMenu As IntPtr) As Integer
   End Function

   <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=False)> _
   Public Shared Function GetSubMenu(ByVal hMenu As IntPtr, ByVal nPos As Integer) As IntPtr
   End Function

   <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
   Public Shared Function InsertMenuItem(ByVal hMenu As IntPtr,
                                         ByVal uItem As Integer,
                                         <MarshalAs(UnmanagedType.Bool)> fByPosition As Boolean,
                                         ByRef lpmii As MENUITEMINFO) _
                                    As <MarshalAs(UnmanagedType.Bool)> Boolean
   End Function

   <DllImport("user32.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
   Public Shared Function DrawMenuBar(ByVal hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
   End Function

End Class

Then you can use the function like this (re-write your code to match):

  ' Get a handle to the menu assigned to a window (in this case, your form)
  Dim hMenu As IntPtr = NativeMethods.GetMenu(Me.Handle)

  ' Get a count of the total items in that menu
  Dim menuItemCount As Integer = NativeMethods.GetMenuItemCount(hMenu)

  ' Get a handle to the sub-menu at index 0
  Dim hSubMenu As IntPtr = NativeMethods.GetSubMenu(hMenu, 0)

  ' Get a count of the total items in that sub-menu
  Dim subMenuItemCount As Integer = NativeMethods.GetMenuItemCount(hSubMenu)

  ' Create and fill in a MENUITEMINFO structure, describing the menu item to add
  Dim mii As New NativeMethods.MENUITEMINFO
  With mii
     .cbSize = Marshal.SizeOf(mii)   ' prefer Marshal.SizeOf over the VB 6 Len() function
     .fMask = NativeMethods.MIIM_FTYPE Or NativeMethods.MIIM_STATE Or NativeMethods.MIIM_ID Or NativeMethods.MIIM_STRING
     .fType = NativeMethods.MFT_STRING
     .fState = NativeMethods.MFS_ENABLED
     .wID = 0                        ' your custom menu item ID here
     .hSubMenu = IntPtr.Zero
     .hbmpChecked = IntPtr.Zero
     .hbmpUnchecked = IntPtr.Zero
     .dwItemData = IntPtr.Zero
     .dwTypeData = "My Menu Item"    ' the name of your custom menu item
  End With

  ' Insert the menu item described by the above structure
  ' (notice that we're passing the structure by reference in the P/Invoke definition!)
  NativeMethods.InsertMenuItem(hSubMenu, subMenuItemCount + 1, True, mii)

  ' Force an update of the window's menu bar (again, in this case, your form)
  NativeMethods.DrawMenuBar(Me.Handle)

Everything works as expected, at least within the same process. Note that P/Invoke is a fairly difficult topic, and you'll need to have a fairly thorough understanding of not only VB.NET but also the Win32 API to get it to work correctly. Copying and pasting code that you find online is an inherently risky proposition. Most of the time, it won't work. The rest of the time, it's a possible security risk. Unfortunately, you'll need more than an answer on Stack Overflow to explain to you how it all works.

Edit: Actually, the above code works just fine across processes, too. No special effort required. I tried monkeying with the menus in a running instance of Notepad, and everything worked fine. Not that I recommend doing this without a very good reason...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM