c#和WIN32 調用

作者:劉鐵猛
日期: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 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 TypesSpecificationCLR Type
char, INT8, SBYTE, CHAR8-bit signed integerSystem.SByte
short, short int, INT16, SHORT16-bit signed integerSystem.Int16
int, long, long int, INT32, LONG32, BOOL, INT 32-bit signed integerSystem.Int32
__int64, INT64, LONGLONG64-bit signed integerSystem.Int64
unsigned char, UINT8, UCHAR, BYTE8-bit unsigned integerSystem.Byte
unsigned short, UINT16, USHORT, WORD, ATOM, WCHAR, __wchar_t16-bit unsigned integerSystem.UInt16
unsigned, unsigned int, UINT32, ULONG32, DWORD32, ULONG, DWORD, UINT32-bit unsigned integerSystem.UInt32
unsigned __int64, UINT64, DWORDLONG, ULONGLONG64-bit unsigned integerSystem.UInt64
float, FLOATSingle-precision floating pointSystem.Single
double, long double, DOUBLEDouble-precision floating pointSystem.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類庫好好翻翻吧,會有很多驚喜哦!
??????? 最后,用一個例子結束我們的文章吧!
????????例子是這樣滴~~~~~
??????? 那是在很久很久以前,我給一個公司寫程序用來控制用戶登錄,在登錄之前,用戶不能把鼠標移出登錄窗體,因為要控制鼠標,所以我首先想起了調用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,我很想知道我的文章都去哪里了.謝謝.

轉載于:https://www.cnblogs.com/C-CHERS/p/4875501.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272853.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272853.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272853.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【干貨】微信公眾號運營必備工具(完整版)

做微信公眾號運營最基本的要素有兩點:一是內容要強大(內),二是排版要美觀(外)。做好前者,你需要有充足的知識儲備以及精彩獨到的文筆作為支撐,而做好后者則相對簡單許多,…

java定時線程池_java 定時器線程池(ScheduledThreadPoolExecutor)的實現

前言定時器線程池提供了定時執行任務的能力,即可以延遲執行,可以周期性執行。但定時器線程池也還是線程池,最底層實現還是ThreadPoolExecutor,可以參考我的另外一篇文章多線程–精通ThreadPoolExecutor。特點說明1.構造函數public…

iOS 關于關鍵字高亮

- (NSMutableAttributedString *)colorStr: (NSString *)originalStr // originalStr : 需要高亮傳入的字符串 { NSMutableAttributedString *dataStr [[[NSMutableAttributedString alloc] initWithString:originalStr] autorelease]; for (int i 0; i < originalStr.l…

成功,要“借力”,不要“盡力”(深刻!)

01每個人都喜歡成功&#xff0c;卻又時常感覺自己力不從心一個小男孩在院子里搬一塊石頭&#xff0c;父親在旁邊鼓勵&#xff1a;“孩子&#xff0c;只要你全力以赴&#xff0c;一定搬得起來&#xff01;”但是石頭太重&#xff0c;最終孩子也沒能搬起來。他告訴父親&#xff1…

java 網站開發實例_完整的javaweb項目

【實例簡介】主要功能有以下幾個&#xff1a;1.用戶注冊 2.用戶登錄 3.用戶列表展示 4.用戶信息修改 5.用戶信息刪除【實例截圖】【核心代碼】javaweb└── javaweb├── src│ └── com│ ├── dao│ │ ├── UserDaoImpl.java│ │ └── UserDao.java│…

Nginx+Php-fpm+MySQL+Redis源碼編譯安裝指南

說明&#xff1a;本教程由三部分組成如下&#xff1a; 1. 源碼編譯安裝Nginx 2. 源碼編譯安裝php以及mysql、redis擴展模塊 3. 配置虛擬主機 文中所涉及安裝包程序均提供下載鏈接&#xff0c;歡迎使用 執行環境以及前置條件&#xff1a;Ubuntu 12.04 LTS 已安裝…

NetFramework各個版本的特性筆記

我的博客&#xff1a;http://www.cnblogs.com/hgmyz/p/6916064.html公式記憶&#xff1a;.Net 2.0CLRBCLC#(VB.Net)Win FormWeb Form.Net 3.0.Net 2.0WCFWPFWFWCS.Net 3.5.Net 3.0Asp.Net AjaxSliverlightLinq.Net 4.0 增加了并行的支持&#xff0c;與舊的Framwork并行工作。默…

從0開始學Java——JSPServlet——HttpServletRequest相關的幾個路徑信息

在HttpServletRequest中有幾個獲取路徑的接口&#xff1a;getRequestURI/getContextPath/getServletPath/getPathInfo 這些接口互相之間有什么區別&#xff0c;通過下面這段代碼就可以分辨清楚了&#xff1a; 1 WebServlet("/hello.view")2 public class FirstServle…

C#編譯和運行過程圖例

一張圖&#xff0c;描述C#編譯和運行過程&#xff0c;比較容易記憶理解

java 不重啟部署_編譯Java類后不重啟Tomcat有兩種方式:熱部署、熱加載

不重啟Tomcat有兩種方式&#xff1a;熱部署、熱加載熱部署&#xff1a;容器狀況在運行時重新部署整個項目。這類環境下一般整個內存會清空,重新加載&#xff0c;這類方式有可能會造成sessin丟失等環境。tomcat 6確實可以熱部署了,而且對話也沒丟.熱加載&#xff1a;最好是在調試…

修改mysql的用戶密碼

修改的用戶都以root為列。一、擁有原來的myql的root的密碼&#xff1b; 方法一&#xff1a; #mysql -u root mysql> SET PASSWORD FOR rootlocalhost PASSWORD(newpass); 方法二&#xff1a;在mysql系統外&#xff0c;使用mysqladmin# mysqladmin -u root -p password &quo…

C#中的堆和棧理解

引言&#xff1a;程序運行時&#xff0c;它的數據必須存在內存中&#xff0c;一個數據需要多大內存、存儲在什么地方以及如何存儲都依賴于該數據的數據類型。1、什么是棧棧是一個內存數組&#xff0c;是一個LIFO&#xff08;Last-In-First-Out 后進先出&#xff09;的數據結構。…

java sessionmanager_java.lang.IllegalStateException:沒有SessionManager

你錯過了3件事.Main.javaimport org.eclipse.jetty.server.Server;import org.eclipse.jetty.server.handler.ContextHandler;import org.eclipse.jetty.server.session.HashSessionIdManager;import org.eclipse.jetty.server.session.HashSessionManager;import org.eclipse.…

什么是鏈表

鏈表是一種物理存儲單元上非連續、非順序的存儲結構&#xff0c;數據元素的邏輯順序是通過鏈表中的指針鏈接次序實現的。鏈表由一系列結點&#xff08;鏈表中每一個元素稱為結點&#xff09;組成&#xff0c;結點可以在運行時動態生成。每個結點包括兩個部分&#xff1a;一個是…

C# 基礎:Sealed、new、virtual、abstract、override的理解

目錄 1、sealed 2、new 3、virtual 4、abstract 5、override 1、sealed 密封類不能被繼承&#xff0c;密封方法可以重寫基類中的方法&#xff0c;但其本身不能在任何派生類&#xff08;子類&#xff09;中 進一步重寫。當應用于屬性或者方法時&#xff0c;sealed 修飾符必須始終…

梁興珍 java_數據結構與算法_Java語言

第1章 綜述1.1 數據結構和算法能起到什么作用&#xff1f;1.2 數據結構的概述1.3 算法的概述1.4 一些定義1.5 面向對象編程1.6 軟件工程1.7 對于C程序員的Java1.8 Java數據結構的類庫第2章 數組2.1 Array專題Applet2.2 Java中數組的基礎知識2.3 將程序劃分成類2.4 類接口2.5 Or…

Yii 2.0: yii2-highcharts-widget創建餅狀圖

安裝 The preferred way to install this extension is through composer. 項目根目錄下執行&#xff1a; php composer.phar require --prefer-dist miloschuman/yii2-highcharts-widget "*"或者在composer.json中添加 "miloschuman/yii2-highcharts-widget&qu…

【原創】C#中的抽象類(abstract class)和接口(interface)的比較

在C#中抽象類和接口是兩個相當重要的概念&#xff0c;深入理解對C#程序員是非常必要的&#xff0c;現總結如下&#xff1a;一、抽象類的特點&#xff1a;1、抽象方法只用于方法的聲明并不包含方法的實現&#xff0c;可以看作沒有實現實體的虛方法。2、抽象類不能進行實例化。3、…

U3D 腳本添加和獲得對象

有時候&#xff0c;一開始可能沒有對象&#xff0c;而是由于某種觸發&#xff0c;產生的一個對象&#xff0c;這里講解下&#xff0c;如何通過腳本來創建一個對象&#xff1a; 這是通過腳本創建一個立方體&#xff1a; using UnityEngine; using System.Collections;public cla…

50條超精辟的經典語錄:嘩眾,可以取寵,也可以失寵!

在人生道路上給自己定位很重要&#xff0c;不要苛求自己達到不可能達到的高度。我們能把每一件平凡的事做好就是不平凡&#xff0c;把每一件簡單的事做成功就是不簡單。1.我們只有一個地球&#xff0c;所以你要愛護地球&#xff1b;地球上只有一個我&#xff0c;所以你也要愛護…