c# - 从 C# 使用 C 库的技巧

标签 c# .net c interop

我有一个 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++ 项目中公开的函数(被 DllImported),需要在 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/

相关文章:

c - 如何使用 gcc 修复 SDL2 编译错误?

c - 准确找到生成的数字范围?

c# - jBPM自定义授权

.net - 开始解耦对象的最佳位置

c# - 了解获取和设置访问器

.net - 海洋单元测试

c# - C# 中的序列化和对象版本控制

c - 在 C 中使用合并排序或快速排序对非常长的链表进行排序

c# - 无法从 ComboBox 获取值

c# - Microsoft.Graph.Models.ODataErrors.ODataError 与 .PatchAsync(用户)上的 Graph API