从 C++/CLI 函数调用 C# 函数

Calling C# function from a C++/CLI function

我的项目要求我使用 C# 为 C++ 提供用户界面。我调用的其中一个 C++ 函数完成了一系列工作,并通过另一个函数提供定期进度更新 "object." 下面是我的意思的示例。

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++ CLI 项目中引用该程序集。在 C++ CLI 包装器中,您将调用通过此 C# 程序集公开的函数,还将调用本机 C++ 代码。

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

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

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

编写托管 DLL

要创建一个简单的托管 DLL,它有一个 public 方法来添加两个数字和 return 结果,请按照下列步骤操作:

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

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

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

要在 class 中实现此 public 接口,请将以下代码添加到 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

或者,我曾经做的(在我切换到使用 P/Invoke 方法从 C# 调用到 C++ 之前)是有 3 个项目(如 StraightLine 所提到的)但我有 C#,托管C++ 和本机 C++,并让托管 C++ 成为我 bridge/proxy 在两者(本机 C++ 和 C#)之间进行对话。这样可以更轻松地在我的本机 C++ 端工作。需要注意的是,某些 STL(主要是容器)不受托管支持,或者有时 std::string(托管 C++ 版本)的行为在本机 C++ std::string 中使用时会导致异常,因此请注意支持托管 C++ 的 STL 库。

此外,正如 StraightLine 所提到的,桥接代码(在我的例子中是托管 C++)必须有一个包装器,它将从托管到本地编组,反之亦然(即你的 "const char*" 到 System.String,如果你的字符是 8 位等)