[英]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
: 声明
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
: 声明
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. P / Invoke代码不正确 。它似乎是从VB 6源复制的,等效名称的数据类型在VB 6中的含义与在VB.NET中的含义非常不同。
In addition, handles/pointers are declared using fixed integer types, which will not work properly in 64-bit environments. 此外,句柄/指针使用固定的整数类型声明,在64位环境中将无法正常使用。 These types of values should always be declared using the
IntPtr
type specifically designed for this purpose. 这些类型的值应始终使用专门为此目的设计的
IntPtr
类型声明。
And, pointers to structures need to be passed ByRef
in VB.NET. 并且,结构的指针需要在VB.NET中传递
ByRef
。 You can't pass them ByVal
. 您不能通过他们
ByVal
。
You need to use the tools found in the System.Runtime.InteropServices
namespace and the .NET marshaller to help you out. 您需要使用
System.Runtime.InteropServices
命名空间和.NET编组器中的工具来帮助您。
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.
请注意,P / Invoke是一个相当困难的主题,您不仅需要对VB.NET也需要对Win32 API有一个相当透彻的理解,才能使其正常工作。 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.
不幸的是,您不仅需要有关Stack Overflow的答案,还可以向您解释它们的工作原理。
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...
这并不是说我建议没有一个很好的理由这样做...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.