《Inside C#》笔记(十五) 非托管代码 上

时间:2022-04-18 02:25:14

为了保证向后兼容性,C#和.NET可以通过非托管的方式运行旧代码。非托管代码是指没有被.NET运行时管控的代码。非托管代码主要包括:平台调用服务(PlatformInvocation Services)、不安全代码(Unsafe Code)、COM互操作(COM interoperability)。

 

一 平台调用服务

平台调用服务(Platform Invocation Services)也被称作PInvoke,可以使用非托管DLL中的方法、结构甚至是给其传递回调函数。在使用非托管DLL前需事先了解DLL内部方法的参数和返回值。

a)基本使用方法为:

 

《Inside C#》笔记(十五) 非托管代码 上

MessageBoxA属于Win32的API,需要先声明一个与MessageBoxA的方法签名一致的方法,然后DllImport导入这个DLL,签名方法必须用static extern修饰。

 

b)签名方法也可与DLL中的方法不同名,但需在DllImport的EntryPoint指定原始名称。

 

 

《Inside C#》笔记(十五) 非托管代码 上

 

c)使用CharSet

CharSet可以指定DLL所使用的字符集。比如上述的MessageBoxA实际上对应的是Ansi编码,还有对应Unicode编码的MessageBoxW,除了直接指定调用哪个,还可用下面的写法:

 

《Inside C#》笔记(十五) 非托管代码 上

 

 

编译器会根据CharSet的类型决定调用哪种MessageBox。这应该需要DLL内部做相应配合,至少需要知道每个MessageBox对应的字符集。

 

d)回调

不仅C#代码可以调用DLL的方法,DLL方法也可用回调的方式使用C#代码。

 

这里将PrintWindow作为回调函数传递给了API中的EnumWindows方法。

 

《Inside C#》笔记(十五) 非托管代码 上

 

e)Marshal(排列、整理?)

在前面的例子中,DLL中MessageBox的方法参数为:

 

《Inside C#》笔记(十五) 非托管代码 上

 

 

C#代码中的方法签名并没有与之完全匹配,但却能正常运行,这是因为编译器自动进行了默认的Marshal,比如将C#的string类型对应为Win32的LPSTR。这个过程也可以手动进行,使用MarshalAs:

 

《Inside C#》笔记(十五) 非托管代码 上

?

 

如果要Marshal返回值,要标记在方法体上面。

 

二 编写不安全代码

这里的不安全代码指的是没有被.NET运行时托管的代码,内存的分配、释放、寻址等都不受约束,比如可以在C#代码中使用指针,在有些场合C#指针非常有用,比如需要调用C语言编写的API时、或者需要对内存有完全的控制时。

a)与不安全代码相关的关键字unsafe和fixed

unsafe关键字用来告知.NET运行时,相关的代码块将不受托管。不受托管的代码块可以是方法、属性、或者是一个方法内部的代码片段。