C#托管代码调用C++非托管代码——P/Invoke技术

看了P/Invoke技术的介绍,因而想写下点东西,东西包含两个部分:知识的纪录和个人理解及疑问。web

 r托管代码中调用非托管API函数的过程svg

   1定位包含API的DLL;函数

  二、载入DLL布局

  三、找到DLL中想要的那个API,而后把参数压入栈中、排列数据(排列数据是什么意思?数据封送post

  四、把执行权限从托管代码中转移到非托管代码中()性能

 对Dll中的函数进行一些说明,以能调用spa

   DllImport特性来讲明函数,有一些特殊的做用,好比换掉API的原来名字,见DLLImport特性。指针

非托管函数和托管方法中数据类型对应:数据封送code

 
 [DllImport("msvcrt.dll")]
public static extern int puts(
    [MarshalAs(UnmanagedType.LPStr)]
    string m);puts  函数的参数的默认封送处理已从默认值 LPTSTR 重写为 LPSTR
 

修改默认封送有什么用?默认状况下,本机结构和托管结构在内存中的布局有所不一样,所以,若要跨托管/非托管边界成功传递结构,须要执行一些额外步骤来保持数据的完整性。xml

若是某个API中使用一个结构,那么C#中没有一个对应的托管类型与之对应的话,怎么办?须要为用户定义的结构指定自定义封送处理。

能够为传递到非托管函数或从非托管函数返回的结构和类的字段指定自定义封送处理属性。经过向结构或类的字段中添加 MarshalAs 属性能够作到这一点。还必须使用 StructLayout 属性设置结构的布局,还能够控制字符串成员的默认封送处理,并设置默认封装大小。

C++中:

struct SS { 
int a; 
byte b; 
}

对应C#中:

class SS { public int a; public byte b; }

封送,在此看来算是传数据,可是为何要封送呢?封送是为了在托管内存和非托管内存中正常传递数据。?有时,出于对性能的考虑,会对托管结构的成员进行从新排列,所以有必要使用 StructLayoutAttribute 特性指示该结构为顺序布局。 将结构封装设置显式设置为与本机结构所使用的设置相同的设置也是一个好办法。

 数据封送–基本数据类型对应关系:

数据类型

数据封送中指针处理的两种状况 :

1、普通指针

  在C#中使用ref out来实行封送。

2、Handle类型

  C#中使用IntPtr来进行封送

   封送,个人理解是两个方面,封装和传送。传送就像是方法中参数的传递那样;而封装就有些意思了,在封装前有时候须要作一些处理。就像是上面的 class SS和C++中的struct  ss ,其实二者中的数据a、b在内存中是不同的。详细以下:

1 class SS  { public int a; public byte b; }

 

  看起来a是在b的前面,其实在内存中可不是这样的,C#中类的字段成员的顺序在内存中是自动安排的,也就是说不像是源代码中那样,至因而为何是自动(auto)安排顺序,还不知道,但愿有人知道的话说一下,而在C++中类成员的顺序倒是和看到的同样的,这就是为何在封装前要作一些处理了,由于成员在内存中的顺序不同!

C#中有一个特性能够调整这个顺序,也就是[StructLayout(LayoutKind.Sequential)],在没有显式进行修饰一个类时,默认状况下是[StructLayout(LayoutKind.Auto)]的。处理以下:

[StructLayout(LayoutKind.Sequential)] class SS { public int a; public byte b; }

 

  注册回调方法

  遇到回调函数的处理。