如何为非托管 c dll 创建 c++\cli 包装器
Posted
技术标签:
【中文标题】如何为非托管 c dll 创建 c++\\cli 包装器【英文标题】:how to create a c++\cli wrapper for unmanaged c dll如何为非托管 c dll 创建 c++\cli 包装器 【发布时间】:2014-04-11 13:35:10 【问题描述】:我可能自己都搞糊涂了,但以前没有这样做过,一些指导会很有帮助。
我正在尝试从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;
【问题讨论】:
Win32 是 32 位和 64 位版本的 Windows 中 Windows API 的通用名称。如果 C# 应用程序是 64 位的,那么您使用的 DLL 必须是 64 位的。是吗? c dll是64位的,c++\cli wrapper dll的win32位让我觉得是32位 【参考方案1】:封装 C 和 C++ 之间的区别真的很小。您需要创建一个 C++/CLI 类库。然后,您在封装本机代码的托管 C++ 引用类中编写函数。
例如,假设 DLL 导出这个函数:
int sqr(int x)
然后在你的类库中包含头文件:
#include <mynativelibrary.h>
您还需要将导入库提供给链接器。
然后你就可以公开这个函数了。最简单的方法是将函数包装为 ref 类的静态方法。例如:
public ref class Class1
public:
static int sqr(int x)
return ::sqr(x);
;
然后,您可以像使用任何其他程序集一样在 C# 代码中使用此程序集。
【讨论】:
谢谢我试试看。如果 c 函数接受一个结构并返回一个结构怎么办。这在 c++ 包装器中是如何处理的? 函数本身没有在头文件中定义。只有结构和宏等有区别吗? 显然你需要适应和泛化更复杂的类型。将非托管结构调整为托管 ref 结构。等等。我不相信您没有在任何地方声明导出的函数。我有点担心你似乎在要求我们在这里教你 C++/CLI。 我浏览了整个 c 项目,唯一找到该函数的地方是 c 文件中。我确实说过我以前从未这样做过:) 没关系。然后将 .c 文件添加到 C++/CLI 项目并将其编译到 C++/CLI 程序集中。这样您就不需要单独的非托管 DLL。在 Stack Overflow 问题中,我认为我们无法合理地将您从完全新手转变为成品。很抱歉,这对您没有帮助。【参考方案2】:我在 VisualStudio 2012 中创建了一些项目,这些项目使用托管代码包装了旧的 MFC dll。我是这样做的:
-
在 CLR 中创建一个类库。
将旧项目链接到这个新项目
在新项目中为函数和结构创建包装器并调用旧代码。
在 C# 代码中使用新对象。
请不要忘记为托管 C++ 代码创建单元测试。 (我总是忘记...:))
祝你好运。
【讨论】:
你能不能!详细说明这一点,我是 Visual C++ 的新手,也面临同样的问题 @Ka7lm1011 请看这个:c-sharpcorner.com/UploadFile/SamTomato/…【参考方案3】:如果Java可以通过jna调用你的C代码,那么C#通过PInvoke应该没有问题。虽然 C++ 互操作(使用 C++/Cli)是一种 PInvoke(隐式 PInvoke),但使用 DllImport 是显式 PInvoke。
当您不需要指定函数参数的编组方式或在显式调用 DllImportAttribute 时可以指定的任何其他详细信息时,隐式 PInvoke 很有用,但您需要创建一个额外的 C++/CLI Dll。
在这两种方式中,您都必须处理将原生数据类型编组到管理的数据类型,这是不可避免且痛苦的。
在 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 互操作,因为它可以处理原生类型和管理类型,所以调用顺序是:
C# 代码使用托管数据类型调用此 C++/CLI 函数:
ManagedmyStruct[] doSomething(ManagedmyStruct[] input, double a)
在C++/CLI函数domSomething中,调用native函数,步骤如下:
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;
【讨论】:
上面已经加了代码,主要是想知道如何处理包含变长数组的struct,size为1的会变长 长度为 1 的数组作为包含名称长度为 int var 的 struct 的最终成员非常清楚地表明了 C struct hack。因此,没有多少 pinvoke struct marshalling 可以完成这项工作。您需要手动 IntPtr 编组。以上是关于如何为非托管 c dll 创建 c++\cli 包装器的主要内容,如果未能解决你的问题,请参考以下文章
在Visual Studio 2010中将Native / C ++ DLL链接到托管C ++ / CLI包装器