-->

Capturing stdout from unmanaged DLL in C# caller

2020-07-24 16:26发布

问题:

I have a C# application calling a native C++ DLL via some C++/CLI marshalling code:

C# --> C++/CLI --> C++ (no CLR)

I want the DLL to post string updates back to the calling application while it is running. Currently the unmanaged DLL writes output to stdout. Essentially I need to capture this output in the UI.

In other cases where I am shelling an unmanaged exe this can be achieved by simply redirecting stdout from the callee to a string buffer in the UI data bound to a text panel.

I do not have the option to call the DLL using P/Invoke or shelling it as an exe, since the interop layer performs essential marshalling of non-primitive types. The unmanaged DLL has no CLR support and must remain this way.

I had limited success using delegates (http://msdn.microsoft.com/en-us/library/367eeye0(v=vs.100).aspx), as there always seems to be somewhere that the managed and unmanaged worlds collide. Taking the example in the link, passing a managed delegate from C++/CLI masquerading as a native function pointer to the DLL works, but the callback is defined outside the scope of the C++/CLI class and therefore cannot access a managed delegate passed in from the calling layer (C#).

Ideally I would like to define a class that accepts unmanaged strings, and can convert these to managed and call back into the UI layer, but if this class has the necessary support for managed strings, it cannot be passed to unmanaged code.

I may be missing a simple trick with redirection that would allow stdout to be captured without passing strings between layers (e.g redirecting stdout from C++/CLI that is received via a delegate). If this is not possible can anyone suggest an alternative technique?

Managed C++:

using namespace System::Runtime::InteropServices;
using namespace System;

namespace BusinessObjectInterop
{
    typedef void (__stdcall *UnmanagedFP)(std::string);

    //Callback function
    public delegate void SetProgressDelegate(std::string);  


    void SetProgress(std::string s) {
        Console::WriteLine(s);

        //set m_progress - could use managed delegate passed from UI and exposed in static method in CIntermediate?
    }

    public ref class CIntermediate       
    {
    public:

        //Invoked by managed (C#) UI
        void ^ CallBusinessObject(Object ^ data, String ^ progress)
        {
        //Do some data marshalling...
        //...

        m_progress = progress;      

        //Create wrapper for managed delegate
        SetProgressDelegate^ fp = gcnew SetProgressDelegate(SetProgress);
        GCHandle gch = GCHandle::Alloc(fp);
        IntPtr ip = Marshal::GetFunctionPointerForDelegate(fp);
        UnmanagedFP cb = static_cast<UnmanagedFP>(ip.ToPointer());

        //Call unmanaged DLL
        BusinessObject::DoStuff(cb);
        gch.Free();
        }

    private:
        String ^ m_progress;

    };
}

Thanks in advance.

回答1:

Right, this doesn't work. A DLL doesn't own stdout, a process does. And your process doesn't create one since it is GUI app, nor can redirection work since that requires another process.

The workaround is simple. Ensure you've got the Debug configuration selected. Right-click your project, Properties, Linker, System. Change the SubSystem setting to "Console (/SUBSYSTEM:CONSOLE)" Application tab. Change the Output type setting to "Console application".

Press F5. Presto, now you've got both a console window that displays debugging output from the DLL. And your regular GUI.