2011-08-05 129 views
0

我想在其他进程中插入一个新的菜单。但是,我得到一个错误:为按钮在windows API中需要帮助InsertMenuItem

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

代码:

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 

我该如何解决THI错误?

+0

你使用一个钩子执行到其他进程的上下文吗? –

+0

是我的初学者 – sozai

回答

3

P/Invoke代码不正确 ...它看起来是从VB 6源复制的,并且VB 6中的等效名称的数据类型与VB.NET中的语义含义有很大不同。

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

而且,指向结构的指针需要在VB.NET中传递ByRef。你不能通过他们ByVal。您需要使用System.Runtime.InteropServices namespace和.NET编组中的工具来帮助您。

这是你不应该复制和粘贴你在网上找到的代码而不理解它的意义和作用的另一个原因。

的声明应该是这样的:

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才能使其正常工作。复制并粘贴您在网上找到的代码是一个固有风险的提议。大多数时候,这是行不通的。其余时间,这是一个可能的安全风险。不幸的是,你需要的不仅仅是堆栈溢出的答案,以向你解释它是如何工作的。

编辑:实际上,上面的代码也适用于跨进程。无需特别努力。我尝试用记事本运行实例中的菜单进行操作,并且一切正常。不,我建议这样做,没有非常很好的理由...