2012-10-05 10 views
8

Z programu C# chcę używać WM_COPYDATA z SendMessage do komunikacji ze starszą aplikacją C++/cli MFC.Proces C# do C++ z WM_COPYDATA przekazującą struct z ciągami znaków

Chcę przekazać zarządzaną strukturę zawierającą obiekty ciągów.

Mogę znaleźć uchwyt aplikacji C++ do użycia z grzywną SendMessage.

Bit, o którym nie wiem, to to, w jaki sposób struktura i jej łańcuchy mogą zostać zebrane i odczytane na drugim końcu. Zwłaszcza, że ​​zawiera nieryśne składniki.

Czy ludzie myślą, że jest to wykonalne? Nadal będę nad tym pracował, ale pochwalę kogoś, kto zrobił coś takiego, mówiąc mi, że to nie zadziała.

Oto kilka kodu demo, jeśli był to program C++/cli i nie jest trudno go uruchomić. Jednak chciałbym, aby ta biblioteka była w bibliotece .Net, więc można ją łatwo ponownie wykorzystać.

//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; 
} 

Odpowiedz

9

Mam to działa.

Proste podejście polega na serializacji struktury do pojedynczego ciągu znaków i przesłaniu ciągu znaków. Blog swhistlesoft był pomocny http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

Może to wystarczyć, aby zapewnić prostą komunikację. Strukturę można ponownie skonstruować na drugim końcu, jeśli to konieczne.

Jeśli struktura z dowolną liczbą łańcuchów ma być tak samo ustawiona, jak to jest, to musi mieć stały rozmiar, to jest główna rzecz, której nie otrzymałem.

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

zasadniczo ustawia rozmiar, aby pasować do rozmiaru C++, który w tym przypadku jest TCHAR szTest [9];

Aby przesłać.struct netto poprzez WM_COPYDATA z C# C++ (/ CLI) musiałem zrobić w następujący sposób:

[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; 
    }; 

Aby wysłać ciąg:

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; 

Aby otrzymywać ciąg w C++:

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

Aby wysłać struct:

  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); 

Aby ponownie ceive struct w 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; 
    } 

Należy pamiętać, jest to kod demo i wymaga pracy w zakresie stylizacji, wyjątek obsługi etc, etc ...

+0

Dzięki. W przypadku niestandardowej struktury kolumny w WM_COPYDATA Twój przykład okazał się jedynym działaniem znalezionym w tej witrynie. –

1

Z dokumentacji:

Dane są przekazywane nie musi zawierać wskaźniki lub inne odniesienia do obiektów nie dostępnych dla aplikacji odbierającej dane.

Musisz więc spakować swój ciąg do pliku COPYDATASTRUCT.lpData. Jeśli masz maksymalną długość dla każdej struny następnie można osadzić je w stałej strukturze długości

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

jeśli masz tylko jedną zmienną długość łańcucha można umieścić napis na końcu i użyć nagłówka następnie danych łańcuchowych

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()); 

Jeśli masz wiele ciągów o zmiennej długości można nadal korzystać z nagłówka i przeznaczyć więcej miejsca dla każdego ciągów plus podwójne zerowy terminatora lub serializacji wszystko w jeden ciąg znaków XML.