【问题标题】:Exposing a C++ API to C#向 C# 公开 C++ API
【发布时间】:2011-02-06 01:35:09
【问题描述】:

所以我有一个包含在 *.dll 中的 C++ API,我想使用 C# 应用程序来调用 API 中的方法。

到目前为止,我已经创建了一个包含本机 C++ API 的 C++/CLR 项目,并设法创建了一个类似于以下内容的“桥”类:

// ManagedBridge.h
#include <CoreAPI.h>
using namespace __CORE_API;

namespace ManagedAPIWrapper
{
    public ref class Bridge
    {
        public:
            int             bridge_test(void);
            int             bridge_test2(api_struct* temp);
    }
}

.

// ManagedBridge.cpp
#include <ManagedBridge.h>

int Bridge::bridge_test(void)
{
    return test();
}

int Bridge::bridge_test2(api_struct* temp)
{
    return test2(temp);
}

我还有一个 C# 应用程序,它引用了 C++/CLR“Bridge.dll”,然后使用其中包含的方法。我对此有很多问题:

  1. 我不知道如何在 C# 程序中调用 bridge_test2,因为它不知道 api_struct 究竟是什么。我知道我需要在某处编组对象,但我是在 C# 程序还是在 C++/CLR 桥中进行?
  2. 这似乎是一种非常冗长的公开 API 中所有方法的方法,难道没有更简单的方法让我错过了吗? (这不使用 P/Invoke!)

编辑:好的,由于下面的回复,我现在已经掌握了基础知识,但是我的结构(在本例中称为“api_struct2”)在C++ 原生代码,如下所示:

typedef struct
{
    enum_type1  eEnumExample;
    union
    {
            long        lData;
            int     iData;
            unsigned char   ucArray[128];
            char        *cString;
            void        *pvoid;
    } uData;
} api_struct2;

我想我已经知道如何让枚举工作了;我已经在托管代码中重新声明了它,并且正在执行“native_enum test = static_cast(eEnumExample)”以将托管版本切换为本地版本。

然而工会让我难住了,我真的不知道如何攻击它.. 有什么想法吗?

【问题讨论】:

    标签: c# c++ interop clr marshalling


    【解决方案1】:

    因为它不知道 api_struct 实际上是什么

    您需要在 .NET 程序集中定义一个托管版本,该版本使用属性(如 StructLayoutAttribute)来确保其正确编组。

    这似乎是一个非常冗长的 [...]

    另一种方法是围绕您的 API 创建一个 COM 包装器(例如使用 ATL)。这可能会更费力,但至少您避免了 P/Invoke 所需的结构和函数定义的双重编码。

    更正:您已经创建了一个 C++/CLI 项目:所以只需添加正确的 '#pragma' 来告诉编译器这是 .NET 代码,然后是输出是 C# 项目可以引用的程序集。

    【讨论】:

    • 添加#pragma 对我有什么帮助?有什么方法可以将所有编组放在 C++/CLI 项目中,以便每个引用它的项目都不必重新声明等效的 .NET 结构/类?
    • @Siyfion: #pragma managed 允许切换进出托管代码。见msdn.microsoft.com/en-us/library/0adb9zxe(VS.100).aspx
    • 我明白这一点,但它目前有效,所以我看不出添加它可以让我做什么!?
    【解决方案2】:

    是的,您通过引用传递了一个非托管结构。这对 C# 程序来说是个问题,指针与垃圾收集完全不兼容。不算它可能也没有结构声明的事实。

    您可以通过声明结构的托管版本来解决它:

    public value struct managed_api_struct {
      // Members...
    };
    

    现在您可以将方法声明为

    int bridge_test2(managed_api_struct temp);   // pass by value
    

    int bridge_test2(managed_api_struct% temp);  // pass by reference
    

    如果结构有超过 4 个字段(约 16 个字节),请选择后者。该方法需要将结构成员一一复制到非托管的 api_struct 中,并调用非托管类方法。不幸的是,这是必要的,因为托管结构的内存布局是不可预测的。

    这一切都非常机械,您可能会得到帮助from SWIG。自己没用过,不知道它是否足够聪明来处理传递的结构。

    一种完全不同的方法是通过为包装类提供构造函数和/或属性来让您构建 api_struct 的内容,从而使包装类更简洁。或者你可以为结构声明一个包装器引用类,就像在托管代码中一样。

    【讨论】:

    • 这是否意味着我在 C++/CLI *.dll 或 C# 代码本身中创建 managed_api_struct?另外,我认为只要使用 StructLayout 属性,就可以将托管结构传递给本机代码?
    • 没关系,但是 C++/CLI 避免循环依赖是有意义的。是的,[StructLayout] 有效,但您必须使用 Marshal::StructureToPtr() 调用。托管结构的布局是不可预测的。
    • 我在问题中添加了一个额外的“编辑:...”,如果您能给我您的想法,我将不胜感激?谢谢!
    • 您通常不能在托管代码中使用联合,这是一个根本不安全的结构。没有必要,托管结构不必类似于非托管结构。
    【解决方案3】:

    Yo 正试图以比实际情况更复杂的方式进行操作。你想要的是 两个 不同的结构。一个托管一个非托管。您在外部(向您的 C# 应用程序)公开托管版本。它将完全是“.Net-ish”,没有工会的概念。

    在您的网桥中,您会收到结构的托管版本,手动创建非托管结构并编写代码以逐个字段地将数据移动到非托管结构。然后调用您的非托管代码,最后将数据移回托管结构。

    C++/CLI 的美妙之处在于托管代码也可以处理非托管代码和数据(并包括非托管 .h 文件)。

    【讨论】:

      猜你喜欢
      • 2010-09-21
      • 2011-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-02
      • 2013-10-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多