Захват stdout из неуправляемой DLL в вызывающей программе C #

У меня есть приложение C #, вызывающее родную C ++ DLL через некоторый код маршаллинга C ++ / CLI:

C # -> C ++ / CLI -> C ++ (без CLR)

Я хочу, чтобы DLL отправляла обновления строк обратно вызывающему приложению во время его работы. В настоящее время неуправляемая DLL записывает вывод в стандартный вывод. По сути, мне нужно зафиксировать этот вывод в пользовательском интерфейсе.

В других случаях, когда я обстреливаю неуправляемый exe, это может быть достигнуто путем простого перенаправления stdout от вызываемого к строковому буферу в данных пользовательского интерфейса, привязанных к текстовой панели.

У меня нет возможности вызвать DLL с помощью P / Invoke или обработать ее как exe, поскольку уровень взаимодействия выполняет существенную сортировку непримитивных типов. Неуправляемая DLL не имеет поддержки среды CLR и должна оставаться такой.

У меня был ограниченный успех с использованием делегатов (http://msdn.microsoft.com/en-us/library/367eeye0%28v=vs.100%29.aspx), поскольку всегда есть место, где управляемый и неуправляемый миры сталкиваются. Взяв пример в ссылке, передача управляемого делегата из C ++ / CLI, маскирующегося под собственный указатель функции на DLL, работает, но обратный вызов определяется вне области действия класса C ++ / CLI и, следовательно, не может получить доступ к управляемому делегату, переданному из вызывающий слой (C #).

В идеале я хотел бы определить класс, который принимает неуправляемые строки и может преобразовывать их в управляемые и вызывать обратно на уровень пользовательского интерфейса, но если этот класс имеет необходимую поддержку управляемых строк, его нельзя передать неуправляемому коду.

Возможно, мне не хватает простого трюка с перенаправлением, который позволил бы захватить stdout без передачи строк между слоями (например, перенаправление stdout из C ++ / CLI, полученного через делегата). Если это невозможно, может ли кто-нибудь предложить альтернативный метод?

Управляемый 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;

    };
}

Заранее спасибо.


person greenback    schedule 05.10.2012    source источник


Ответы (1)


Правильно, это не работает. У DLL нет стандартного вывода, а есть у процесса. И ваш процесс не создает его, поскольку это приложение с графическим интерфейсом, и перенаправление не может работать, поскольку для этого требуется другой процесс.

Решение простое. Убедитесь, что у вас выбрана конфигурация отладки. Щелкните правой кнопкой мыши свой проект, Свойства, Linker, System. Измените параметр «Подсистема» на «Консоль (/ SUBSYSTEM: CONSOLE)» вкладка «Приложение». Измените параметр Тип вывода на «Консольное приложение».

Нажмите F5. Престо, теперь у вас есть окно консоли, в котором отображаются отладочные данные из библиотеки DLL. И ваш обычный графический интерфейс.

person Hans Passant    schedule 06.10.2012
comment
Этот параметр предназначен для приложения C ++ / CLI, не так ли? В вопросе говорится, что приложение (которое вызывает C ++ / CLI DLL) находится на C #. Или работает, даже если выставить в проекте C ++ / CLI DLL? - person svick; 06.10.2012
comment
Спасибо за ответы. Есть ли способ перенаправить stdout с консоли обратно в основное приложение, учитывая архитектуру, описанную выше, то есть без использования класса .Net ShelledProcess или тому подобного? В идеале я хочу иметь возможность постобработки stdout перед отображением в пользовательском интерфейсе, чтобы удалить ненужный текст и добавить различные разметки и другое форматирование. - person greenback; 07.10.2012
comment
Еще одна вещь: было бы лучше полностью скрыть окно консоли и отображать только сообщения о ходе выполнения в пользовательском интерфейсе. - person greenback; 07.10.2012