在C ++ DLL或C ++ exe上编写C#GUI

时间:2011-01-14 04:10:25

标签: c# c++ dll

我有一个C ++控制台Exe,它可以进行一些编程。现在我想编写一个C#GUI来完成C ++ exe所做的一些编程。我想的方法很少,

  1. 用C ++中的所有编程从头开始编写C#GUI。(我不想为它所需的返工量做这件事)
  2. 构建一个C ++ dll,它完成编程并在GUI应用程序中导入。(现在我有一个问题。如何在c ++ dll中捕获例程的输出并在GUI中显示它?我应该返回输出作为应用程序调用的每个例程的字符串。?因为我不知道托管c ++我将构建一个非托管C ++ DLL。)

6 个答案:

答案 0 :(得分:7)

构建C ++ / CLI dll确实不是那么难。您基本上使用非托管C ++代码,除了您定义了一个“public ref class”,它承载了您希望C#代码看到的函数。

您要返回什么样的数据?单个数字,数字矩阵,复杂对象?

更新:由于已经澄清“输出”是iostreams,here is a project演示了cout重定向到调用库的.NET应用程序。重定向clogcerr只需要在现有重定向后DllMain图案中添加一些额外的行。

The zip file包含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)); }

答案 1 :(得分:3)

所以你只想从托管的.net代码调用c ++库?

然后你需要在c ++中构建一个COM对象或一个p-invokable库。每种方法都有自己的优点和缺点,具体取决于您的业务需求。您必须在消费者中编组数据。这两个概念都有大量的材料。

答案 2 :(得分:0)

答案 3 :(得分:0)

您可以编写一个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);
    }
}

答案 4 :(得分:0)

请参阅the first comment in this page重定向stderr

答案 5 :(得分:0)

可能最好的方法是使用P / Invoke或Platform Invoke。根据结构或C ++ DLL接口,您可能希望将其包装在纯C接口中;如果您的界面仅使用blittable类型,则最简单。如果将dll接口限制为blittable类型(Int32,Single,Boolean,Int32 [],Single [],Double [] - 基础),则无需在托管之间进行任何复杂的数据封送处理em>(C#)和非托管(C)内存空间。

例如,在c#代码中,使用DllImport属性定义C / C ++ dll中的可用调用。

[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垃圾收集器可能会更改这些数组的位置,但C / C ++ DLL将不知道它。所以为了防止这种情况,我只是在C#中修改了这些指针,现在当我的dll在这些数组上运行时,它们不会在内存中移动。这是现在不安全的代码,我需要编译带有该标志的C#代码。

语言互操作有许多精细的细节,但这应该让你滚动。如果可能的话,坚持使用blittable类型的无状态C接口是一个很好的策略。这将使您的语言互操作代码保持最清晰。

祝你好运,