使用C#编写GUI覆盖在C++ dll或C++ exe上

7
我有一个用C++编写的控制台应用程序,它执行一些编程任务。现在我想编写一个C# GUI应用程序,以便执行部分与C++应用程序相同的编程任务。我考虑了几种方法:
  1. 从头开始使用C++编写所有编程内容的C# GUI。(由于需要重新工作的数量,我不想这样做)
  2. 构建一个C++ DLL来执行编程任务,并将其导入GUI应用程序。(现在我有一个问题。如何捕获C++ DLL中例程的输出并在GUI中显示?我是否应该为应用程序调用的每个例程返回字符串输出?由于我不知道托管C ++,所以我将构建一个非托管C++ DLL。)

你是否考虑过添加COM接口?在我看来,使用COM容器传递参数和程序数据可能是一种解决方案。 - baris.aydinoz
6个回答

7
构建一个C++/CLI dll并不难。您基本上使用非托管的C++代码,除了定义一个“public ref class”,其中包含您希望C#代码看到的函数。
您要返回什么类型的数据?单个数字、数字矩阵、复杂对象?
更新:由于已经澄清“输出”是iostreams,这里有一个项目演示如何将cout重定向到调用库的.NET应用程序中。重定向clogcerr只需要在现有重定向的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)); }

我想在C# GUI中显示来自C++ DLL的日志消息。 - excray
@user97642:所以C++ DLL正在写入std::clog,您想将此输出发送到C#? - Ben Voigt
@user97642:好的,它们被设计得非常灵活。我有一些cout拦截代码,可以重新编写以将流发送到.NET,但这需要一些时间,而且现在已经过了午夜,所以我现在无法做到这一点。 - Ben Voigt

3

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

那么你需要在 c++ 中构建一个 COM 对象或一个可通过 p/Invoke 调用的库。每种方法都有其自己的优缺点,具体取决于你的业务需求。你需要在消费者中进行数据编组。关于这两个概念有大量的资料。


0

你可以像选项2中建议的那样编写一个C# GUI包装器并生成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);
    }
}

我正在寻找一种解决第二个选项的方法。使用C++ DLL的C# GUI。 - excray

0

在这里最好的方法可能是使用P/Invoke或平台调用。根据您的C++ dll接口的结构,您可能希望将其包装在纯C接口中;如果您的接口仅使用可平板类型,则最容易。如果您将dll接口限制为可平板类型(Int32、Single、Boolean、Int32[]、Single[]、Double[]-基础知识),则不需要在托管(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# 代码。
语言交互有许多细节,但这应该可以帮助您入门。如果可能的话,坚持使用可混合类型的无状态 C 接口是一个很好的策略。这将使你的语言交互代码保持最干净。
祝好运,
保罗


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接