如何为非托管c dll创建c ++ \ cli包装器

时间:2014-04-11 13:35:10

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

我可能会让自己感到困惑,但之前没有做过这样的事情,而且一点方向会非常有用。
我试图从C应用程序调用一些C#代码。我尝试过使用PInvoke,但发现它有点棘手。我以为我会尝试做一个C++\CLI包装器。

有一些复杂的结构具有可变长度的双重数组,PInvoke很难处理 我已经读了一下如何做到这一点,但我无法弄明白。我发现的大部分内容与包裹C++而不是C有关。 C代码已经在导出其功能,这些功能已经从Java应用程序及其JNA服务中运行。我有C代码,标题,库和dll,但宁愿不对现有的任何内容进行更改,以免扰乱其他消费应用程序。调用它的C#应用程序将是64位,大多数示例都是创建win32库,这有关系吗?

更新:在下面添加代码:
注意:这只是几个函数中的一个函数,可能是最简单的函数,但它们都非常相似。

C HEADER:
typedef struct myStruct_t
{
    double prefix[8];
    int length;
    double array[1];
}
myStruct;

C:
extern "C" __declspec( dllexport ) myStruct *doSomething(const myStruct *input, double a)
{
    myStruct *output;
    //doSomething
    return output;
}

3 个答案:

答案 0 :(得分:1)

包装C和C ++之间的区别非常小。您需要创建C ++ / CLI类库。然后,您在托管本机代码的托管C ++ ref类中编写函数。

例如,假设DLL导出此函数:

int sqr(int x)

然后在您的类库中,您将包含头文件:

#include <mynativelibrary.h>

您还需要将导入库提供给链接器。

然后你可以公开这个功能。最简单的方法是将函数包装为ref类的静态方法。例如:

public ref class Class1
{
public:
    static int sqr(int x)
    {
        return ::sqr(x);
    }
};

然后,您可以像使用任何其他程序集一样在C#代码中使用此程序集。

答案 1 :(得分:1)

我在VisualStudio 2012中创建了一些项目,它们使用托管代码包装旧的MFC dll。我是这样做的:

  1. 在CLR中创建一个类库。
  2. 将旧项目链接到此新项目
  3. 在新项目中为函数和结构创建包装器并调用旧代码。
  4. 在C#代码中使用新对象。
  5. 请不要忘记为托管C ++代码创建Unittesting。 (我总是忘记...... :))

    祝你好运。

答案 2 :(得分:1)

如果Java可以通过jna调用你的C代码,那么通过PInvoke应该没有C#的问题。虽然C ++ interop(使用C ++ / Cli)是一种PInvoke(隐式PInvoke),但使用DllImport是显式的PInvoke。

隐式PInvoke非常有用,当您不需要指定如何封送函数参数时,或者在显式调用DllImportAttribute时可以指定的任何其他详细信息,但是您需要创建一个额外的C ++ / CLI Dll。

在这两种方式中,你必须处理marshal本地数据类型到管理数据类型,这是不可避免和痛苦的。

在C#中,结构可以声明为:

[StructLayout(LayoutKind.Sequential)]
    public struct myStruct {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
        double prefix[] intersects;

        public int length;

        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public double[] array;
    }
但是对于该函数,DLLImport无法处理这种情况,因为C#不能为函数返回的非托管指针删除内存,可以在C中创建另一个wrap函数使其使用out参数返回结果,在这种情况下,C#代码是:

[DllImport("...")]
    public static extern void doSomething([In, Out] myStruct[] results,  myStruct[] input,  int len);

或者您可以使用C ++ / CLI互操作,因为它可以处理本机和管理类型,因此调用顺序为:

  1. C#代码使用托管数据类型调用此C ++ / CLI函数:

    ManagedmyStruct [] doSomething(ManagedmyStruct []输入,双倍a)

  2. 在C ++ / CLI函数domSomething中,它调用本机函数,步骤如下:

    ManagedmyStruct [] doSomething(ManagedmyStruct []输入,双倍a) {

      //convert the ManagedmyStruct[] input to native type myStruct* input
      myStruct ret* =  doSomething(input, a);
      //convert ret to managed type ManagedmyStruct[] rets
      return rets;
    

    }