【问题标题】:need help in windows API InsertMenuItem在 windows API InsertMenuItem 中需要帮助
【发布时间】:2011-08-05 06:33:08
【问题描述】:

我想在其他进程中插入一个新菜单。但我得到一个错误:

试图读取或写入受保护的内存。这通常表明其他内存已损坏。

按钮代码:

    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)  

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

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

如何解决这个错误?

【问题讨论】:

  • 您是否使用钩子在其他进程的上下文中执行?

标签: .net vb.net winapi pinvoke menuitem


【解决方案1】:

P/Invoke 代码不正确...它看起来是从 VB 6 源代码复制而来,等效名称的数据类型在 VB 6 中的语义含义与它们在 VB 6 中的语义非常不同VB.NET。

此外,句柄/指针是使用固定整数类型声明的,这在 64 位环境中无法正常工作。这些类型的值应始终使用专门为此目的设计的IntPtr 类型声明。

而且,指向结构的指针需要在 VB.NET 中传递ByRef。你不能通过他们ByVal

您需要使用System.Runtime.InteropServices namespace 中的工具和 .NET marshaller 来帮助您。

这也是为什么您不应该只是复制和粘贴您在网上找到的代码而不了解其含义和作用的另一个原因。

声明应如下所示:

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

然后你可以像这样使用函数(重写你的代码来匹配):

  ' 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)

一切都按预期进行,至少在同一个过程中。请注意,P/Invoke 是一个相当困难的主题,您需要对 VB.NET 和 Win32 API 有相当透彻的了解才能使其正常工作。复制和粘贴您在网上找到的代码具有内在风险。大多数时候,它不起作用。其余时间,这是一个可能的安全风险。不幸的是,您需要的不仅仅是 Stack Overflow 上的答案来向您解释它是如何工作的。

编辑:实际上,上面的代码也可以跨进程正常工作。不需要特别的努力。我尝试在记事本的运行实例中使用菜单进行操作,一切正常。并不是说我建议在没有非常充分理由的情况下这样做......

【讨论】:

    猜你喜欢
    • 2017-06-03
    • 1970-01-01
    • 2011-06-23
    • 2013-10-02
    • 1970-01-01
    • 1970-01-01
    • 2011-08-02
    • 1970-01-01
    • 2015-10-24
    相关资源
    最近更新 更多