1. 程式人生 > >c# API大全(三)

c# API大全(三)

c# API大全(3) 
2009-11-15 16:09 
API(應用程式設計介面)是程式與處理器介面的命令集。最常用的就是在外部呼叫微軟WINDOWS內部的程序。WINDOWS API包括成千的你可以使用的函式、結構、常量。這些函式是用C語言寫的,在使用他們之前,你必須宣告。定義Dll的程序將相當的複雜,甚至比VB還複雜。你可以使用API Viewer工具得到API函式的宣告,但是必須注意的是,它的引數型別跟C#的不一樣。大部分的高階語言都支援API,微軟函式類庫(MFC)封裝了大部分的Win32 API。ODBC API對提高資料庫的操作速度大有好處。使用API,可以請求更底層的系統服務。API從簡單的對話方塊到複雜的加密運算都提供支援。開發者應該知道如何在他們程式中使用API API有許多型別,(針對不同的作業系統、處理器„„„„)OS specific API: 作業系統特有API: 每種作業系統都有一套公用API和專有API。比如:Windows NT 支援MS-DOS, Win16, Win32, POSIX (行動式作業系統介面),OS/2 console API ;同時Windows 95 supports MS-DOS, Win16 和Win32 API。Win16 和 Win32 API: WIN16 是基於16位的處理器,並使用16位的值,它是一個獨立的平臺。比如:你可以執行TSR 程式在MS-DOS環境下。WIN32 是基於32位的處理器,並使用32位的值。他可用於任何作業系統,它的使用範圍更廣。Win32 API has 32 prefix after the library name e.g. KERNEL32, USER32 etc? Win32 API的DLL一般都具有32的字尾,比如:KERNEL32, USER32等。所有的API都在下面3個DLL中實現的。Kernel User GDI 1. KERNEL 它的庫名是:KERNEL32.DLL,它是作業系統管理的API集Process loading. 載入程序Context switching. File I/O. 檔案操作Memory management. 記憶體管理比如:GlobalMemoryStatus 函式獲得目前系統物理虛擬記憶體的使用資訊。2. USER 在WIN32下,它的庫名是 USER32.DLL This allows managing the entire user interfaces such as 它管理全部的使用者介面,比如:Windows 視窗Menus 選單Dialog Boxes 對話方塊Icons etc., 圖示等比如:DrawIcon 畫一個圖示在指定的裝置上。3. GDI (Graphical Device Interface) 這個DLL是GDI32.dll,它負責影象的輸出,使用GDI繪出視窗,選單,對話方塊It can create Graphical Output. 輸出影象比如:CreateBitmap 函式建立一個指定寬度、高度和顏色格式的點陣圖。C#中API的工具對初學者是相當不錯的。在C#使用中使用API之前,你應該先知道C#中如何使用結構、型別轉換,安全與不安全程式碼等。使用複雜的api之前,我們先用一個簡單的MessageBox API作為列子。開啟一個C#工程,增加一個按鈕,在按鈕的點選事件中,我們將顯示一個資訊框。增加使用外部庫的名稱空間:using System.Runtime.InteropServices; 下面宣告API 
[DllImport("User32.dll")] DllImport屬性用來指定包含外部方法的動態連線庫的位置。 "User32.dll"指出了庫名,static 指明它不屬於特定的物件。extern 指明是一個外部的方法。帶有DllImport 屬性的方法必須帶有修飾符extern 。MessageBox 是一個漢數名,帶四個引數返回一個int型值。許多API使用結構來傳遞、返回引數,這樣可以減少複雜度。它也允許使用象MessageBox 函式那樣,使用固定的引數。在按鈕的點選事件中增加下面程式碼:protected void button1_Click (object sender, System.EventArgs e) { MessageBox (0,"APIMessage Box","API Demo",0); } 編譯並執行程式,點選按鈕以後,你就可以看到一個由API呼叫的資訊框。Using structure 使用結構API中經常使用複雜的結構。不過一旦你明白了他們,將是很簡單的。In next example we will use GetSystemInfo API which returns information about the current system. 下面的列子,我們用GetSystemInfo API得到當前系統的資訊。第一步:增加一個C#視窗,並在上面增加一個按鈕,在視窗內碼表增加一個名稱空間:using System.Runtime.InteropServices; 宣告 GetSystemInfo 的引數結構: [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint 
lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; }宣告API函式:[DllImport("kernel32")] static extern void GetSystemInfo(ref 
SYSTEM_INFO pSI); ref是一個標誌參量傳遞形式的關鍵字,它使傳入傳出的變數指向同一個變數(傳址傳遞)在按鈕點選事件中增加下面的程式碼,protected void button1_Click (object sender, System.EventArgs e) { try {SYSTEM_INFO pSI = new SYSTEM_INFO(); GetSystemInfo(ref pSI); Once you retrieve the structure perform operations on required parameter 比如:
listBox1.Items.Insert(0,pSI.dwActiveProcessorMask.ToString()); } catch(Exception er) { MessageBox.Show (er.Message); } }用Visual C#呼叫Windows API函式北京機械工業學院研00級(100085)冉林倉 Api函式是構築Windws應用程式的基石,每一種Windows應用程式開發工具,它提供的底層函式都間接或直接地呼叫了Windows API函式,同時為了實現功能擴充套件,一般也都提供了呼叫WindowsAPI函式的介面,也就是說具備呼叫動態連線庫的能力。Visual C#和其它開發工具一樣也能夠呼叫動態連結庫的API函式。.NET框架本身提供了這樣一種服務,允許受管轄的程式碼呼叫動態連結庫中實現的非受管轄函式,包括作業系統提供的Windows API函式。它能夠定位和呼叫輸出函式,根據需要,組織其各個引數(整型、字串型別、陣列、和結構等等)跨越互操作邊界。下面以C#為例簡單介紹呼叫API的基本過程: 動態連結庫函式的宣告  動態連結庫函式使用前必須宣告,相對於VB,C#函式宣告顯得更加羅嗦,前者通過 Api Viewer貼上以後,可以直接使用,而後者則需要對引數作些額外的變化工作。 動態連結庫函式宣告部分一般由下列兩部分組成,一是函式名或索引號,二是動態連結庫的檔名。  譬如,你想呼叫User32.DLL中的MessageBox函式,我們必須指明函式的名字MessageBoxA或MessageBoxW,以及庫名字
User32.dll,我們知道Win32 API對每一個涉及字串和字元的函式一般都存在兩個版本,單位元組字元的ANSI版本和雙位元組字元的UNICODE版本。 下面是一個呼叫API函式的例子: [DllImport("KERNEL32.DLL", 
EntryPoint="MoveFileW", SetLastError=true, CharSet=CharSet.Unicode, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)] public static extern bool MoveFile(String src, String dst);  其中入口點EntryPoint標識函式在動態連結庫的入口位置,在一個受管轄的工程中,目標函式的原始名字和序號入口點不僅標識一個跨越互操作界限的函式。而且,你還可以把這個入口點對映為一個不同的名字,也就是對函式進行重新命名。重新命名可以給呼叫函式帶來種種便利,通過重新命名,一方面我們不用為函式的大小寫傷透腦筋,同時它也可以保證與已有的命名規則保持一致,允許帶有不同引數型別的函式共存,更重要的是它簡化了對ANSI和Unicode版本的呼叫。CharSet用於標識函式呼叫所採用的是Unicode或是ANSI版本,ExactSpelling=false將告訴編譯器,讓編譯器決定使用Unicode或者是Ansi版本。其它的引數請參考MSDN線上幫助.  在C#中,你可以在EntryPoint域通過名字和序號宣告一個動態連結庫函式,如果在方法定義中使用的函式名與DLL入口點相同,你不需要在EntryPoint域顯示宣告函式。否則,你必須使用下列屬性格式指示一個名字和序號。[DllImport("dllname", EntryPoint="Functionname")] [DllImport("dllname", EntryPoint="#123")] 值得注意的是,你必須在數字序號前加“#” 下面是一個用MsgBox替換MessageBox名字的例子: [C#] using System.Runtime.InteropServices; public class Win32 
{ [DllImport("user32.dll", EntryPoint="MessageBox")] public static extern int MsgBox(int hWnd, String text, String caption, uint type); } 許多受管轄的動態連結庫函式期望你能夠傳遞一個複雜的引數型別給函式,譬如一個使用者定義的結構型別成員或者受管轄程式碼定義的一個類成員,這時你必須提供額外的資訊格式化這個型別,以保持引數原有的佈局和對齊。C#提供了一個StructLayoutAttribute類,通過它你可以定義自己的格式化型別,在受管轄程式碼中,格式化型別是一個用StructLayoutAttribute說明的結構或類成員,通過它能夠保證其內部成員預期的佈局資訊。佈局的選項共有三種:佈局選項 描述 LayoutKind.Automatic 為了提高效率允許執行態對型別成員重新排序。 注意:永遠不要使用這個選項來呼叫不受管轄的動態連結庫函式。 
LayoutKind.Explicit 對每個域按照FieldOffset屬性對型別成員排序 
LayoutKind.Sequential 對出現在受管轄型別定義地方的不受管轄記憶體中的型別成員進行排序。 傳遞結構成員 下面的例子說明如何在受管轄程式碼中定義一個點和矩形型別,並作為一個引數傳遞給User32.dll庫中的PtInRect函式, 函式的不受管轄原型宣告如下: BOOL PtInRect(const RECT *lprc, POINT pt); 注意你必須通過引用傳遞Rect結構引數,因為函式需要一個Rect的結構指標。 [C#] using System.Runtime.InteropServices; 
[StructLayout(LayoutKind.Sequential)] public struct Point { public int x; public int y; } [StructLayout(LayoutKind.Explicit] public struct Rect { [FieldOffset(0)] public int left; [FieldOffset(4)] public int top; [FieldOffset(8)] public int right; [FieldOffset(12)] public int bottom; } class Win32API { [DllImport("User32.dll")] public static extern Bool PtInRect(ref Rect r, Point p); } 類似你可以呼叫GetSystemInfo函式獲得系統資訊: ? using System.Runtime.InteropServices; 
[StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public uint dwOemId; public uint dwPageSize; public uint 
lpMinimumApplicationAddress; public uint lpMaximumApplicationAddress; public uint dwActiveProcessorMask; public uint dwNumberOfProcessors; public uint dwProcessorType; public uint dwAllocationGranularity; public uint dwProcessorLevel; public uint dwProcessorRevision; } [DllImport("kernel32")] static extern void GetSystemInfo(ref SYSTEM_INFO pSI); SYSTEM_INFO pSI = new SYSTEM_INFO(); GetSystemInfo(refpSI); 類成員的傳遞 同樣只要類具有一個固定的類成員佈局,你也可以傳遞一個類成員給一個不受管轄的動態連結庫函式,下面的例子主要說明如何傳遞一個sequential順序定義的MySystemTime類給User32.dll的GetSystemTime函式, 函式用C/C++呼叫規範如下: void GetSystemTime(SYSTEMTIME* SystemTime); 不像傳值型別,類總是通過引用傳遞引數. [C#] 
[StructLayout(LayoutKind.Sequential)] public class MySystemTime 
{ public ushort wYear; public ushort wMonth; public ushort wDayOfWeek; public ushort wDay; public ushort wHour; public ushort wMinute; public ushort wSecond; public ushort wMilliseconds; } class Win32API { [DllImport("User32.dll")] public static extern void GetSystemTime(MySystemTime st); } 回撥函式的傳遞: 從受管轄的程式碼中呼叫大多數動態連結庫函式,你只需建立一個受管轄的函式定義,然後呼叫它即可,這個過程非常直接。 如果一個動態連結庫函式需要一個函式指標作為引數,你還需要做以下幾步: 首先,你必須參考有關這個函式的文件,確定這個函式是否需要一個回撥;第二,你必須在受管轄程式碼中建立一個回撥函式;最後,你可以把指向這個函式的指標作為一個引數創遞給DLL函式,. 回撥函式及其實現: 回撥函式經常用在任務需要重複執行的場合,譬如用於列舉函式,譬如Win32 API 中的EnumFontFamilies(字型列舉), EnumPrinters(印表機), EnumWindows (視窗列舉)函式. 下面以視窗列舉為例,談談如何通過呼叫EnumWindow 函式遍歷系統中存在的所有視窗分下面幾個步驟: 1. 在實現呼叫前先參考函式的宣告 BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARMAM IParam) 顯然這個函式需要一個回撥函式地址作為引數. 2. 建立一個受管轄的回撥函式,這個例子宣告為代表型別(delegate),也就是我們所說的回撥,它帶有兩個引數hwnd和lparam,第一個引數是一個視窗控制代碼,第二個引數由應用程式定義,兩個引數均為整形。  當這個回撥函式返回一個非零值時,標示執行成功,零則暗示失敗,這個例子總是返回True值,以便持續列舉。 3. 最後建立以代表物件
(delegate),並把它作為一個引數傳遞給EnumWindows 函式,平臺會自動地把代表轉化成函式能夠識別的回撥格式。[C#] using System; using 
System.Runtime.InteropServices; public delegate bool CallBack(int hwnd, int lParam); public class EnumReportApp { [DllImport("user32")] public static extern int EnumWindows(CallBack x, int y); public static void Main() { CallBack myCallBack = new CallBack(EnumReportApp.Report); EnumWindows(myCallBack, 0); } public static bool Report(int hwnd, int lParam) { Console.Write("視窗控制代碼為"); Console.WriteLine(hwnd); return true; } } 指標型別引數傳遞:  在Windows API函式呼叫時,大部分函式採用指標傳遞引數,對一個結構變數指標,我們除了使用上面的類和結構方法傳遞引數之外,我們有時還可以採用陣列傳遞引數。 下面這個函式通過呼叫
GetUserName獲得使用者名稱 BOOL GetUserName( LPTSTR lpBuffer, // 使用者名稱緩衝區 LPDWORD nSize // 存放緩衝區大小的地址指標 );   [DllImport("Advapi32.dll", EntryPoint="GetComputerName", ExactSpelling=false, SetLastError=true)] static extern bool 
GetComputerName ( [MarshalAs(UnmanagedType.LPArray)] byte[] lpBuffer,  [MarshalAs(UnmanagedType.LPArray)] Int32[] nSize );  這個函式接受兩個引數,char * 和int *,因為你必須分配一個字串緩衝區以接受字串指標,你可以使用String類代替這個引數型別,當然你還可以宣告一個位元組陣列傳遞ANSI字串,同樣你也可以宣告一個只有一個元素的長整型陣列,使用陣列名作為第二個引數。上面的函式可以呼叫如下:byte[] str=new byte[20]; Int32[] len=new Int32[1]; len[0]=20; GetComputerName (str,len); 
MessageBox.Show(System.Text.Encoding.ASCII.GetString(str));  最後需要提醒的是,每一種方法使用前必須在檔案頭加上:  using System.Runtime.InteropServices