0

我想从 .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 结构,有时会引发访问冲突,有时没有错误....

请指教。

4

1 回答 1

3

问题是您没有正确编组两个应用程序之间的数据。您在一个应用程序中分配内存,然后将指向该内存的指针传递给第二个应用程序。但是因为应用程序有私有内存空间并且不能读取彼此的内存,所以该指针对于第二个应用程序是无用的。

根据它在应用程序的内存空间中指向的内容,它可能会触发访问冲突,或者只是无法正常工作。

也许您对AllocHGlobalandFreeHGlobal函数的命名感到困惑。与第一印象可能暗示的相反,它们实际上并没有分配和释放全局内存。至少不是机器上运行的所有进程都可以全局访问的内存。该名称来自 WindowsHGLOBAL数据类型,在 16 位 Windows 时代,它曾经就是这个意思,所有应用程序确实共享一个公共内存空间,并且可以读取彼此的内存。但在现代 32 位 Windows 中,情况已不再如此。出于向后兼容性的原因,保留了这些名称。HGLOBAL并且HLOCAL在今天实际上意味着同样的事情。MSDN上提供了有关细节的更多信息. 但这主要是一种好奇心。您无需了解和理解所有这些即可使代码正常工作。

关键是AllocHGlobal所做的只是从进程的默认堆中分配内存,仅对该进程可读。因此需要跨进程编组内存,使其可以从接收消息的另一个进程中访问。当然,手动执行此操作是一种选择。但不是一个很好的。做对很棘手,而且没有什么意义。就像 Tim 的评论暗示的那样,更简单的选择是使用WM_COPYDATAmessage,它会为您进行编组。当您使用此消息时,您要共享的数据被打包在一个COPYDATASTRUCT结构中。

您可以保留大部分现有代码来分配内存,只需将自定义RF_TESTMESSAGE窗口消息替换为WM_COPYDATA. pinvoke 网站上提供了示例代码,包括必要的结构定义。

像这样的东西(警告 - 未经测试和未编译):

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函数(如果您还没有在我看不到的代码中这样做)以确保您的自定义 ID窗口消息是唯一的。

于 2013-08-13T16:07:27.150 回答