我想从.net应用程序A发送一个字符串消息,并从应用程序B接收消息,这里是代码:
--------申请A
Private Const RF_TESTMESSAGE As Integer = &HA123
Public Structure MyData
Public M As String
Public I As Integer
End Structure
Public Function SendTest()
Dim Data As New MyData
Data.M = "QWERTY"
Data.I = 15
Dim P As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(Data))
Marshal.StructureToPtr(Data, P, False)
Dim Hdl As New IntPtr(11111) 'While 11111 is the WndHD for the application B for testing
SendMessage(Hdl, RF_TESTMESSAGE, IntPtr.Zero, P)
End Function
-------申请B
Private Const RF_TESTMESSAGE As Integer = &HA123
Public Structure MyData
Public M As String
Public I As Integer
End Structure
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
If m.Msg = RF_TESTMESSAGE Then
Dim A = DirectCast(m.GetLParam(GetType(MyData)), MyData)
MsgBox(A.M)
MsgBox(A.I)
Marshal.FreeHGlobal(m.LParam)
End If
MyBase.WndProc(m)
End Sub
应用程序B始终收到消息,但无法将点lParam转换为有效的MyData结构,有时会引发访问冲突,有时没有错误....
请建议。
答案 0 :(得分:3)
问题是您没有正确地对两个应用程序之间的数据进行编组。您在一个应用程序中分配内存,然后将指向该内存的指针传递给第二个应用程序。但是因为应用程序有私有内存空间而无法读取彼此的内存,所以该指针对第二个应用程序没用。
根据 应用程序的内存空间所指的内容,它可能会触发访问冲突,或者只是无法正常工作。
也许您对AllocHGlobal
和FreeHGlobal
函数的命名感到困惑。与第一印象可能暗示的相反,它们实际上并不分配和释放全局内存。至少不是机器上运行的所有进程全局可访问的内存。该名称来自Windows HGLOBAL
数据类型,在16位Windows时代,这种类型恰好就是这一点,所有应用程序都共享一个共同的内存空间并可以读取彼此的内存。但现代32位Windows不再是这种情况。保留名称是出于向后兼容的原因。 HGLOBAL
和HLOCAL
现在实际上意味着同样的事情。有关细节的详细信息,请参阅on MSDN。但这主要是好奇心。您无需了解和了解所有内容即可使代码正常运行。
重点是所有AllocHGlobal
都是从进程的默认堆中分配内存,只对该进程可读。因此需要跨进程封送内存,使其可以从接收消息的另一个进程访问。当然,手动执行此操作是一种选择。但不是一个很好的。做到这一点很棘手,而且没什么意义。就像蒂姆的评论暗示一样,更容易的选择是使用WM_COPYDATA
message,它为您进行编组。使用此消息时,您要共享的数据将打包在COPYDATASTRUCT
structure中。
您可以保留大部分现有代码来分配内存,只需将RF_TESTMESSAGE
窗口消息替换为WM_COPYDATA
即可。示例代码,包括必要的结构定义,可在pinvoke website上找到。
像这样(警告未经测试和未编译):
Private Const WM_COPYDATA As Integer = &H004A
<StructLayout(LayoutKind.Sequential)> _
Public Structure COPYDATASTRUCT
Public dwData As IntPtr
Public cdData As Integer
Public lpData As IntPtr
End Structure
<StructLayout(LayoutKind.Sequential)> _
Public Structure MyData
Public M As String
Public I As Integer
End Structure
Public Function SendTest()
' Create your data structure, MyData, and fill it.
Dim data As New MyData
data.M = "QWERTY"
data.I = 15
Dim pData As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(data))
Marshal.StructureToPtr(data, pData, False)
' Create the COPYDATASTRUCT you'll use to shuttle the data.
Dim copy As New COPYDATASTRUCT
copy.dwData = IntPtr.Zero
copy.lpData = pData
copy.cbData = Marshal.SizeOf(data)
Dim pCopy As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(copy))
Marshal.StructureToPtr(copy, pCopy, False)
' Send the message to the other application.
SendMessage(New IntPtr(11111), WM_COPYDATA, IntPtr.Zero, pCopy)
' Free the memory we allocated
' (This works because SendMessage is synchronous, and does not
' return until the other application has finished processing
' the data that you have sent it. That also means that the
' other application should not and cannot free the memory.
' If it needs it after processing the message, it needs to
' make a local copy.)
Marshal.FreeHGlobal(pCopy)
Marshal.FreeHGlobal(pData)
End Function
如果您决定不使用WM_COPYDATA
进行简单的路由而是自己封送数据,则需要确保调用RegisterWindowMessage
函数(如果您在代码I中没有这样做)无法看到)确保自定义窗口消息的ID是唯一的。