C#中的API32(轉貼)

來源:互聯網
上載者:User

作者:劉鐵猛
日期:2005-12-20
關鍵字:C# .NET Win32 API

著作權聲明:本文章受智慧財產權法保護,如果閣下想轉載,在轉載的時候煩勞閣下連同在下的姓名一起轉載,並向bladey@tom.com發一個Mail,我很想知道我的文章都去哪裡了.謝謝.

小序
        Win32 API可以直接控制Microsoft Windows的核心,因為API(Application Programming Interface)本來就是微軟留給我們直接控制Windows的介面。想玩兒嗎?呵呵,太難了。
        C#使用非常簡單,寫程式就像打拱豬,Sorry  -_-! ,搭積木一樣簡單。想玩兒嗎?呵呵,沒辦法直接控制Windows的核心。
        難道就沒有兩全其美的辦法嗎?當然不是!要不微軟的產品早就沒人買了。其實從C#(或者說.NET平台)調用Win32 API還是非常簡單滴~~~~今天偶們大家就一起來研究研究。

一.    基礎知識
        Win32 API是C語言(注意,不是C++語言,儘管C語言是C++語言的子集)函數集。C#語言與C語言是完全不同的(除了文法上比較像),所以,要想用C#語言調用C語言的Win32 API,要費上一番周折。首先我們就要準備一些基礎知識。
1. Win32 API函數放在哪裡?
        Win32 API函數是Windows的核心,比如我們看到的表單、按鈕、對話方塊什麼的,都是依靠Win32函數“畫”在螢幕上的,由於這些控制項(有時也稱組件)都用於使用者與Windows進行互動,所以控制這些控制項的Win32 API函數稱為“使用者介面”函數(User Interface Win32 API),簡稱UI函數;還有一些函數,並不用於互動,比如管理當前系統正在啟動並執行進程、硬體系統狀態的監視等等……這些函數只有一套,但是可以被所有的Windows程式調用(只要這個程式的許可權足夠高),簡而言之,API是為程式所共用的。為了達到所有程式能共用一套API的目的,Windows採用了“動態連結程式庫”的辦法。之所以叫“動態連結程式庫”,是因為這樣的函數庫的調用方式是“隨用隨取”而不是像靜態連結庫那樣“用不用都要帶上”。
        這裡不太好理解,不要緊,我們舉個小例子。我們把Windows比做一個遊樂場,而把在遊樂場裡玩兒的小孩比做一個一個程式。小孩在玩的過程中可能要喝水。我們有兩個辦法讓小傢伙們想喝水的時候就有水喝:1.給每個小傢伙配一個水壺,小傢伙們喝了的話就喝自己帶的水;2.給遊樂場配一個飲水機,誰渴了誰來喝。顯然,第二個方法要好得多,這體現在三個地方。第一,帶著水壺,小傢伙身體不靈活、玩不爽(影響程式的速度),況且這隻是帶了一個水壺,要是再帶上飯盒呢?還有輪滑、頭盔、創可貼、紗布……AK-47 My God,如果帶全了就趕上美國大兵了。所以遊樂園裡還是有個公用“倉庫”要來的方便,讓大家隨用隨取(動態連結)。第二,小傢伙們帶了那麼多東西,佔了遊樂場很多地方,讓遊樂場擁擠不堪,別的小朋友就進不來了(程式體積大,影響程式和系統的效能)。第三,如果某件物品升級了,比如水壺從一升的改為二升的,那麼每個小傢伙就必須go home去換新的(重新編譯器,由編譯器把新的靜態庫連結進程式主體裡),而第二種情況裡,只要遊樂場把自己倉庫裡的水壺換個型號,那麼所有小傢伙就都在同一時間擁有了大容量的水壺。(悟空!我就一會兒不在,你怎麼就亂丟東西?!打到小朋友多不好~~~~~)
        悟空已經急了,我就不再嘰嘰歪歪了……呃……Win32 API函數是放在Windows系統的核心庫檔案中的,這些庫在硬碟裡的儲存形式是.dll檔案。我們常用到的dll檔案是user32.dllkernel32.dll兩個檔案,還有其它一些dll檔案也非常重要,大家要在實踐中多積累經驗。
        我們知道Win32 API函數是放在dll檔案中了,但新問題又來了——我們怎麼調用它們呢?這些dll檔案是用C語言寫的,原始碼經C語言編譯器編譯之後,會以二進位可執行代碼形式存放在這些dll檔案中,就好像蘋果被打碎機打成果醬後裝在罐子裡一樣——你再也分不清哪個是你GF給你的,哪個是你老媽給你的一樣。為了能讓程式使用這些函數,微軟在發布每個新的作業系統的時候,也會放出這個系統的SDK,目前最新的是Win2003 SP1 SDK,據說Vista的馬上就要放出來,而且已經把UI的API從核心庫中分離出去以提高系統的穩定性了。SDK裡有一些C語言的標頭檔(.h檔案),這些檔案裡描述了核心dll檔案裡都有哪些Win32 API函數,在寫程式的時候,把這些.h檔案用#include"....."指令包含進你的程式裡,你就可以使用這些Win32 API了。至於程式是怎樣連結的,超出了本文的範圍——也超出了本人的知識範圍:D
         至此,如果你是C語言高手,已經可以使用Windows SDK去調教Windows了!不過,今天我們討論的是C#語言調用Win32 API的問題。我們現在已經知道API函數放在dll動態連結程式庫檔案裡,也知道C語言怎麼調用它們了,那麼C#語言怎麼辦呢?C#語言是不能使用C語言的.h檔案的。C#語言也使用dll動態連結程式庫,不過這些dll都是.NET版本的,具有“自描述性”,也就是自己肚子裡都有哪些函數都已經寫在自己的metadata裡了,不用再附加一個.h檔案來說明。現在,我們已經找到了問題的關鍵點:如何用.NET平台上的C#語言來調用Win32平台上的dll檔案。答案非常簡單:使用DllImport特性
二.  小試牛刀
        下面,就讓我們寫一個小程式,試一試如何用C#語言和DllImport特性來調用Win32 API。

using System;
using System.Runtime.InteropServices;
class Program
{
     [DllImport("User32.dll")]
     public static extern int MessageBox(int h, string m, string c, int type);

     static intMain()
     {
         MessageBox(0, "Hello Win32 API", "水之真諦", 4);
         Console.ReadLine();
         return 0;
     }
}

        建立一個C#的控制台程式,把VS自動產生的程式碼清空,把上面的代碼Copy過去就可以編譯執行了。讓我們剖析一下這個程式:
1. 要使用DllImport這個特性(特性也是一種類),必須使用這一句using System.Runtime.InteropServices;
,匯入“運行時->互動服務”。喔~~~~運行時的互動服務不就是“動態連結”嗎?感謝Microsoft!
2. 然後我們就可以製造一個DllImport類的執行個體,並把這個執行個體綁定在我們要使用的函數上了。“屬性類別”這種類非常怪——製造類執行個體的時候不使用MyClass mc = new MyClass();這種形式,而是使用[屬性類別(參數列表)]這種形式;屬性類別不能獨立存在,一定要用作修飾其它目標上(本例是修飾後面的一個函數),不同的特性可以用來修飾類、函數、變數等等;屬性類別執行個體在被編譯的時候也不產生可執行代碼,而是被放進metadata裡以備檢索。總之,你記住屬性類別很怪就是了,想瞭解更多就查查MSDN,懶得查就先這麼記——不懂慣性定律不影響你學騎單車。[DllImport("User32.dll")]是說我們要使用的Win32 API函數在User32.dll這個檔案裡。問題又來了:我怎麼知道那麼多API函數都在哪個dll檔案裡呢?這個你可以在MSDN裡查到,位置是Root->Win32 and COM Development->Development Guides->Windows API->Windows API->Windows API Reference->Functions by Category。開啟這頁,你會看到有很多API的分類,API全在這裡了。開啟一個分類,比如Dialog Box,在Functions段,你會看到很多具體的函數,其中就有上面用到的MessageBox函數,點擊進入。你將開啟MessageBox的詳細解釋和具體用法。它的名字、傳回值、參數類型盡收眼底、一覽無餘!而且很練英文哦~~~~在這一頁的底部,你可以看到一個小表格,裡面有一項“Minimum DLL Version   user32.dll”就是說這個函數在user32.dll裡。
3. 接下來就是我們的函數了。在C#裡調用Win32函數有這麼幾個要點。第一:名字要與Win32 API的完全一樣。第二:函數除了要有相應的DllImport類修飾外,還要聲明成public static extern類型的。第三:也是最變態的一點,函數的傳回值和參數類型要與Win32 API完全一致!這可難煞我們這群初學者——Win32的資料類型比較搞怪,比如什麼LPSTR、什麼HINSTANCE都是些蝦米東東呢?給大家一個小參考,我的Blog裡有《Windows資料類型探幽——千迴百轉你是誰?》系列拙文,可以查一下。另外在此,我從MSDN裡摘出一張表來,是常用Win32資料類型與.NET平台資料類型的對應表:
Figure 2 Non-Pointer Data Types

Win32 Types Specification CLR Type
char, INT8, SBYTE, CHAR 8-bit signed integer System.SByte
short, short int, INT16, SHORT 16-bit signed integer System.Int16
int, long, long int, INT32, LONG32, BOOL, INT 32-bit signed integer System.Int32
__int64, INT64, LONGLONG 64-bit signed integer System.Int64
unsigned char, UINT8, UCHAR, BYTE 8-bit unsigned integer System.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR, __wchar_t 16-bit unsigned integer System.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT 32-bit unsigned integer System.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG 64-bit unsigned integer System.UInt64
float, FLOAT Single-precision floating point System.Single
double, long double, DOUBLE Double-precision floating point System.Double
In Win32 this type is an integer with a specially assigned meaning; in contrast, the CLR provides a specific type devoted to this meaning.

        有了這些東西,我們就能把一個Win32 API函數轉成C#函數了。還拿MessageBox函數為例(看剛才給出的函數表),它的Win32原形如下:

int MessageBox(  HWND hWnd,   LPCTSTR lpText,    LPCTSTR lpCaption,  UINT uType );

函數名:MessageBox將保持不變。
傳回值:int 將保持不變(無論是Win32還是C#,int都是32位整數)
參數表:H開頭意味著是Handle,一般情況下Handld都是指標類型,Win32平台的指標類型是用32位來儲存的,所以在C#裡正好對應一個int整型。不過,既然是指標,就沒有什麼正負之分,32位都應該用來儲存數值——這樣一來,用uint(無符號32位整型)來對應Win32的H類型更合理。不過提醒大家一點,int是受C#和.NET CLR雙重支援的,而uint只受C#支援而不受.NET CLR支援,所以,本例還是老老實實地使用了int型。(肚子餓了……再堅持堅持……)
至於LPCTSTR是Long Pointer to Constant String的縮寫,說白了就是——字串。所以,用C#裡的string類型就對了。
修飾符:要求有相應的DllImport和public static extern

經過上面一番折騰,Win32的MessageBox函數就封裝成C#可以調用的函數了:

     [DllImport("User32.dll")]
     public static extern int MessageBox(int h, string m, string c, int type);

        好人做到底,我把四個參數的用處也說一下:
第一個:彈出的MessageBox的父視窗是誰。本例中沒有,所以是0,也就是“null 指標”。
第二個:MessageBox的內容。本例中是“Hello Win32 API”。
第三個:MessageBox的標題。本例中用的是本人Blog的名字——水之真諦——請大家不要忘記。
第四個:MessageBox上的按鈕是什麼,如果是0,那就只有一個OK,MessageBox太短了,你將看不全“水之真諦”四個字,於是偶改成了4,這樣就有兩個按鈕了。這些在MSDN的函數用法裡都有。不過,我還是非常推薦您閱讀一下本人的另一篇拙作《一個Win32程式的進化》 。
        至此,一個麻雀雖小、五毒俱全~~~Sorry  -_-! 五髒俱全的C#調用Win32 API的程式就分析完了。原理並不難吧!應屆生拿去蒙HR足夠了!真正見功底的地方是你使用MSDN、SDK、.NET Framework類庫VC/VC#的熟練程度。相信我——MSDN+SDK+VC/C#絕對足夠把Windows收拾得服服帖帖了:D
三. 真的有必要嗎?
         嘿嘿嘿嘿……看我的表情,我在壞壞地笑哦!你們都上當啦!操作Windows的底層不一定都要調用Win32 API滴~~~~(哪兒來的磚頭!!!)
         我想說的是:.NET Framework是對Win32 API的良好封裝,大部分Win32 API函數都已經封裝在了.NET Framework類庫的各個類裡了。如果說Win32 API函數是散落在地上的珍珠的話,那麼.NET Framework就是把這些珍珠按種類分放到了各個抽屜裡——讓我想起我媽來了——我的書放得滿地滿床的時候我總是能找到,她一收拾我就再也找不到了,鬱悶。唉……沒辦法,我們還是仔細把.NET Framework類庫好好翻翻吧,會有很多驚喜哦!
        最後,用一個例子結束我們的文章吧!
        例子是這樣滴~~~~~
        那是在很久很久以前,我給一個公司寫程式用來控制使用者登入,在登入之前,使用者不能把滑鼠移出登入表單,因為要控制滑鼠,所以我首先想起了調用Win32 API中與Cursor相關的函數來——於是不管三七二十一、花了九牛二虎之力調用了Win32 API中的ClipCursor()這個函數,效果還不錯。
        結果前兩天翻.NET Framework類庫的時候,發現System.Windows.Forms.Cursor類的Clip屬性就是專門做這個用的!差點沒把鼻子氣歪了……請大家自己動手建立一個C#的Windows程式,把下面的核心代碼貼到主表單的雙擊事件裡,試一試。做這個例子的目的就是要告訴大家:1.對類庫的瞭解程式直接決定了你編程的效率和品質——用類庫裡的組件比我們“從輪子造起”要快得多、安全得多。2.不到萬不得已,不要去直接調Win32 API函數——那是不安全的。

         private void Form1_DoubleClick(object sender, EventArgs e)
         {
              Rectangle r = new Rectangle(this.Left, this.Top, this.Width, this.Height);
              System.Windows.Forms.Cursor.Clip = r;
         }

        最後,大家一定非常想知道,.NET Framework都為我們封裝好了哪些Win32 API,OK,MSDN裡有一篇文章,專門列出了這些。文章的名字是《Microsoft Win32 to Microsoft .NET Framework API Map》請感興趣的朋友自己閱讀。
四.  感恩
        新年快到了,這篇文章也做為一份小小的禮物,一是博大家一樂,二是讓我們永遠銘記這幸福的時刻。
        送給對我有著知遇之恩的陸經理;
        送給劉瑩Lead感謝在工作中給予的指導和支援;
        送給我的Team夥伴樂蓮、王勇(Worksoft),沒有你們的協助,我是不可能開始工作的!
        送給我宿舍的兄弟張雄,沒有你,我可能要睡在城鐵站了。
        送給段瑋和陳寧,感謝你們組織的活動和培訓。
        送給陳曦、陳建、王勇、常勇、舜賢、挺挺、對面的女孩李芳,還有本組的JJMM,還有小朱……與你們共事是我最大的快樂!

        文章寫完鳥~~~~傾城MM大概也把飯做好鳥~~~~回家鳥~~~~

著作權聲明:本文章受智慧財產權法保護,如果閣下想轉載,在轉載的時候煩勞閣下連同在下的姓名一起轉載,並向bladey@tom.com發一個Mail,我很想知道我的文章都去哪裡了.謝謝.

看貼要回貼,不回貼,小心我用彈弓打你家玻璃!!!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.