天天看点

C#调用C/C++的dll须知

1. C#类型与C/C++类型的对应关系

基本数据类型(C# <—> C/C++)

  • System.Int32,int <—> int, long
  • System.Int64,int64 <—> long long, __int64
  • System.Char, char <—> char, byte, unsigned char
  • System.Int16, short <—> short
  • System.UInt32, uint <—> unsigned int, unsigned long
  • System.UInt16, ushort <—> unsigned short, DWORD
  • System.String, string <—> char[], char *, const char *
  • System.IntPtr <—> void *, [Type]*
  • Sytem.Boolean, bool <—> bool, BOOL

2. C/C++函数参数类型与C#类型对应关系

① 基本的数据类型对应关系与1相同,但是也有一些不一样的地方

② const char * 一般作为输入参数, C#直接使用string类型即可(StringBuilder也可以)

③ char *作为输入参数的时候, C#需要使用ref string类型或者StringBuilder类型

④ 结构体指针做输入参数的时候, C#一般使用ref + 对应的结构体类型

3. C/C++结构体与C#结构体的成员类型对应关系

A. 如果C/C++结构体成员类型是基本数据类型, C#中对应的结构体成员类型使用1中的对应关系即可, 如:

C/C++代码

struct Some
{
   	int number;
   	char character;
   	char name[32];	
}
           

C#代码

struct Some
{
   	int number;
   	char charactor;
   	[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
   	string name;
}
           

B. 如果C/C++的结构体成员中包含的成员仍然是结构体(单个,非数组), 则对应的C#结构体只需要同样包含结构体, 如:

C/C++代码

struct A
{
    int number;
};
struct B
{
   struct A a;
   int otherNumber;
};
           

对应的C#代码

public struct A
{
    public int number;
}

public struct B
{
   public A a;
   public int otherNumber;
}
           

###C. 如果C/C++结构体成员中包含数组(长度固定),则C#结构体中也使用数组对应的类型, 如:

C/C++代码

struct A
{
int number;
};
struct B 
{
int numbers[10];
struct A alist[5];
};
           

对应的C#代码

public struct A 
{
	int number;
}
public struct B
{
	[MarshalAs(UnmanagedType.ByValArray, SizeConst=10, ArraySubType=UnmanagedType.SysInt)]	
	public int[] numbers;
	[MarshalAs(UnmanagedType.ByValArray, SizeConst=10, ArraySubType=UnmanagedType.Struct)]
	public A[] alist;
}
           

###D. 如果C/C++的结构体成员中包含另一个结构体的指针,C#对应的结构体中一律使用IntPtr类型, 如:

C/C++代码

struct A
{
 int number;
};
struct B
 {
 int tag;
 struct A *pA;
}
           

对应的C#代码

public struct A
{
 public int number;
}
public struct B
{
 public int tag;
 public IntPtr pA;
 }
           

然后在使用的时候需要用到Marshal类的StructureToPtr和PtrToStructure方法进行转换

转换方法如下:

(1)结构体转换成IntPtr

A a = new A();
B b = new B();
IntPtr pA= Marshal.AllocHGlobal(Marshal.SizeOf(a));
Marshal.StructureToPtr(a, pA, false);
b.pA = pA;
           

(2)IntPtr 转换成结构体

B b = new B();
//...省略中间的操作过程
A aInB = (A)Marshal.PtrToStructure(b.pA, typeof(A))
//或者
A aInB = new A();
Marshal.PtrToStructure(b.pA, aInB);
           

4. 关于类型转换中的数组长度

  • C/C++中,如果是char类型的数组(一般表示最大长度不超过某个数字的字符串),那么在进行参数传递的时候,如果C#中的string字符串字节数超过了C/C++中的最大字节数,则会发生截断, 如C/C++中, char name[3]; 但是在C#中却使用了string name = “Jack”,实际传到C/C++中, name中的内容为“Ja”,多余的部分没有填充进来
  • C/C++中使用的数组为定长数组,在参数传递过程中,如果C#中使用的数组长度比C/C++中数组长度短,则会发生System.ArgumentException异常,提示“未能封送类型,因为嵌入数组实例的长度与布局中声明的长度不匹配“(C#的string到C/C++的char 数组不会,但是C#char[]到C/C++的char数组会有此异常),因此, 我们要定义相同长度的数组才能正常工作(C#数组长度更大的时候,会忽略多余的部分)