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

后端 未结 2 1485
伪装坚强ぢ
伪装坚强ぢ 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:27

    From the documentation:

    The data being passed must not contain pointers or other references to objects not accessible to the application receiving the data.

    So you need to pack your string into COPYDATASTRUCT.lpData. If you have a max length for each string then you can embed it in a fixed length structure

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

    If you have only one variable length string you can put the string at the end and use a header followed by string data

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

    If you have multiple variable length strings you can still use a header and allocate more spaces for every strings plus a double null terminator, or serialize everything into one XML string.

    0 讨论(0)
  • 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>(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;
        };
    

    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...

    0 讨论(0)
提交回复
热议问题