C# to C++ process with WM_COPYDATA passing struct with strings

后端 未结 2 1525
伪装坚强ぢ
伪装坚强ぢ 2021-02-08 03:48

From a c# program I want to use WM_COPYDATA with SendMessage to communicate with a legacy c++/cli MFC application.

I want to pass a managed struct containing string obje

2条回答
  •  野的像风
    2021-02-08 04:30

    I have it working.

    A simple approach is to serialize the struct to a single string and transfer a string. The swhistlesoft blog was helpful http://www.swhistlesoft.com/blog/2011/11/19/1636-wm_copydata-with-net-and-c

    This may be enough to provide the simple messaging. The struct can be re-constructed at the other end if necessary.

    If a struct with any number of strings is to be marshalled as-is then it must be a fixed size, that's the main thing I wasn't getting. The

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

    basically sets the size to match the c++ size which in our case is a TCHAR szTest[ 9 ];

    In order to transfer a .Net struct via WM_COPYDATA from c# to c++(/cli) I had to do as follows:

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

    To send a string:

        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;
    

    To receive string in c++:

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

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

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

    Please note this is demo code and needs work in terms of styling, exception handling etc, etc...

提交回复
热议问题