C#与api(一)

by admin on 2020年1月21日

C#
API
C:ProgramFilesMicrosoftVisual Studio .NET FrameworkSDKSamples
Technologies InteropPlatformInvoke WinAPIsCS目录下有多量的调用API的事例。
风度翩翩、调用格式
using System.Runtime.InteropServices; //引用此称号空间,简化后面的代码
//使用DllImportAttribute特性来引进api函数,注意注明的是空方法,即方法体为空。
[DllImport(“user32.dll”)]
public static extern ReturnType FunctionName(type arg1,type arg2,…);
//调用时与调用其他措施并无分化

可以应用字段进一层证实天性,用逗号隔离,如:
[ DllImport( “kernel32″, EntryPoint=”GetVersionEx” )]
DllImportAttribute脾气的国有字段如下:
1、CallingConvention
提醒向非托管达成传递方式参数时所用的
CallingConvention 值。
CallingConvention.Cdecl : 调用方清理宾馆。它使您可以调用具备 varargs 的函数。
CallingConvention.StdCall :
被调用方清理仓库。它是从托管代码调用非托管函数的默许约定。
2、CharSet
调控调用函数的名目版本及提示如何向方法封送
String 参数。
此 字段棉被服装置为 CharSet
值之后生可畏。要是 CharSet 字段设置为
Unicode,则怀有字符串参数在传递到非托管达成在此以前都改动来 Unicode 字符。那还诱致向 DLL
EntryPoint 的名目中追加字母“W”。假诺此字段设置为
Ansi,则字符串将更动来 ANSI
字符串,同一时间向 DLL EntryPoint
的称号中扩大字母“A”。大大多 Win32 API 使用这种扩大“W”或“A”的预定。假如 CharSet 设置为
Auto,则这种转移就是与平台有关的(在 Windows
NT 上为 Unicode,在
Windows 98 上为 Ansi)。CharSet 的私下认可值为
Ansi。CharSet
字段也用于分明将从钦点的 DLL
导入哪个版本的函数。CharSet.Ansi
和 CharSet.Unicode
的称呼相配准绳大不相符。对于 Ansi
来讲,假若将 EntryPoint 设置为“MyMethod”且它存在的话,则赶回“MyMethod”。假使 DLL
中绝非“MyMethod”,但存在“MyMethodA”,则赶回“MyMethodA”。对于 Unicode
来讲则刚刚相反。若是将 EntryPoint
设置为“MyMethod”且它存在的话,则赶回“MyMethodW”。假使 DLL
中不设有“MyMethodW”,但存在“MyMethod”,则赶回“MyMethod”。倘若使用的是
Auto,则十一分准则与平台有关(在 Windows NT
上为 Unicode,在
Windows 98 上为 Ansi)。假设 ExactSpelling 设置为
true,则只有当 DLL 中存在“MyMethod”时才回去“MyMethod”。
3、EntryPoint 提示要调用的 DLL 入口点的名号或序号。
比如您的办法名不想与api函数同名的话,必须要钦赐此参数,举个例子:
[DllImport(“user32.dll”,CharSet=”CharSet.Auto”,EntryPoint=”MessageBox”)]
public static extern int MsgBox(IntPtr hWnd,string txt,string caption,
int type);
4、 ExactSpelling
提示是或不是应校勘非托管 DLL
中的入口点的名目,以与 CharSet
字段中钦赐的 CharSet
值相呼应。假使为 true,则当 DllImportAttribute.CharSet 字段设置为 CharSet 的 Ansi
值时,向方法名称中追加字母 A,当 DllImportAttribute.CharSet 字段设置为 CharSet 的 Unicode
值时,向方法的称谓中加进字母
W。此字段的暗许值是 false。
5、PreserveSig
提醒托管方法签字不应调换到返回HRESULT、况兼或许有二个应和于再次回到值的附加
[out, retval] 参数的非托管签名。
6、 SetLastError
提醒被调用方在从属性化方法重回早前将调用 Win32
API SetLastError。 true
提醒调用方将调用SetLastError,默以为false。运维时封送拆收器将调用 GetLastError
并缓存重临的值,防止其被其余 API
调用重写。客户可经过调用 GetLastWin32Error
来搜索错误代码。

二、参数类型:
1、数值型直接用相应的就可。(DWOMuranoD -> int , WO中华VD -> Int16)
2、API中字符串指针类型 -> .net中string
3、API中句柄 (dWord)
-> .net中IntPtr
4、API中组织 ->
.net中协会照旧类。注意这种情景下,要先用StructLayout脾性节制注解布局或类
公 共语言运营库利用StructLayoutAttribute调整类或协会的数额字段在托管内部存款和储蓄器中的物理布局,即类或构造要求按某种情势排列。借使要将类
传递给急需钦赐构造的非托管代码,则显式调整类布局是十分重要的。它的构造函数中用LayoutKind值初阶化 StructLayoutAttribute 类的新实例。 LayoutKind.Sequential
用于抑遏将成员按其冒出的依次实行依次布局。
LayoutKind.Explicit 用于调控各个数据成员的确切地方。利用 Explicit,每一个成员必须采用菲尔德OffsetAttribute 提示此字段在等级次序中的地点。如:
[StructLayout(LayoutKind.Explicit, Size=16, CharSet=CharSet.Ansi)]
public class MySystemTime
{
[FieldOffset(0)]public ushort wYear;
[FieldOffset(2)]public ushort wMonth;
[FieldOffset(4)]public ushort wDayOfWeek;
[FieldOffset(6)]public ushort wDay;
[FieldOffset(8)]public ushort wHour;
[FieldOffset(10)]public ushort wMinute;
[FieldOffset(12)]public ushort wSecond;
[FieldOffset(14)]public ushort wMilliseconds;
}
上面是指向性API中OSVEENVISIONSIONINFO构造,在.net中定义对应类或构造的例证:
/**********************************************
* API中定义原构造注明
* OSVERSIONINFOA STRUCT
* dwOSVersionInfoSize DWORD ?
* dwMajorVersion DWORD ?
* dwMinorVersion DWORD ?
* dwBuildNumber DWORD ?
* dwPlatformId DWORD ?
* szCSDVersion BYTE 128 dup (?)
* OSVERSIONINFOA ENDS
*
* OSVERSIONINFO equ <OSVERSIONINFOA>
*********************************************/

//.net中表明为类
[ StructLayout( LayoutKind.Sequential )]
public class OSVersionInfo
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
public String versionString;
}
//或者
//.net中宣称为构造
[ StructLayout( LayoutKind.Sequential )]
public struct OSVersionInfo2
{
public int OSVersionInfoSize;
public int majorVersion;
public int minorVersion;
public int buildNumber;
public int platformId;

[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=128 )]
public String versionString;
}

此例中用到MashalAs特性,它用来描述字段、方法或参数的封送管理格式。用它看成参数前缀并内定指标要求的数据类型。比如,以下代码将多少个参数作为数据类型长指针封送给 Windows API 函数的字符串
(LPStr卡塔尔国:
[MarshalAs(UnmanagedType.LPStr)]
String existingfile;
[MarshalAs(UnmanagedType.LPStr)]
String newfile;

只顾组织作为参数时候,日常前边要增添ref修饰符,不然会晤世错误:对象的引用未有一点点名对象的实例。
[ DllImport( “kernel32″, EntryPoint=”GetVersionEx” )]
public static extern bool GetVersionEx2( ref OSVersionInfo2 osvi
);

三、如何确保使用托管对象的平台调用成功?
假设在调用平台 invoke
后的其他职责都未援引托管对象,则垃圾回笼器或者将做到该托管对象。那将释放财富并使句柄无效,从而变成平台invoke 调用战败。用 HandleRef
包装句柄可保险在阳台 invoke
调用完结前,不对托管对象开展垃圾回笼。
例如说上面:
FileStream fs = new FileStream( “a.txt”, FileMode.Open );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
ReadFile(fs.Handle, buffer, 5, out read, 0 ); //调用Win API中的ReadFile函数
鉴于fs是托管对象,所以有望在凉台调用还未能如愿时候被垃圾回笼站回笼。将文件流的句柄用HandleRef包装后,就可以制止被垃圾站回笼:
[ DllImport( “Kernel32.dll” )]
public static extern bool ReadFile(
HandleRef hndRef,
StringBuilder buffer,
int numberOfBytesToRead,
out int numberOfBytesRead,
ref Overlapped flag );
……
……
FileStream fs = new FileStream( “HandleRef.txt”, FileMode.Open );
HandleRef hr = new HandleRef( fs, fs.Handle );
StringBuilder buffer = new StringBuilder( 5 );
int read = 0;
// platform invoke will hold reference to HandleRef until call ends
ReadFile( hr, buffer, 5, out read, 0 );

自家
在团结方今的编制程序中注意到三个方向,便是以此趋势才引出前段时间的专辑大旨。近些日子,作者在根据 Microsoft? .NET Framework
的应用程序中成就了大批量的 Win32?
Interop。作者实际不是要说自家的应用程序充满了自定义的 interop 代码,但一时作者会在 .NET
Framework
类库中境遇一些附带但又繁絮、不丰盛的故事情节,通过调用该 Windows? API,能够快速减掉那样的难为。

由此作者以为,.NET Framework 1.0 或 1.1 版类库中设有任何 Windows
所没有的成效约束都多如牛毛。终究,叁12个人的
Windows(不管何种版本)是三个老奸巨猾的操作系统,为广大顾客服务了十多年。相比之下,.NET Framework 却是贰个新东西。

乘胜愈来愈多的开辟职员将临蓐应用程序转到托管代码,开垦职员更频仍地钻研底层操作系统以图找寻部分主要意义显得很自然—最少如今是这么。


得庆幸的是,公共语言运维库 (CL牧马人State of Qatar 的 interop 作用(称为平台调用 (P/Invoke卡塔尔)极度完美。在本专栏中,我将主要介绍怎么着实际行使 P/Invoke 来调用 Windows API
函数。当指 CL哈弗 的
COM Interop 功用时,P/Invoke
充作名词使用;当指该意义的接收时,则将其看成动词使用。我并不思索间接介绍 COM Interop,因为它比 P/Invoke
具有越来越好的可访谈性,却更为复杂,那有一点漏洞非常多,那使得将 COM Interop 作为专栏宗旨来谈谈不太轻松。

走进 P/Invoke

第风流倜傥从察看叁个简单的 P/Invoke 示例起头。让我们看风流洒脱看怎么着调用 Win32 MessageBeep
函数,它的非托管评释如以下代码所示:

BOOL
MessageBeep(
UINT uType // beep type
);

为了调用 MessageBeep,您须要在 C#
中将以下代码增多到四个类或组织定义中:

[DllImport(“User32.dll”)]
static extern Boolean MessageBeep(UInt32 beepType);

为之侧目的是,只要求这段代码就可以使托管代码调用非托管的 MessageBeep
API。它不是叁个措施调用,而是贰个表面方法定义。(此外,它相近于两个出自 C 而 C#
允许的直白端口,由此以它为源点来介绍部分定义是有帮忙的。)来自托管代码的或是调用如下所示:

MessageBeep(0);


注意,将来 MessageBeep 方法被声称为 static。那是 P/Invoke
方法所要求的,因为在该 Windows API
中从未同样的实例概念。接下来,还要小心该办法被标志为 extern。那是提醒编写翻译器该措施是透过三个从 DLL 导出的函数达成的,由此没有供给提供方法体。

聊起贫乏方法体,您是或不是注意到 MessageBeep
表明并不曾富含三个方法体?与当先二分之黄金年代算法由中间语言 (IL卡塔尔(قطر‎ 指令组成的托管方法不一致,P/Invoke 方法只是元数据,实时
(JITState of Qatar 编译器在运转时通过它将托管代码与非托管的
DLL
函数连接起来。施行这种到非托管世界的总是所需的多个重视信息正是导出非托管方法的 DLL 的名目。那风度翩翩音信是由
MessageBeep 方法评释早先的 DllImport
自定义属性提供的。在本例中,能够看出,MessageBeep 非托管 API
是由 Windows 中的
User32.dll 导出的。

到现行反革命甘休,关于调用 MessageBeep
就剩五个话题从未介绍,请回想一下,调用的代码与以下所示代码片段极度相仿:

[DllImport(“User32.dll”)]
static extern Boolean MessageBeep(UInt32 beepType);

最后那多个话题是与数量封送处理 (data marshalingState of Qatar和从托管代码到非托管函数的骨子里方法调用有关的话题。调用非托管 MessageBeep 函数能够由找到效用域内的extern MessageBeep
表明的其余托管代码实施。该调用相近于其余其余对静态方法的调用。它与别的任何托管方法调用的协同之处在于带给了数据封送管理的供给。

C#
的平整之一是它的调用语法只好访谈 CL凯雷德 数据类型,比如 System.UInt32
和 System.Boolean。C# 显著不识别 Windows API
中接收的依附 C 的数据类型(比方 UINT 和
BOOL),那一个品种只是 C
语言类型的类型定义而已。所以当 Windows API
函数 MessageBeep 按以下措施编写时

BOOL
MessageBeep( UINT uType )

表面方法就一定要选拔 CL奥迪Q5类型来定义,如你在前方的代码片段中所看见的。须要选取与底蕴 API 函数类型不一样但与之相配的 CL大切诺基类型是 P/Invoke
较难使用的叁个上面。由此,在本专栏的末端小编将用全体的章节来介绍数据封送管理。

样式

在 C# 中对 Windows API
举行 P/Invoke
调用是非常粗略的。但倘使类库拒却使您的应用程序发出嘟声,应该设法调用 Windows 使它进行那项职业,是吧?


的。不过与选取的秘籍有关,何况涉及吗大!日常,假设类库提供某种渠道来达成您的寻思,则最棒利用 API 而并不是一贯调用非托管代码,因为 CLHighlander 类型和 Win32
之间在样式上有非常的大的不等。笔者得以将有关那一个难题的提出归咎为一句话。当你实行 P/Invoke
时,不要使应用程序逻辑直接属于其余外界方法或内部的预制构件。假若你遵从这些小法规,从深刻看平日会省去过多的分神。

图 1 中的代码突显了自己所争辨的
MessageBeep 外界方法的起码叠合代码。图 1中并不曾其余鲜明的生成,而只是对无包装的表面方法实行部分经常的修正,那足以使办事尤其自在一些。从顶上部分从头,您会专一到四个名叫 Sound 的完全类型,它专项使用于
MessageBeep。尽管小编索要动用 Windows API
函数 PlaySound
来加多对播放波形的扶助,则足以选择 Sound
类型。不过,作者不会因公然单个公共静态方法的品种而恼火。终归那只是应用程序代码而已。还应有小心到,Sound
是密闭的,并定义了三个空的私家构造函数。那几个只是局地细节,目标是使顾客不会错误地从 Sound 派生类可能成立它的实例。

图 1 中的代码的下四个风味是,P/Invoke 现身岗位的莫过于外界方法是
Sound 的私人商品房方法。那些方法只是由公共
MessageBeep 方法直接公开,前者接收 BeepTypes
类型的参数。那个直接的额外层是叁个很关键的细节,它提供了以下好处。首先,应该在类库中引进多少个前程的 beep 托管方法,可以再一次地经过国有 MessageBeep 方法来利用托管
API,而不用纠正应用程序中的其他代码。

该包裹
方法的第叁个低价是:当您进行 P/Invoke
调用时,您遗弃了免于访谈矛盾和任何低端破坏的职责,那平日是由 CL兰德兰德酷路泽提供的。缓冲方法能够尊敬你的应用程序的别的部分免接纳访谈问冲突及相符难题的震慑(就算它不做任何事而只是传递参数)。该缓冲方法将由 P/Invoke 调用引进的其余交秘书密的不当当地化。

将个人外界方法隐讳在公私包装前边的第三并且也是最后的二个低价是,提供了向该方法增添一些微细的 CL本田CR-V 样式的机缘。举例,在图 1中,笔者将 Windows API 函数重临的 Boolean 失败转变到更像 CL福特Explorer的特别。小编还定义了七个名称叫 BeepTypes
的枚举类型,它的成员对应于同该 Windows API
一齐利用的定义值。由于 C#
不帮忙定义,因而能够利用托管枚举类型来制止幻数向一切应用程序代码扩散。

打包措施的结尾一个利润对于简易的 Windows API 函数(如
MessageBeep)诚然是无关大局的。不过当你早前调用更复杂的非托管函数时,您会意识,手动将 Windows API 样式调换来对 CLRubicon尤其团结的艺术所推动的功利会进一层多。越是筹算在任何应用程序中收音和录音 interop
功效,越是应该认真地思虑包装的规划。同偶尔间本身感觉,在非面向对象的静态包装措施中利用对 CLEvoque 友好的参数也无须不得以。

DLL
Import 属性

以后是更加尖锐地张开搜求的时候了。在对托管代码举行P/Invoke 调用时,DllImportAttribute
类型扮演着首要的剧中人物。DllImportAttribute
的重概况义是给 CLHaval 提醒哪个 DLL 导出您想要调用的函数。相关
DLL 的称号被用作一个构造函数参数字传送递给
DllImportAttribute。

假如您不能自然哪个 DLL 定义了你要选取的 Windows API
函数,Platform SDK
文书档案将为您提供最佳的帮忙资源。在 Windows API
函数核心文字接近尾声的职分,SDK
文书档案钦赐了 C
应用程序要选用该函数必需链接的 .lib
文件。在大致具备的场合下,该 .lib
文件具备与概念该函数的系统 DLL
文件大器晚成律的名号。举例,假设该函数供给 C
应用程序链接到
Kernel32.lib,则该函数就定义在 Kernel32.dll
中。您能够在 MessageBeep
中找到有关 MessageBeep 的 Platform SDK
文书档案核心。在该主旨结尾处,您会注意到它提议库文件是 User32.lib;那注明 MessageBeep
是从 User32.dll 中程导弹出的。

可选的 DllImportAttribute 属性

除去提议宿主 DLL 外,DllImportAttribute
还含有了有个别可选属性,在那之中七个特别有趣:EntryPoint、CharSet、SetLastError 和
CallingConvention。

EntryPoint
在不愿意外部托管方法具备与 DLL
导出同样的名称的事态下,可以设置该属性来提示导出的 DLL
函数的入口点名称。当你定义多个调用相似非托管函数的外界方法时,那非常有用。其它,在 Windows 中还是能通过它们的序号值绑定到导出的 DLL 函数。假设您要求如此做,则诸如“#1”或“#129”的 EntryPoint 值提醒 DLL
中国和北美洲托管函数的序号值并不是函数名。

CharSet
对于字符集,并不是全数版本的 Windows 都以大器晚成致创制的。Windows
9x 连串成品缺乏主要的 Unicode
协理,而 Windows NT 和 Windows CE 种类则生机勃勃起头就应用
Unicode。在此些操作系统上运营的 CLGL450将Unicode 用于
String 和 Char
数据的中间表示。但也不必忧郁—当调用 Windows 9x API
函数时,CLENVISION会自动进行供给的转移,将其从
Unicode转换为 ANSI。

比如 DLL 函数不以任何形式管理文件,则能够忽视 DllImportAttribute 的 CharSet
属性。但是,当 Char 或 String 数据是等式的一片段时,应该将 CharSet 属性设置为
CharSet.Auto。那样能够使 CLENVISION按照宿主 OS
使用十分的字符集。若无显式地设置 CharSet
属性,则其暗中认可值为
CharSet.Ansi。这一个暗许值是有劣势的,因为对于在
Windows 二零零二、Windows XP 和 Windows NT? 上拓宽的 interop
调用,它会被动地影响文本参数封送管理的性质。

应该显式地选用 CharSet.Ansi 或 CharSet.Unicode
的 CharSet 值实际不是行使 CharSet.Auto
的唯生龙活虎景况是:您显式地钦赐了一个导出函数,而该函数特定于那二种 Win32 OS 中的某黄金年代种。ReadDirectoryChangesW API
函数正是这么的叁个例证,它只设有于依靠 Windows
NT 的操作系统中,何况只帮忙Unicode;在这里种意况下,您应该显式地采用CharSet.Unicode。

临时候,Windows API
是还是不是有字符集关系并不显明。生龙活虎种决不会有错的确认办法是在 Platform SDK 中反省该函数的 C
语言头文件。(借让你不可能自然要看哪个头文件,则足以查看 Platform SDK 文书档案中列出的种种API 函数的头文件。)假设你发掘该 API
函数确实定义为四个辉映到以 A 或 W
结尾的函数名的宏,则字符集与你尝试调用的函数有涉嫌。Windows API 函数的叁个例证是在
WinUser.h 中评释的 GetMessage
API,您大概会惊喜地意识它有 A 和 W 两种版本。

SetLastError
错误管理非常主要,但在编制程序时经常被忘记。当您进行 P/Invoke 调用时,也会晤对其余的挑衅—管理托管代码中 Windows API
错误处理和特别之间的界别。作者得以给您一点提出。

如果您正在利用 P/Invoke 调用
Windows API 函数,而对于该函数,您使用
GetLastError 来寻觅增加的错误音信,则应当在表面方法的 DllImportAttribute 中将SetLastError 属性设置为
true。那适用于超越四分之第二航空宇航大学表方法。

那会促成 CLHighlander 在历次调用外界方法之后缓存由
API 函数设置的谬误。然后,在卷入格局中,能够经过调用类库的 System.Runtime.InteropServices.Marshal
类型中定义的 Marshal.GetLastWin32Error
方法来获得缓存的错误值。笔者的建议是检查那些梦想来自 API
函数的不当班值日,并为那一个值引发一个可感知的要命。对于其它兼具波折意况(富含根本就没意料到的失利意况),则吸引在 System.ComponentModel 命名空间中定义的 Win32Exception,并将
Marshal.GetLastWin32Error
重临的值传递给它。如若您回头看一下图 1中的代码,您会看出自个儿在 extern MessageBeep
方法的共用包装中就使用了这种办法。

CallingConvention
作者将在这里介绍的终极也只怕是最不根本的三个 DllImportAttribute 属性是
CallingConvention。通过此属性,能够给 CLENCORE提醒应该将哪一种函数调用约定用于仓库中的参数。CallingConvention.Winapi
的暗中同意值是最佳的选用,它在大非常多情景下都灵验。但是,假如该调用不起功效,则能够检查 Platform SDK 中的注解头文件,看看您调用的 API 函数是或不是是二个不适合调用约定规范的不行 API。

常备,本机函数(举个例子 Windows API 函数或 C-
运营时 DLL
函数)的调用约定描述了怎么将参数推入线程货仓或从线程仓库中消除。大好多 Windows API
函数都以首先将函数的最后二个参数推入仓库,然后由被调用的函数担负清理该货仓。相反,大多 C-运转时 DLL
函数都被定义为根据办法参数在议程具名中出现的相继将其推入货仓,将旅舍清总管业交给调用者。

幸运的是,要让 P/Invoke
调用专门的学问只须要让外围设备精通调用约定就能够。经常,从私下认可值 CallingConvention.Winapi
开首是最棒的接纳。然后,在 C
运营时 DLL
函数和个别函数中,大概要求将预订修正为
CallingConvention.Cdecl。

数量封送管理


据封送管理是 P/Invoke
具备挑衅性的上边。当在托管和非托管代码之间传递数据时,CLEvoque固守好些个准绳,超少有开辟职员会时常遭受它们直至可将那么些准绳记住。除非你是一名类库开荒人士,不然在平日状态下无需精通其细节。为了最可行地在 CLRubicon 上行使
P/Invoke,即便只有的时候必要 interop
的应用程序开辟人士仍旧应该知道数据封送管理的局部底子知识。

在上月特辑的剩余部分中,笔者将研讨轻巧数字和字符串数据的数码封送管理。作者将从最基本的数字数据封送处理开头,然后介绍轻易的指针封送管理和字符串封送管理。

封送数字和逻辑标量

Windows
OS 当先二分之一是用 C 编写的。因而,Windows API
所用到的数据类型要么是 C
类型,要么是经过类型定义或宏定义重新标志的 C
类型。让大家看看没有指针的数额封送管理。轻便起见,首先珍视切磋的是数字和布尔值。

当通过值向 Windows API
函数传递参数时,必要领悟以下难点的答案:

?
数据从根本上讲是整型的可能浮点型的?

?
借使数量是整型的,则它是有号子的还是无符号的?

?
假设数据是整型的,则它的位数是有个别?

?
借使数量是浮点型的,则它是单精度的依旧双精度的?

突发性答案很明朗,但不时却不精通。Windows API 以种种法子重新定义了主导的 C 数据类型。图 2
列出了 C 和 Win32
的局部公共数据类型及其标准,以至三个富有至极标准的共用语言运转库类型。

平日,只要你采用三个其标准与该参数的 Win32
类型相相配的 CLTucson类型,您的代码就能够寻常办事。可是也可以有一点特例。举个例子,在 Windows API 中定义的 BOOL
类型是叁个有暗记的 叁九个人整型。不过,BOOL 用于提醒 Boolean 值 true 或 false。即便你不用将 BOOL
参数作为 System.Int32
值封送,然而只要运用 System.Boolean
类型,就能够收获更合适的璀璨。字符类型的光彩夺目相通于 BOOL,因为有二个特定的 CLRAV4类型 (System.Char卡塔尔(قطر‎建议字符的意思。


领悟那些新闻之后,稳步介绍演示只怕是有援助的。依然接收 beep 主旨作为例子,让我们来试一下 Kernel32.dll 初级
Beep,它会透过Computer的音箱发生嘟声。那一个格局的 Platform SDK 文书档案能够在 Beep
中找到。本机 API
按以下方法打开记录:

BOOL
Beep(
DWORD dwFreq, // Frequency
DWORD dwDuration // Duration in milliseconds
);


参数封送处理地点,您的干活是询问怎么样 CLRAV4数据类型与 Beep API 函数所使用的 DWO福特ExplorerD 和 BOOL
数据类型般协作。回想一下图 2中的图表,您将见到 DWO凯雷德D 是叁个 32 位的无符号整数值,仿佛 CLEscort类型
System.UInt32。这意味你能够选取 UInt32
值作为送往 Beep 的八个参数。BOOL
重临值是三个要命有趣的场所,因为该图片告诉大家,在 Win32 中,BOOL
是二个 三16个人的有暗号整数。由此,您能够使用 System.Int32
值作为来自 Beep 的重临值。然则,CLQashqai 也定义了 System.Boolean
类型作为 Boolean
值的语义,所以应当选取它来替代。CL福特Explorer暗中认可将 System.Boolean 值封送为 34个人的有标记整数。此处所呈现的表面方法定义是用来
Beep 的结果 P/Invoke 方法:

[DllImport(“Kernel32.dll”,
SetLastError=true)]
static extern Boolean Beep(
UInt32 frequency, UInt32 duration);

指南针参数

多数 Windows API
函数将指针作为它们的一个或四个参数。指针扩充了封送数据的复杂,因为它们扩充了一个直接层。若无指针,您能够透过值在线程仓库中传递数据。有了指针,则足以因此援引传递数据,方法是将该数量的内部存款和储蓄器地址推入线程仓库中。然后,函数通过内部存款和储蓄器地址间接待上访谈数据。使用托管代码表示此附加直接层的艺术有四种。

在 C# 中,假诺将艺术参数定义为 ref

out,则数据经过引用并不是通过值传递。尽管你未有行使 Interop
也是这么,但只是从二个托管方法调用到另四个托管方法。举个例子,如若经过 ref 传递 System.Int32
参数,则在线程仓库中传递的是该数额之处,并不是整数值本人。上面是贰个定义为经过引用选用整数值的点子的演示:

void
FlipInt32(ref Int32 num){
num = -num;
}

这里,FlipInt32 方法拿到叁个 Int32
值的地址、访问数据、对它求反,然后将求反过的值赋给原来变量。在偏下代码中,FlipInt32 方法会将调用程序的变量
x 的值从 10 纠正为
-10:

Int32
x = 10;
FlipInt32(ref x);

在托管代码中得以援用这种技艺,将指针传递给非托管代码。举例,FileEncryptionStatus API 函数以
32 位无符号位掩码的款式重回文件加密状态。该
API 按以下所示格局开展记录:

BOOL
FileEncryptionStatus(
LPCTSTR lpFileName, // file name
LPDWORD lpStatus // encryption status
);


注意,该函数并不利用它的重临值重临状态,而是回到贰个Boolean
值,提示调用是或不是中标。在功成名就的意况下,实际的情状值是通过首个参数重返的。它的职业方法是调用程序向该函数传递指向三个 DWOENVISIOND 变量的指针,而该 API
函数用状态值填充

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图