我有一个 C 语言的库,我想在 C# 中使用它。
根据我从互联网上收集到的信息,one idea就是把它包装在一个 C++ dll 中,然后 DllImport 那个。
问题是我想调用的函数有一个相当讨厌的参数集:包括对函数的引用(这将是一个 .NET 函数)和几个数组(一些写,一些读)。
int lmdif(int (*fcn)(int, int, double*, double*, int*),
int m, int n, double* x, double ftol, double xtol, double gtol,
int maxfev, double epsfcn, double factor)
对于这样的界面,我应该注意哪些问题? (还有解决方案,请)
你为什么不……
- 用 C# 重写?我开始了,但它已经是从 FORTRAN 机器翻译而来的,我不太喜欢编写我无法理解的代码。
- 使用 an existing .NET library ?我现在正在尝试,但结果并不完全相同
- 用 C++ 重新编译?正想着呢,不过看起来好痛
最佳答案
郑重声明,我已经完成了大部分工作,然后最终将适当的 C 文件拉入 C++ 项目(因为我一直在与我什至不需要的代码中的兼容性问题作斗争)。
以下是我在此过程中获得的一些提示,可能对遇到此问题的人有所帮助:
编码数组
在 C 语言中, double 指针 (double*
) 和 double 组 (double*
) 没有区别。当您进行互操作时,您需要能够消除歧义。我需要传递 double
数组,因此签名可能如下所示:
[DllImport(@"PathToDll")]
public static extern Foo(
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)[In] double[] x,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)[Out] double[] y,
int x_size,
int y_size)
C 需要数组长度的额外信息,您必须将其作为单独的参数传入。编码还需要知道此大小的位置,因此您指定 SizeParamIndex
,它指示参数列表中大小参数从零开始的索引。
您还可以指定数组的传递方向。在此示例中,x
被传递给 Foo
,它“返回”y
。
调用约定
您实际上不需要了解这意味着什么的更详细信息(换句话说,我不需要),您只需要知道存在不同的调用约定,并且它们需要在两端匹配。 C# 默认为 StdCall
,C 默认为 Cdecl
。这意味着您只需要明确指定调用约定,如果它与您使用它的任何一方的默认值不同。
这在回调的情况下尤其棘手。如果我们从 C#
向 C
传递回调,我们打算使用 StdCall
调用该回调,但是当我们传递它,我们正在使用 Cdecl
。这导致以下签名(有关上下文,请参阅 this question):
//=======C-code======
//type signature of callback function
typedef int (__stdcall *FuncCallBack)(int, int);
void SetWrappedCallback(FuncCallBack); //here default = __cdecl
//======C# code======
public delegate int FuncCallBack(int a, int b); // here default = StdCall
[DllImport(@"PathToDll", CallingConvention = CallingConvention.Cdecl)]
private static extern void SetWrappedCallback(FuncCallBack func);
打包回调
很明显,但对我来说并不是很明显:
int MyFunc(int a, int b)
{
return a * b;
}
//...
FuncCallBack ptr = new FuncCallBack(MyFunc);
SetWrappedCallback(ptr);
.def文件
任何你想从 C++ 项目中公开的函数(被 DllImport
ed),需要在 ModDef.def
文件中计算,其内容看起来像这样:
LIBRARY MyLibName
EXPORTS
SetWrappedCallback @1
外部“C”
如果您想从 C++ 使用 C 函数,您必须将它们声明为 extern "C"
。如果你包含一个 C 函数的头文件,你可以这样做:
extern "C" {
#include "C_declarations.h"
}
预编译 header
为了避免编译错误,我必须做的另一件事是 右键单击 -> Properties -> C/C++ -> Precompiled Headers
并将 Precompiled header
设置为 不为每个“C”文件使用预编译头文件。
关于c# - 从 C# 使用 C 库的技巧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10847370/