Mattias Nykkel Mattias Nykkel - 9 days ago 5
C++ Question

Submitting C++ callback to .NET C++/CLI Wrapper

I've been searching the web for a solution to the following problem but cannot get my head around it.

We have a big monolithic application med in C++. To "upgrade" it to the new world we embed WPF views in it generated in a C++/CLI managed wrapper. The initial call is made from C++ via a smartpointer.

if (SUCCEEDED(ptr.CreateInstance(__uuidof(CloudServices))))
{
CRect rect;
pWndContainer->GetWindowRect(rect);

ptr->InitializeControl((LONG)pWndContainer->GetSafeHwnd(), ID_AIS_BALANCEMATRIX, bstr.m_str, rect.Width(), rect.Height());
}


And in the wrapper class the interface is declared like

interface ICloudServices : IDispatch{
[id(1)] HRESULT InitializeControl([in] LONG hWndParent, [in] LONG controlTypeId, [in] BSTR parameters, [in] LONG width, [in] LONG height);


And is implemented like this in the wrapper

STDMETHODIMP CCloudServices::InitializeControl(LONG hWndParent, LONG controlTypeId, BSTR parameters, LONG width, LONG height) { ... }


Problem:
Everything works fine and the wpf view is rendered within the C++ app. Now we need to send information back to C++ from the .NET code.

How can I submit an unmanaged callback function to the wrapper as an argument to the InitializeControl and how do I use/convert it to a relevant .net delegate?

See desired solution schematic

Answer

We did not seem to find a working solution to this problem so we ended up with sending the information back using WM_DATACOMPY. Not the most effecient way but it works.

    public void SendCommand(Command command)
    {
        // Find the target window handle.
        IntPtr hTargetWnd = NativeMethod.FindWindow("OurAppNameInCpp", null);
        if (hTargetWnd == IntPtr.Zero)
        {
            throw new Exception("Sending '{0}'. Unable to find the \"HogiaAuditAPP\" window".Args(command.AsXmlMessage));
        }

        // Prepare the COPYDATASTRUCT struct with the data to be sent.
        MyStruct myStruct;

        myStruct.Message = command.AsXmlMessage;

        // Marshal the managed struct to a native block of memory.
        int myStructSize = Marshal.SizeOf(myStruct);
        IntPtr pMyStruct = Marshal.AllocHGlobal(myStructSize);
        try
        {
            Marshal.StructureToPtr(myStruct, pMyStruct, true);

            COPYDATASTRUCT cds = new COPYDATASTRUCT();
            cds.cbData = myStruct.Message.Length + 1;
            cds.lpData = Marshal.StringToHGlobalAnsi(myStruct.Message);

            // Send the COPYDATASTRUCT struct through the WM_COPYDATA message to 
            // the receiving window. (The application must use SendMessage, 
            // instead of PostMessage to send WM_COPYDATA because the receiving 
            // application must accept while it is guaranteed to be valid.)
            NativeMethod.SendMessage(hTargetWnd, WM_COPYDATA, IntPtr.Zero, ref cds);

            int result = Marshal.GetLastWin32Error();
            if (result != 0)
                throw new Exception("SendMessage(WM_COPYDATA) failed w/err 0x{0:X}".Args(result));
        }
        finally
        {
            Marshal.FreeHGlobal(pMyStruct);
        }
    }