Windows API

Windows API

Windows 這個多作業系統除了協調應用程序的執行、分配內存、管理資源之外,它同時也是一個很大的服務中心,調用這個服務中心的各種服務(每一種服務就是一個函數),可以幫應用程式達到開啟視窗、描繪圖形、使用周邊設備等目的,由於這些函數服務的對象是應用程序(Application),所以便稱之為 Application Programming Interface,簡稱 API 函數。WIN32 API也就是Microsoft Windows 32位平台的應用程序編程介面。

簡介


基本信息

Windows API 就是Windows應用程序介面,是針對Microsoft Windows操作系統家族的系統編程介面,這樣的系統包括Windows 8.1、Windows 8、Windows 7、Windows VistaWindows XP、Windows Server 2012、Windows 2008 R2 、Windows Server 2003、Windows 2000、Windows 95、Windows 98、Windows Me(Millennium Editon)和Windows CE等幾乎所有版本。
其中32位Windows操作系統的編程介面稱為 Win32 API,以便與以前16位版本Windows編程介面(16位Windows API)區別開來。

函數分類

Windows API包括幾千個可調用的函數,它們大致可以分為以下幾個大類:
基本服務;
組件服務;
用戶界面服務;
圖形多媒體服務;
消息和協作;
網路;
Web服務。

發展現狀


當Windows操作系統開始佔據主導地位的時候,開發Windows平台下的應用程序成為人們的需要。而在Windows程序設計領域處於發展的初期,Windows程序員所能使用的編程工具唯有API函數,這些函數是Windows提供給應用程序與操作系統的介面,他們猶如“積木塊”一樣,可以搭建出各種界面豐富,功能靈活的應用程序。所以可以認為API函數是構築整個Windows框架的基石,在它的下面是Windows的操作系統核心,而它的上面則是所有的華麗的Windows應用程序。
程序員想編寫具有Windows風格的軟體,必須藉助API,API也因此被賦予至高無上的地位。但是,如若沒有合適的Windows編程平台,那麼Windows開發是一項很複雜的工作。在可視化編程IDE出來之前,那時的Windows程序開發還是比較複雜的工作,程序員必須熟記一大堆常用的API函數,而且還得對Windows操作系統有深入的了解。然而隨著軟體技術的不斷發展,在Windows平台上出現了很多優秀的可視化編程環境,程序員可以採用“所見即所得”的編程方式來開發具有精美用戶界面和功能強大的應用程序。
這些優秀可視化編程環境操作簡單、界面友好(諸如VB、VC++、DELPHI等),在這些工具中提供了大量的類庫和各種控制項,它們替代了API的神秘功能,事實上這些類庫和控制項都是構架在WIN32 API函數基礎之上的,是封裝了的API函數的集合。它們把常用的API函數的組合在一起成為一個控制項或類庫,並賦予其方便的使用方法,所以極大的加速了Windows應用程序開發的過程。有了這些控制項和類庫,程序員便可以把主要精力放在程序整體功能的設計上,而不必過於關注技術細節。
實際上如果我們要開發出更靈活、更實用、更具效率的應用程序,必然要涉及到直接使用API函數,雖然類庫和控制項使應用程序的開發簡單的多,但它們只提供Windows的一般功能,對於比較複雜和特殊的功能來說,使用類庫和控制項是非常難以實現的,這時就需要採用API函數來實現。
這也是API函數使用的場合,所以我們對待API函數不必刻意去研究每一個函數的用法,那也是不現實的(能用得到的API函數有幾千個呢)。正如某位大蝦所說:API不要去學,在需要的時候去查API幫助就足夠了。但是,許多API函數令人難以理解,易於誤用,還會導致出錯,這一切都阻礙了它的推廣。

概要介紹


應用程序介面為:“‘計算機操作系統(Operating system)’或‘程序庫’提供給應用程序調用使用的代碼”。其主要目的是讓應用程序開發人員得以調用一組常式功能,而無須考慮其底層的源代碼為何、或理解其內部工作機制的細節。API本身是抽象的,它僅定義了一個介面,而不涉入應用程序如何實現的細節。
例如,圖形庫中的一組API定義了繪製指針的方式,可於屏幕上顯示指針。當應用程序需要指針功能時,可引用、編譯時鏈接到這組API,而運行時就會調用此API的實現(庫)來顯示指針。
應用程序介面是一組數量上千、極其複雜的函數和副程序,可讓程序設計師做很多任務作,譬如“讀取文件”、“顯示菜單”、“在視窗中顯示網頁”等等。操作系統的API可用來分配存儲器或讀取文件。許多系統應用程序藉由API介面來實現,像是圖形系統、資料庫、網路Web服務,甚至是在線遊戲。
應用程序介面有諸多不同設計。用於快速執行的介面通常包括函數、常量、變數與數據結構。也有其它方式,如通過解釋器,或是提供抽象層以遮蔽同API實現相關的信息,確保使用API的代碼無需更改而適應實現變化。
應用程序介面經常是軟體開發工具包(SDK)的一部分。

表達方式


Visual Basic
[Public|Private] Declare Function|Sub name Lib "libname" [Alias "aliasname"]([[Byval] variable [As type][,[Byval] variable [As type]]...]) [As type]
C #
[DllImport("libname",'Named Parameters')][public|private|internal] [Type] FunctionName(Type parameter1,Type parameter2...);

API舉例


C#中調用Windows 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、數值型直接用對應的就可。(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⑵]public ushort wMonth;
[FieldOffset⑷]public ushort wDayOfWeek;
[FieldOffset⑹]public ushort wDay;
[FieldOffset⑻]public ushort wHour;
[FieldOffset⑽]public ushort wMinute;
[FieldOffset⑿]public ushort wSecond;
[FieldOffset⒁]public ushort wMilliseconds;
}
下面是針對API中OSVERSIONINFO結構,在.net中定義對應類或結構的例子:
//.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;
}
此例中用到MarshalAs特性,它用於描述欄位、方法或參數的封送處理格式。用它作為參數前綴並指定目標需要的數據類型。例如,以下代碼將兩個參數作為數據類型長指針封送給
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 );

分類


Windows API所提供的功能可以歸為七類:
1.基礎服務(Base Services),提供對Windows系統可用的基礎資源的訪問介面。比如像:文件系統(file system)、外部設備(device)、進程(process)、線程(thread)以及訪問註冊表(Windows registry)和錯誤處理機制(error handling)。這些功能介面位於 16位Windows下的kernel.exe、krnl286.exe或krnl386.exe系統文檔中,以及32位Windows下的 kernel32.dll和advapi32.dll中。
2.圖形設備介面(GDI),提供功能為:輸出圖形內容到顯示器、印表機以及其他外部輸出設備。它位於16位Windows下的gdi.exe;以及32位Windows下的gdi32.dll。
3.圖形化用戶界面(GUI),提供的功能有創建和管理屏幕和大多數基本控制項(control),比如按鈕和滾動條。接收滑鼠和鍵盤輸入,以及其他與GUI有關的功能。這些調用介面位於:16位Windows下的user.exe,以及32位Windows下的user32.dll。從Windows XP版本之後,基本控制項和通用對話框控制項(Common Control Library)的調用介面放在comctl32.dll中。
4.通用對話框鏈接庫(Common Dialog Box Library),為應用程序提供標準對話框,比如打開/保存文檔對話框、顏色對話框和字體對話框等等。這個鏈接庫位於:16位Windows下的commdlg.dll中,以及32位Windows下comdlg32.dll中。它被歸類為User Interface API之下。
5.通用控制項鏈接庫(Common Control Library),為應用程序提供介面來訪問操作系統提供的一些高級控制項。比如像:狀態欄(status bar)、進度條(progress bars)、工具欄(toolbar)和標籤(tab)。這個鏈接庫位於:16位Windows下的commctrl.dll中,以及32位Windows下comctl32.dll中。。它被歸類為User Interface API之下。
6.Windows外殼(Windows Shell),作為Windows API的組成部分,不僅允許應用程序訪問Windows外殼提供的功能,還對之有所改進和增強。它位於16位Windows下的shell.dll中,以及32位Windows下的shell32.dll中(Windows 95則在 shlwapi.dll中)。它被歸類為User Interface API之下。
7.網路服務(Network Services),為訪問操作系統提供的多種網路 功能提供介面。它包括NetBIOS、Winsock、NetDDE及RPC等。