一 调用格式
C#在调用windowsAPI函数接口的时候有一套专门的调用流程
首先我们在调用API函数的时候必须引用命名空间InteropServices
using System.Runtime.InteropServices;
例如我们想调用windows的kernel32.dll动态库中的接口函数中的AllocConsole()控制台函数接口
下面我们用最简单的调用形式来声明调用此函数接口(空方法,即方法体为空)
[DllImport("kernel32.dll")]
public static extern bool AllocConsole();
下面我们可以用DllImportAttribute特性添加字段进一步说明
1 CallingConvention
指示向非托管实现传递方法参数时所用的CallingConvention值
CallingConvention.Cded:调用方清理堆栈。他使用你能够调用具有varargs的函数
CallingConvention.StdCall:被调用方清理堆栈。他是从托管代码调用非托管函数的默认约定
2 CharSet
控制调用函数的名称版本及指示如何向方法封送String参数
CharSet.Ansi:所有字符串转换成ANSI字符串,同时向DLL EnterPoint的名称中追加字母”A“
CharSet.Unicode:所有字符串参数在传递到非托管实现之前都转换成Unicode字符;向DLL EnterPoint的名称中追加字母"W"
CharSet.Auto:这种转换就与平台有关(例如在windows NT上位unicode,而在windows 98上为Ansi)。
CharSet的默认值为Ansi
3 EnterPoint
指示要调用的DLL入口点的名称或序号
如果你的方法名不想与api函数同名的话,一定要指定此参数
例如我想调用user32.dll中的MessageBox函数但是我想以自己的名字MsgBox名字来命名的话
[DllImport("user32.dll",CharSet="CharSet.Auto",EnterPoint="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 SerLastError
指示被调用方法从属性化方法返回值之前调用win32 API SetLastError。
true指示调用方将调用SetLastError,默认为false。运行时封送拆收器调用GetLastError并缓存返回的值,以防其他API调用重写。
二 参数类型转换
C++ | C# |
DWORD | int |
WORD | Int16 |
字符串指针类型 | string |
句柄(handle、hWnd) | IntPtr |
结构或者类 | 要先用StructLayout特性限定声明结构或类 |
1 Explicit
用于控制每个数据成员的精确位置。利用Explicit,每个成员必须使用FieldOffsetAttribute指示此字段在类型中的位置:
[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;
}
2 Sequential
用于强制将成员按出现的顺序进行顺序布局
例如针对API中的OSVERSIONINFO结构,在.net中定义类或者结构的例子如下
API原型
* 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;
}
注意:结构作为参数的时候,一般前面要加上ref修饰符,否则会出现错误:对象的引用没有指定对象的实例