作为记录,我得到了大部分的方法来让它工作,然后最终将适当的 C 文件拉入 C++ 项目(因为我正在与我什至不需要的代码中的兼容性问题作斗争)。
以下是我在此过程中收集的一些提示,可能对遇到此问题的人有所帮助:
编组阵列
double*
在 C 中,指向双精度 ( ) 的指针和双精度数组( )之间没有区别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
'sends back'y
中。
调用约定
您实际上不需要了解这意味着什么的更详细的细节(换句话说,我不需要),您只需要知道存在不同的调用约定,并且它们需要在双方都匹配。C# 默认为StdCall
,C 默认为Cdecl
. 这意味着您只需要显式指定调用约定,如果它与您使用它的任何一方的默认值不同。
这在回调的情况下尤其麻烦。如果我们将回调传递给C
from C#
,我们打算使用 调用该回调StdCall
,但是当我们传入它时,我们正在使用Cdecl
. 这会产生以下签名(有关上下文,请参阅此问题):
//=======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
编辑)公开的任何函数都需要在ModDef.def
文件中显示,其内容如下所示:
LIBRARY MyLibName
EXPORTS
SetWrappedCallback @1
外部“C”
如果你想在 C++ 中使用 C 函数,你必须将它们声明为extern "C"
. 如果你包含 C 函数的头文件,你可以这样:
extern "C" {
#include "C_declarations.h"
}
预编译头文件
为了避免编译错误,我必须做的另一件事是将每个“C”文件Right-click -> Properties -> C/C++ -> Precompiled Headers
设置Precompiled header
为不使用预编译头文件。