2012-10-05 9 views
8

Von einem C# -Programm möchte ich WM_COPYDATA mit SendMessage für die Kommunikation mit einer älteren C++/CLI MFC-Anwendung verwenden.C# zu C++ - Prozess mit WM_COPYDATA übergeben struct mit Zeichenfolgen

Ich möchte eine verwaltete Struktur übergeben, die String-Objekte enthält.

Ich kann das Handle auf die C++ - Anwendung für die Verwendung mit SendMessage in Ordnung finden.

Das Bit, über das ich nichts weiß, ist, wie die Struktur und die Strings am anderen Ende gerastert und gelesen werden können. Zumal es Nicht-Blitables enthält.

Denken die Leute, dass dies machbar ist? Ich werde weiter daran arbeiten, aber würde jemanden schätzen, der so etwas getan hat, mir zu sagen, ob es einfach nicht funktionieren wird.

Hier ist ein Demo-Code, wenn es ein C++/CLI-Programm war und es nicht schwer ist, es zum Laufen zu bringen. Ich möchte jedoch, dass dies in einer .Net-Klassenbibliothek ist, so dass es leicht wiederverwendet werden kann.

//Quick demonstation code only, not correctly styled 
int WINAPI WinMain(HINSTANCE hInstance, 
       HINSTANCE hPrevInstance, 
       LPSTR lpCmdLine, 
       int nCmdShow) 
{    
    struct MessageInfo 
    { 
     int  nVersion; 
     char szTest[ 10 ];   
    }; 

    MessageInfo sMessageInfo; 

    sMessageInfo.nVersion = 100; 
    strcpy(sMessageInfo.szTest, "TEST"); 

    COPYDATASTRUCT CDS; 

    CDS.dwData = 1; //just for test 
    CDS.cbData = sizeof(sMessageInfo); 
    CDS.lpData = &sMessageInfo; 

    //find running processes and send them a message 
    //can't just search for "MYAPP.exe" as will be called "MYAPP.exe *32" on a 64bit machine 
    array<System::Diagnostics::Process^>^allProcesses = System::Diagnostics::Process::GetProcesses(); 

    for each (System::Diagnostics::Process^ targetProcess in allProcesses) 
    {   
     if (targetProcess->ProcessName->StartsWith("MYAPP", System::StringComparison::OrdinalIgnoreCase)) 
     { 
      HWND handle = static_cast<HWND>(targetProcess->MainWindowHandle.ToPointer()); 

      BOOL bReturnValue = SendMessage(handle, WM_COPYDATA, (WPARAM)0, (LPARAM)&CDS) == TRUE; 
     } 
    } 

    return 0; 
} 

Antwort

9

Ich habe es funktioniert.

Ein einfacher Ansatz besteht darin, die Struktur zu einer einzigen Zeichenfolge zu serialisieren und eine Zeichenfolge zu übertragen. Das Swhistlesoft-Blog war hilfreich http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

Dies kann genug sein, um die einfache Messaging bereitzustellen. Die Struktur kann bei Bedarf am anderen Ende neu konstruiert werden.

Wenn eine Struktur mit einer beliebigen Anzahl von Strings unverändert verwaltet werden soll, muss sie eine feste Größe haben, das ist die Hauptsache, die ich nicht bekommen habe. Die

MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 9) 

setzt im wesentlichen die Größe, um das C++ Format anzupassen, das in unserem Fall ein TCHAR szTest [9];

Um einen zu übertragen.Net-Struktur über WM_COPYDATA von C# C++ (/ cli) I wie folgt zu tun hatte:

[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 
    static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); 

    [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)] 
    static extern bool SetForegroundWindow(IntPtr hWnd); 

public static uint WM_COPYDATA = 74; 

//from swhistlesoft 
public static IntPtr IntPtrAlloc<T>(T param) 
    { 
     IntPtr retval = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(param)); 
     System.Runtime.InteropServices.Marshal.StructureToPtr(param, retval, false); 
     return (retval); 
    } 

//from swhistlesoft 
    public static void IntPtrFree(IntPtr preAllocated) 
    { 
     if (IntPtr.Zero == preAllocated) throw (new Exception("Go Home")); 
     System.Runtime.InteropServices.Marshal.FreeHGlobal(preAllocated); 
     preAllocated = IntPtr.Zero; 
    } 

    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] 
    struct COPYDATASTRUCT 
    { 
     public uint dwData; 
     public int cbData; 
     public IntPtr lpData; 
    } 

    /// <summary> 
    /// Dot net version of AppInfo structure. Any changes to the structure needs reflecting here. 
    /// struct must be a fixed size for marshalling to work, hence the SizeConst entries 
    /// </summary> 
    [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential, Pack = 1)] 
    struct AppInfoDotNet 
    { 
     public int nVersion;    

     [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValTStr, SizeConst = 9)] 
     public string test; 
    }; 

eine Zeichenfolge zu senden:

COPYDATASTRUCT cd = new COPYDATASTRUCT(); 
    cd.dwData = 2; 

    cd.cbData = parameters.Length + 1; 
    cd.lpData = System.Runtime.InteropServices.Marshal.StringToHGlobalAnsi(parameters); 

    IntPtr cdBuffer = IntPtrAlloc(cd); 

    messageReceived = ((int)SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, cdBuffer)) != 0; 

Um Zeichenfolge in c zu erhalten ++:

else if(pCDS->dwData == 2) 
    { 
     //copydata message 
     CString csMessage = (LPCTSTR)pCDS->lpData; 
     OutputDebugString("Copydata message received: " + csMessage); 
    } 

  AppInfoDotNet appInfo = new AppInfoDotNet(); 
      appInfo.test = "a test"; 

      COPYDATASTRUCT cds3; 
      cds3.dwData = 1; 
      cds3.cbData = System.Runtime.InteropServices.Marshal.SizeOf(appInfo); 

      IntPtr structPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(appInfo)); 
      System.Runtime.InteropServices.Marshal.StructureToPtr(appInfo, structPtr, false); 

      cds3.lpData = structPtr; 

      IntPtr iPtr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(System.Runtime.InteropServices.Marshal.SizeOf(cds3)); 
      System.Runtime.InteropServices.Marshal.StructureToPtr(cds3, iPtr, false); 

      messageReceived = ((int)SendMessage(targetProcess.MainWindowHandle, WM_COPYDATA, IntPtr.Zero, iPtr)) != 0; 

      System.Runtime.InteropServices.Marshal.FreeCoTaskMem(iPtr); 
      System.Runtime.InteropServices.Marshal.FreeCoTaskMem(structPtr); 

Erneutes:

Um struct zu senden ceive struct in C++:

LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam) 
{ 
    LRESULT lResult = FALSE; 

    COPYDATASTRUCT *pCDS = (COPYDATASTRUCT*)lParam; 

    //Matching message type for struct 
    if(pCDS->dwData == 1) 
    { 
     AppInfo *pAppInfo = (AppInfo*)pCDS->lpData 
     lResult = true; 
    } 

Bitte beachten Sie, diese Demo-Code ist und Bedürfnisse arbeiten in Bezug auf Design, Ausnahmebehandlung etc, etc ...

+0

Dank. Für das Marshalling der benutzerdefinierten Struktur in WM_COPYDATA war Ihr Beispiel das einzige, das auf dieser Site gefunden wurde. –

1

Aus der Dokumentation:

Die Daten weitergegeben werden dürfen keine Zeiger oder andere Verweise auf Objekte, die nicht zugänglich für die Anwendung enthalten die Daten zu empfangen.

Sie müssen also Ihre Zeichenfolge in COPYDATASTRUCT.lpData packen. Wenn Sie eine maximale Länge für jede Saite, dann können Sie es in einer festen Länge Struktur einzubetten

typedef struct tagMYDATA 
{ 
    char s1[80]; 
    char s2[120]; 
} MYDATA; 

Wenn Sie nur eine Zeichenkette variabler Länge haben, können Sie die Zeichenfolge am Ende setzen und einen Header von String-Daten gefolgt verwenden

typedef struct tagMYDATA 
{ 
    int value1; 
    float value2; 
    int stringLen; 
} MYDATAHEADER; 

MyCDS.cbData = sizeof(MYDATAHEADER)+(int)stringData.size(); 
MyCDS.lpData = new BYTE[MyCDS.cbData]; 
memcpy(MyCDS.lpData,&dataHeader,sizeof*(MYDATAHEADER); 
StringCbCopyA (
    ((BYTE*)MyCDS.lpData)+sizeof*(MYDATAHEADER) 
    ,stringData.size() 
    ,stringData.c_str()); 

Wenn Sie mehrere Zeichenfolgen variabler Länge haben, können Sie noch einen Header verwenden und mehr Räume für alle Strings plus ein Doppel-null-Terminator zuweisen, oder alles in einem XML-String serialisiert.