Написание C # GUI поверх C ++ dll или C ++ exe - PullRequest
7 голосов
/ 14 января 2011

У меня есть консоль C ++ Exe, которая занимается программированием.Теперь я хотел написать C # GUI, который выполняет некоторые программы, которые выполняет C ++ exe.Я думал о нескольких подходах:

  1. Напишите графический интерфейс C # со всеми программами на C ++, выполненными с нуля. (Я не хочу делать это за количество необходимых переделок)
  2. Построить C ++ dll, который выполняет программирование и импортирует его в приложение с графическим интерфейсом. (Теперь у меня есть проблема. Как мне записать выходные данные подпрограмм в c ++ dll и отобразить их в GUI? Должен ли я возвращать вывод в виде строкидля каждой подпрограммы, которую вызывает приложение. Так как я не знаю, что такое управляемый c ++, я собираюсь создать неуправляемую DLL C ++.)

Ответы [ 6 ]

7 голосов
/ 14 января 2011

Создание DLL на C ++ / CLI действительно не так сложно.В основном вы используете неуправляемый код C ++, за исключением того, что вы определяете «public ref class», который содержит функции, которые вы хотите видеть в коде C #.

Какие данные вы возвращаете?Одиночные числа, матрицы чисел, сложные объекты?

ОБНОВЛЕНИЕ: Поскольку выяснилось, что «выходные данные» - это iostreams, - это проект , демонстрирующий перенаправление cout на a.NET приложение, вызывающее библиотеку.Для перенаправления clog или cerr потребуется всего несколько дополнительных строк в шаблоне DllMain, скопированных после существующего перенаправления.

ZIP-файл включает файлы проекта VS2010, но исходный кодтакже должен работать в 2005 или 2008 году.

Функция захвата iostreams содержится в следующем коде:

// compile this part without /clr
class capturebuf : public std::stringbuf
{
protected:
    virtual int sync()
    {
        // ensure NUL termination
        overflow(0);
        // send to .NET trace listeners
        loghelper(pbase());
        // clear buffer
        str(std::string());
        return __super::sync();
    }
};

BOOL WINAPI DllMain(_In_ HANDLE _HDllHandle, _In_ DWORD _Reason, _In_opt_ LPVOID _Reserved)
{
    static std::streambuf* origbuf;
    static capturebuf* altbuf;
    switch (_Reason)
    {
    case DLL_PROCESS_ATTACH:
        origbuf = std::cout.rdbuf();
        std::cout.rdbuf(altbuf = new capturebuf());
        break;
    case DLL_PROCESS_DETACH:
        std::cout.rdbuf(origbuf);
        delete altbuf;
        break;
    }

    return TRUE;
}

// compile this helper function with /clr
void loghelper(char* msg) { Trace::Write(gcnew System::String(msg)); }
3 голосов
/ 14 января 2011

То есть вы просто хотите вызвать библиотеку c ++ из управляемого кода .net?

Тогда вам нужно будет либо создать COM-объект, либо библиотеку p-invokable в c ++. Каждый подход имеет свои плюсы и минусы в зависимости от потребностей вашего бизнеса. Вам придется маршалировать данные в вашем потребителе. В обеих концепциях есть тонны и тонны материала.

0 голосов
/ 22 января 2011

Вероятно, лучший способ для этого - использовать P / Invoke или Platform Invoke. В зависимости от структуры или вашего dll-интерфейса C ++ вы можете обернуть его в чистый C-интерфейс; это проще всего, если ваш интерфейс использует только типы blittable . Если вы ограничите свой dll-интерфейс для blittable типов (Int32, Single, Boolean, Int32 [], Single [], Double [] - основы), вам не нужно будет выполнять какое-либо сложное маршалинг данных между managed (C #) и неуправляемые (C) области памяти.

Например, в вашем коде c # вы определяете доступные вызовы в dll C / C ++, используя атрибут DllImport.

[DllImport, "ExactDllName.dll"]  
static extern boolean OneOfMyCoolCRoutines([In] Double[] x, [In] Double[] y, [Out] Double result)

Маленькие [In] и [Out] не обязательны, но они могут ускорить процесс. Теперь, добавив «ExactDllName.dll» в качестве ссылки на ваш проект C #, вы можете вызывать функцию C / C ++ из своего кода C #.

fixed(Double *x = &x[0], *y = &y[0] )  
{  
   Boolean returnValue = OneOfMyCoolCRoutines(x, y, r);  
}  

Обратите внимание, что я по сути передаю указатели назад и четвертый между моим dll и C # кодом. Это может привести к ошибкам памяти, потому что расположение этих массивов может быть изменено сборщиком мусора CLR, но DLL C / C ++ ничего не будет знать об этом. Поэтому, чтобы защититься от этого, я просто исправил эти указатели в моем C #, и теперь они не будут перемещаться в памяти, пока моя dll работает с этими массивами. Теперь это небезопасный код, и мне нужно скомпилировать мой код C # с этим флагом.

Существует много мелких деталей для взаимодействия языков, но это должно помочь вам. Приверженность интерфейсу C, не поддерживающему состояние, для blittable типов - отличная политика, если это возможно. Это сделает код вашего языкового взаимодействия максимально чистым.

Удачи,

Пол

0 голосов
/ 17 января 2011

См. первый комментарий на этой странице для перенаправления stderr

0 голосов
/ 17 января 2011

Вы можете просто написать оболочку C # GUI (как вы предлагаете в варианте 2) и запустить процесс C ++;однако, это будет немного медленным (я не знаю, имеет ли это значение).

Для запуска вашего C ++ exe и захвата вывода вы можете использовать ProcessRunner , который я собрал.Вот основное использование:

using CSharpTest.Net.Processes;
partial class Program
{
    static int Main(string[] args)
    {
        ProcessRunner run = new ProcessRunner("svn.exe", "update");
        run.OutputReceived += new ProcessOutputEventHandler(run_OutputReceived);
        return run.Run();
    }

    static void run_OutputReceived(object sender, ProcessOutputEventArgs args)
    {
        Console.WriteLine("{0}: {1}", args.Error ? "Error" : "Output", args.Data);
    }
}
0 голосов
/ 14 января 2011

Возможно, актуально: Можно вызвать код C ++ из C #?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...