【问题标题】:Calling C# function from a C++/CLI function从 C++/CLI 函数调用 C# 函数
【发布时间】:2015-05-29 21:26:21
【问题描述】:

我的项目要求我使用 C# 为 C++ 提供用户界面。我调用的其中一个 C++ 函数做了很多工作,并通过另一个“对象”提供定期进度更新。这是我的意思的一个例子。

C++

class AppDelegate : public ProgressDelegate
    {
    void AppDelegate::UpdateStatusText(const char* text)
        {
        // Go() will end up calling me at some point.
        OutputDebugString(text);
        }
    void AppDelegate::ShowMessage(const char* text)
        {
        // Go() will end up calling me at some point.
        OutputDebugString(text);
        }
     };

int CppWrapper::Go()
    {
    return cppInstance->Go()
    }

CSharp

void UpdateStatusText(String text)
   {
   //update UI
   }
void ShowMessage(String text)
   {
   //update UI
   }

我想要做的是获取 updateStatusText 和 ShowMessage 并将文本传递给 C# 以更新我的 UI。我的问题是如何公开适当的 C# 方法,以便我的 C++ 代码可以调用它们?请注意,修改 Go 对我来说不是一个选项。

【问题讨论】:

    标签: c# c++ c++-cli interop marshalling


    【解决方案1】:

    也许这个例子可以帮助你:

    编写托管 DLL

    要创建一个简单的托管 DLL,该 DLL 具有将两个数字相加并返回结果的公共方法,请执行以下步骤:

    启动 Microsoft Visual Studio .NET 或 Microsoft Visual Studio 2005。 在文件菜单上,指向新建,然后单击项目。新建项目对话框打开。 在项目类型下,单击 Visual C# 项目。

    注意在 Visual Studio 2005 中,单击项目类型下的 Visual C#。 在模板下,单击类库。 在名称文本框中,键入 sManagedDLL,然后单击确定。 在代码视图中打开 Class1.cs 文件。 要声明具有添加两个数字的方法的公共接口,请将以下代码添加到 Class1.cs 文件:

    // Interface declaration.
    public interface ICalculator
    {
        int Add(int Number1, int Number2);
    };
    

    要在类中实现此公共接口,请将以下代码添加到 Class1.cs 文件中:

    // Interface implementation.
    public class ManagedClass:ICalculator
    {
        public int Add(int Number1,int Number2)
            {
                return Number1+Number2;
            }
    }
    

    注册托管 DLL 以与 COM 或本机 C++ 一起使用 要将托管 DLL 与 COM 或本机 C++ 一起使用,您必须在 Windows 注册表中注册 DLL 的程序集信息。为此,请按以下步骤操作:

    从本机 C++ 代码调用托管 DLL

    // Import the type library.
    #import "..\ManagedDLL\bin\Debug\ManagedDLL.tlb" raw_interfaces_only
    

    如果您计算机上的路径与此路径不同,请更改类型库的路径。 要声明要使用的命名空间,请将以下代码添加到 CPPClient.cpp 文件中:

    using namespace ManagedDLL;
    

    完整的代码清单

      //Managed DLL
      // Class1.cs
      // A simple managed DLL that contains a method to add two numbers.
      using System;
    
      namespace ManagedDLL
      {
        // Interface declaration.
          public interface ICalculator
          {
              int Add(int Number1, int Number2);
          };
    
          // Interface implementation.
        public class ManagedClass:ICalculator
        {
             public int Add(int Number1,int Number2)
                  {
                      return Number1+Number2;
                  }
        }
      }
    
    
      //C++ Client
      // CPPClient.cpp: Defines the entry point for the console application.
      // C++ client that calls a managed DLL.
    
      #include "stdafx.h"
      #include "tchar.h"
      // Import the type library.
    
      #import "..\ManagedDLL\bin\Debug\ManagedDLL.tlb" raw_interfaces_only
      using namespace ManagedDLL;
      int _tmain(int argc, _TCHAR* argv[])
      {
          // Initialize COM.
          HRESULT hr = CoInitialize(NULL);
    
          // Create the interface pointer.
          ICalculatorPtr pICalc(__uuidof(ManagedClass));
    
          long lResult = 0;
    
          // Call the Add method.
          pICalc->Add(5, 10, &lResult);
    
          wprintf(L"The result is %d\n", lResult);
    
    
          // Uninitialize COM.
          CoUninitialize();
          return 0;
      }
    

    参考: How to call a managed DLL from native Visual C++ code in Visual Studio.NET or in Visual Studio 2005

    【讨论】:

    • 为什么需要接口类?可以正常上课吗?
    【解决方案2】:

    另外,我曾经做的(在我切换到使用 P/Invoke 方法从 C# 调用到 C++ 之前)是有 3 个项目(正如 StraightLine 所提到的),但我有 C#、托管 C++ 和 Native C++,并让托管 C++ 成为我在两者(本机 C++ 和 C#)之间交谈的桥梁/代理。这样一来,在我的 Native C++ 端工作变得更加容易。一个警告是托管不支持某些 STL(主要是容器),或者有时,std::string(托管 C++ 版本)的行为在 Native C++ std::string 中使用时会导致异常,因此请注意哪个STL 库受托管 C++ 支持。

    另外,正如 StraightLine 所提到的,桥接代码(在我的例子中是 Managed C++)必须有一个包装器,它将从 Managed 编组到 Native,反之亦然(即你的“const char*”到 System.String,如果您的 char 是 8 位等)

    【讨论】:

    • 您是如何在您的设置中从 C++ 调用到 C# 的?您必须将 C# 项目构建为可执行文件,而不是库。那么你如何从其他地方引用它呢? OP 说他的 GUI 是用 C# 编写的,所以他应该将他的 C# 代码构建成可执行文件
    【解决方案3】:

    您必须将相关的 C# 代码构建到程序集中,然后在 C++ CLI 项目中引用该程序集。在您的 C++ CLI 包装器中,您将调用通过此 C# 程序集公开的函数,您还将调用本机 C++ 代码。

    C++ CLI 项目可以包含本机 C++ 代码,只需确保未为本机文件启用 CLR 开关。所以,你会有 3 个项目 -

    1. 用于 GUI 的 C# 项目将调用 main。还引用了用于公开要通过 C++ 互操作调用的 C# 函数的程序集。
    2. 公开某些托管函数的 C# 程序集(因此可从 C++ CLI 调用)
    3. C++ CLI 项目将包装并包含本机代码。

    【讨论】: