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

問題は、2 つのアプリケーション間でデータを適切にマーシャリングしていないことです。1 つのアプリケーションでメモリを割り当て、そのメモリへのポインターを 2 番目のアプリケーションに渡します。しかし、アプリケーションにはプライベート メモリ空間があり、互いのメモリを読み取ることができないため、そのポインタは 2 番目のアプリケーションには役に立ちません。

そのアプリケーションのメモリ空間で何を指しているかによって、アクセス違反を引き起こしているか、正しく動作していない可能性があります。

AllocHGlobalFreeHGlobal関数の命名に混乱しているかもしれません。第一印象に反して、実際にはグローバルメモリの割り当てと解放を行いません。少なくとも、マシン上で実行されているすべてのプロセスがグローバルにアクセスできるメモリではありません。この名前は、Windows のHGLOBALデータ型に由来します。16 ビット Windows の時代には、すべてのアプリケーションが共通のメモリ空間を共有し、互いのメモリを読み取ることができたので、まさにこれを意味していました。しかし、最近の 32 ビット Windows ではそうではありません。名前は下位互換性のために残されています。今日では事実上同じことHGLOBALを意味します。HLOCAL詳細については、MSDN を参照してください。. しかし、それはほとんど好奇心です。コードを正しく機能させるために、すべてを知って理解する必要はありません。

要点はAllocHGlobal、プロセスのデフォルト ヒープからメモリを割り当て、そのプロセスだけが読み取ることができるということです。したがって、プロセス間でメモリをマーシャリングして、メッセージを受信する他のプロセスからアクセスできるようにする必要があります。もちろん、これを手動で行うこともオプションです。しかし、あまり良いものではありません。正しく理解するのは難しく、ほとんど意味がありません。Tim のコメントが示唆しているように、より簡単なオプションは、マーシャリングを行うWM_COPYDATAmessageを使用することです。このメッセージを使用すると、共有するデータがCOPYDATASTRUCT構造体にパッケージ化されます。

カスタムRF_TESTMESSAGEウィンドウ メッセージをWM_COPYDATA. 必要な構造定義を含むサンプル コードは、pinvoke の Web サイトで入手できます。

このようなもの (警告 — テストもコンパイルもされていません):

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関数を呼び出す必要があります (まだコードでそうしていない場合は見えません)。ウィンドウ メッセージは一意です。

于 2013-08-13T16:07:27.150 に答える