在2 .net应用程序之间发送字符串消息(VB.NET)

时间:2013-08-13 13:44:17

标签: vb.net winapi pinvoke sendmessage

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

请建议。

1 个答案:

答案 0 :(得分:3)

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

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

也许您对AllocHGlobalFreeHGlobal函数的命名感到困惑。与第一印象可能暗示的相反,它们实际上并不分配和释放全局内存。至少不是机器上运行的所有进程全局可访问的内存。该名称来自Windows HGLOBAL数据类型,在16位Windows时代,这种类型恰好就是这一点,所有应用程序都共享一个共同的内存空间并可以读取彼此的内存。但现代32位Windows不再是这种情况。保留名称是出于向后兼容的原因。 HGLOBALHLOCAL现在实际上意味着同样的事情。有关细节的详细信息,请参阅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是唯一的。