【.Net】從.NET平臺調用Win32 API

 

小序
        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 int Main()
     {
         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,也就是「空指針」。
第二個: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類庫好好翻翻吧,會有不少驚喜哦!
        最後,用一個例子結束咱們的文章吧!
    nbsp;    例子是這樣滴~~~~~
        那是在好久好久之前,我給一個公司寫程序用來控制用戶登陸,在登陸以前,用戶不能把鼠標移出登陸窗體,由於要控制鼠標,因此我首先想起了調用Win32 API中與Cursor相關的函數來——因而無論三七二11、花了九牛二虎之力調用了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》請感興趣的朋友本身閱讀。

安全

 

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

原文:http://blog.csdn.net/fantasiax/article/details/557351函數

相關文章
相關標籤/搜索