Csharp/C#教程:C#中调用Windows API的技术要点说明分享

在.NetFrameworkSDK文档中,关于调用WindowsAPI的指示比较零散,并且其中稍全面一点的是针对VisualBasic.net讲述的。本文将C#中调用API的要点汇集如下,希望给未在C#中使用过API的朋友一点帮助。另外如果安装了VisualStudio.net的话,在C:ProgramFilesMicrosoftVisualStudio.NETFrameworkSDKSamplesTechnologiesInteropPlatformInvokeWinAPIsCS目录下有大量的调用API的例子。

一、调用格式
代码如下:
 usingSystem.Runtime.InteropServices;//引用此名称空间,简化后面的代码

//使用DllImportAttribute特性来引入api函数,注意声明的是空方法,即方法体为空。
[DllImport(“user32.dll”)]
publicstaticexternReturnTypeFunctionName(typearg1,typearg2,…);
//调用时与调用其他方法并无区别

可以使用字段进一步说明特性,用逗号隔开,如:
代码如下:
[DllImport(“kernel32″,EntryPoint=”GetVersionEx”)] 

DllImportAttribute特性的公共字段如下:
1、CallingConvention指示向非托管实现传递方法参数时所用的CallingConvention值。
CallingConvention.Cdecl:调用方清理堆栈。它使您能够调用具有varargs的函数。
CallingConvention.StdCall:被调用方清理堆栈。它是从托管代码调用非托管函数的默认约定。

2、CharSet控制调用函数的名称版本及指示如何向方法封送String参数。

此字段被设置为CharSet值之一。如果CharSet字段设置为Unicode,则所有字符串参数在传递到非托管实现之前都转换成Unicode字符。这还导致向DLLEntryPoint的名称中追加字母“W”。如果此字段设置为Ansi,则字符串将转换成ANSI字符串,同时向DLLEntryPoint的名称中追加字母“A”。

大多数Win32API使用这种追加“W”或“A”的约定。如果CharSet设置为Auto,则这种转换就是与平台有关的(在WindowsNT上为Unicode,在Windows98上为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,则匹配规则与平台有关(在WindowsNT上为Unicode,在Windows98上为Ansi)。如果ExactSpelling设置为true,则只有当DLL中存在“MyMethod”时才返回“MyMethod”。

3、EntryPoint指示要调用的DLL入口点的名称或序号。
如果你的方法名不想与api函数同名的话,一定要指定此参数,例如:

代码如下:
[DllImport(“user32.dll”,CharSet=”CharSet.Auto”,EntryPoint=”MessageBox”)]
publicstaticexternintMsgBox(IntPtrhWnd,stringtxt,stringcaption,inttype);

4、ExactSpelling指示是否应修改非托管DLL中的入口点的名称,以与CharSet字段中指定的CharSet值相对应。如果为true,则当DllImportAttribute.CharSet字段设置为CharSet的Ansi值时,向方法名称中追加字母A,当DllImportAttribute.CharSet字段设置为CharSet的Unicode值时,向方法的名称中追加字母W。此字段的默认值是false。

5、PreserveSig指示托管方法签名不应转换成返回HRESULT、并且可能有一个对应于返回值的附加[out,retval]参数的非托管签名。

6、SetLastError指示被调用方在从属性化方法返回之前将调用Win32APISetLastError。true指示调用方将调用SetLastError,默认为false。运行时封送拆收器将调用GetLastError并缓存返回的值,以防其被其他API调用重写。用户可通过调用GetLastWin32Error来检索错误代码。
二、参数类型:

1、数值型直接用对应的就可。(DWORD->int,WORD->Int16)
2、API中字符串指针类型->.net中string
3、API中句柄(dWord) ->.net中IntPtr
4、API中结构  ->.net中结构或者类。注意这种情况下,要先用StructLayout特性限定声明结构或类

公共语言运行库利用StructLayoutAttribute控制类或结构的数据字段在托管内存中的物理布局,即类或结构需要按某种方式排列。如果要将类传递给需要指定布局的非托管代码,则显式控制类布局是重要的。它的构造函数中用LayoutKind值初始化StructLayoutAttribute类的新实例。LayoutKind.Sequential用于强制将成员按其出现的顺序进行顺序布局。

LayoutKind.Explicit用于控制每个数据成员的精确位置。利用Explicit,每个成员必须使用FieldOffsetAttribute指示此字段在类型中的位置。如:
代码如下:
 [StructLayout(LayoutKind.Explicit,Size=16,CharSet=CharSet.Ansi)]
publicclassMySystemTime
{
[FieldOffset(0)]publicushortwYear;
[FieldOffset(2)]publicushortwMonth;
[FieldOffset(4)]publicushortwDayOfWeek;
[FieldOffset(6)]publicushortwDay;
[FieldOffset(8)]publicushortwHour;
[FieldOffset(10)]publicushortwMinute;
[FieldOffset(12)]publicushortwSecond;
[FieldOffset(14)]publicushortwMilliseconds;
}

下面是针对API中OSVERSIONINFO结构,在.net中定义对应类或结构的例子:
代码如下:
  /**********************************************
*API中定义原结构声明
*OSVERSIONINFOASTRUCT
* dwOSVersionInfoSize  DWORD     ?
* dwMajorVersion       DWORD     ?
* dwMinorVersion       DWORD     ?
* dwBuildNumber        DWORD     ?
* dwPlatformId         DWORD     ?
* szCSDVersion         BYTE128dup(?)
*OSVERSIONINFOAENDS
*
*OSVERSIONINFO equ <OSVERSIONINFOA>
*********************************************/
 
代码如下:
 //.net中声明为类
[StructLayout(LayoutKind.Sequential)]  
publicclassOSVersionInfo
{  
publicintOSVersionInfoSize;
publicintmajorVersion;
publicintminorVersion;
publicintbuildNumber;
publicintplatformId;
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]   
publicStringversionString;
}
//或者
//.net中声明为结构
[StructLayout(LayoutKind.Sequential)] 
publicstructOSVersionInfo2
{
publicintOSVersionInfoSize;
publicintmajorVersion;
publicintminorVersion;
publicintbuildNumber;
publicintplatformId;

代码如下:
[MarshalAs(UnmanagedType.ByValTStr,SizeConst=128)]   
publicStringversionString;
}

此例中用到MashalAs特性,它用于描述字段、方法或参数的封送处理格式。用它作为参数前缀并指定目标需要的数据类型。例如,以下代码将两个参数作为数据类型长指针封送给WindowsAPI函数的字符串(LPStr):
代码如下:
 [MarshalAs(UnmanagedType.LPStr)]
Stringexistingfile;
[MarshalAs(UnmanagedType.LPStr)]
Stringnewfile;

注意结构作为参数时候,一般前面要加上ref修饰符,否则会出现错误:对象的引用没有指定对象的实例。
代码如下:
 [DllImport(“kernel32″,EntryPoint=”GetVersionEx”)]
publicstaticexternboolGetVersionEx2(refOSVersionInfo2osvi); 

三、如何保证使用托管对象的平台调用成功?
如果在调用平台invoke后的任何位置都未引用托管对象,则垃圾回收器可能将完成该托管对象。这将释放资源并使句柄无效,从而导致平台invoke调用失败。用HandleRef包装句柄可保证在平台invoke调用完成前,不对托管对象进行垃圾回收。

例如下面:
代码如下:
 FileStreamfs=newFileStream(“a.txt”,FileMode.Open);
StringBuilderbuffer=newStringBuilder(5);
intread=0;
ReadFile(fs.Handle,buffer,5,outread,0);//调用WinAPI中的ReadFile函数

由于fs是托管对象,所以有可能在平台调用还未完成时候被垃圾回收站回收。将文件流的句柄用HandleRef包装后,就能避免被垃圾站回收:
代码如下:
 [DllImport(“Kernel32.dll”)]
publicstaticexternboolReadFile(
HandleRefhndRef,
StringBuilderbuffer,
intnumberOfBytesToRead,
outintnumberOfBytesRead,
refOverlappedflag);
……
……
FileStreamfs=newFileStream(“HandleRef.txt”,FileMode.Open);
HandleRefhr=newHandleRef(fs,fs.Handle);
StringBuilderbuffer=newStringBuilder(5);
intread=0;
//platforminvokewillholdreferencetoHandleRefuntilcallends
ReadFile(hr,buffer,5,outread,0);

您可能感兴趣的文章:C#WindowsAPI应用之基于FlashWindowEx实现窗口闪烁的方法C#获取进程的主窗口句柄的实现方法C#实现利用WindowsAPI读写INI文件的方法C#调用windowsapi关机(关机api)示例代码分享c#调用api控制windows关机示例(可以重启/注销)c#不使用windowsapi函数打开我的电脑和获取电脑驱动器信息C#中隐式运行CMD命令行窗口的方法C#WinForm中禁止改变窗口大小的方法C#Winform中实现主窗口打开登录窗口关闭的方法C#WinForm窗口最小化到系统托盘解决C#获取鼠标相对当前窗口坐标的实现方法C#Winform让整个窗口都可以拖动C#WindowsAPI应用之基于GetDesktopWindow获得桌面所有窗口句柄的方法

标签: Windows ws ow 调用 do dow

C语言实现的阶乘,排列和组合实例

C++遍历文件夹下文件的方法

上述就是C#学习教程:C#中调用Windows API的技术要点说明分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!

本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。

ctvol管理联系方式QQ:251552304

本文章地址:https://www.ctvol.com/cdevelopment/905287.html

(0)
上一篇 2021年10月22日
下一篇 2021年10月22日

精彩推荐