2012-10-05 62 views
8

从C#程序我希望使用WM_COPYDATA与SendMessage与传统的c + +/cli MFC应用程序进行通信的WM_COPYDATA传递结构与字符串

我想传递一个包含字符串对象的托管结构。

我可以找到用于SendMessage的C++应用程序的句柄。

我不知道的一点是结构和它的字符串如何被整理和读取到另一端。特别是因为它包含非blittables。

难道人们认为这是可行的吗? 我会继续努力的,但会有人知道做过这种事情的人会告诉我它是否不起作用。

这是一些演示代码,如果它是一个c + +/cli程序,并不难得到它的工作。 但是,我希望这是在.Net类库中,因此它可以很容易地重新使用。

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

回答

9

我让它工作。

一种简单的方法是将结构序列化为单个字符串并传输字符串。 swhistlesoft博客很有帮助http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

这可能足以提供简单的消息传递。 如果需要,可以在另一端重新构造该结构。

如果一个具有任意数量字符串的结构将被编组为原样,那么它必须是一个固定大小,这是我没有得到的主要东西。 的

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

基本上设置大小,以匹配C++大小这在我们的情况下是一个TCHAR szTest [9];

为了转移一个。通过WM_COPYDATA从C#和C++(/ CLI)净结构我不得不这样做如下:

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

要发送的字符串:

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; 

要接收的字符串在C++:

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

要重新在C++ ceive结构:

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

请注意,这是演示代码和需求在车型方面的工作,异常处理等,等......

+0

感谢。为了在WM_COPYDATA中封送自定义结构,你的例子是在这个网站上唯一的工作。 –

1

从文档:

被传递的数据必须不包含指针或其他对象的引用的应用程序接收到所述数据不可访问。

所以你需要将你的字符串打包到COPYDATASTRUCT.lpData中。如果为每个串的最大长度,那么您可以在一个固定长度的结构将其嵌入

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

如果只有一个可变长度的字符串就可以把串在端部,并使用一个标题,随后是字符串数据

​​

如果您有多个可变长度的字符串,你仍然可以使用一个头和每个字符串加双空终止分配更多的空间,或序列一切都变成一个XML字符串。

相关问题