2012-10-05 20 views
8

Desde un programa C# Quiero usar WM_COPYDATA con SendMessage para comunicarme con una aplicación heredada de C++/cli MFC.Proceso C# a C++ con WM_COPYDATA pasando estructura con cadenas

Quiero pasar una estructura administrada que contiene objetos de cadena.

Puedo encontrar el identificador de la aplicación C++ para usarlo con SendMessage fine.

El bit que no sé es cómo la estructura y sus cadenas se pueden ordenar y leer en el otro extremo. Especialmente porque contiene elementos no blittables.

¿La gente piensa que esto es factible? Continuaré trabajando en ello, pero agradecería a alguien que haya hecho este tipo de cosas diciéndome si simplemente no va a funcionar.

Aquí hay un código de demostración si era un programa C++/cli y no es difícil hacerlo funcionar. Sin embargo, me gustaría que esté en una biblioteca de clases .Net para que pueda ser reutilizado fácilmente.

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

Respuesta

9

Lo tengo trabajando.

Un enfoque simple consiste en serializar la estructura en una sola cadena y transferir una cadena. El blog de swhistlesoft fue útil http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

Esto puede ser suficiente para proporcionar mensajes sencillos. La estructura se puede reconstruir en el otro extremo si es necesario.

Si una estructura con cualquier cantidad de cadenas se va a ordenar como está, entonces debe ser de un tamaño fijo, eso es lo principal que no estaba obteniendo. El

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

básicamente establece el tamaño para que coincida con el tamaño de C++ que en nuestro caso es un szTest TCHAR [9];

Para transferir a.struct red a través de WM_COPYDATA de C# para C++ (/ CLI) que tenía que hacer de la siguiente manera:

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

Para enviar una cadena:

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; 

Para recibir cadena en C++:

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

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

Re cibir struct en 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; 
    } 

Tenga en cuenta que esto es el código de demostración y las necesidades de trabajo en términos de estilo, la gestión de excepciones, etc, etc ...

+0

Gracias. Para organizar estructura personalizada en WM_COPYDATA, su ejemplo fue el único trabajo encontrado en este sitio. –

1

De la documentación:

están aprobando Los datos no deben contener punteros u otras referencias a objetos que no son accesibles a la aplicación que recibe los datos.

Por lo tanto, debe empaquetar la cadena en COPYDATASTRUCT.lpData. Si usted tiene un largo máximo para cada cuerda entonces se puede incrustar en una estructura de longitud fija

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

Si usted tiene sólo una cadena de longitud variable que puede poner la cadena al final y utilizar una cabecera seguida de datos de cadena

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

Si tiene varias cadenas de longitud variable que todavía se puede utilizar un encabezado y asignar más espacios por cada cuerdas más un terminador doble nulo, o serializar todo en una cadena XML.

Cuestiones relacionadas