C#中挪用Windows API的技巧要點解釋。本站提示廣大學習愛好者:(C#中挪用Windows API的技巧要點解釋)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中挪用Windows API的技巧要點解釋正文
在.Net Framework SDK文檔中,關於挪用Windows API的指導比擬零碎,而且個中稍周全一點的是針對Visual Basic .net講述的。本文將C#中挪用API的要點聚集以下,願望給未在C#中應用過API的同伙一點贊助。別的假如裝置了Visual Studio .net的話,在C:\Program Files\Microsoft Visual Studio .NET\FrameworkSDK\Samples\Technologies\Interop\PlatformInvoke\WinAPIs\CS目次下有年夜量的挪用API的例子。
1、挪用格局
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 來檢索毛病代碼。
2、參數類型:
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)]
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中OSVERSIONINFO構造,在.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 );
3、若何包管應用托管對象的平台挪用勝利?
假如在挪用平台 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 );