天天看點

Win32 API 入門

亂彈:

在學習的時候,人們對結構陌生而且複雜的東西總是心存恐懼。比如學習《植物學》,如果一上來就讓你研究被子植物雌花的構造,那你肯定當場暈菜;又或如學習

《有機化學》,不由分說先讓你分析一下“進階脂肪酸鈉”分子中的共價鍵/離子鍵結構,我保證你下半輩子都不再打算學化學。然而,雌花結構之美,大分子結構

之玄妙是我們無法想象的--在電子顯微鏡下,一個一個細胞如冰雕般晶瑩,如玉石般溫潤,如晚霞般五彩缤紛;在原子級别上,大分子中的電子雲會在不同的能量

級下顯現出各種奇幻的形狀來……為什麼這些美輪美奂的知識卻少人有問津呢?原因就在于他們的複雜,複雜就代表着難度。其實,難度并不是問題,問題在學學習

的方法,如果方法不正确那麼就會“難上加難”。真不知道有多少科學天才正是因為學習方法不正确,而在一次一次失敗的打擊下喪失了熱情與愛好,與攀登科學的

最高峰、摘取科學皇冠上的明珠失之交臂。

那麼,學習複雜知識最好的辦法是什麼呢?其實答案很簡單:從它的發展/演變曆史入手開始學習。想了解被子植物雌花的結構,那就要從單細胞植物(比如藍藻)

學起,然後是多細胞植物,然後是細胞的分化,細胞分化為器官,器官中有“葉”,葉上長了“孢子”就有了裸子植物,葉子卷曲起來保護這些孢子就成了“花”的

雛形,花(為了吸引蟲蟲們來Happy)形成了之後孢子演變成種子--被子植物誕生了。研究有機大分子也一樣,必須從最最簡單的有機分子--甲烷分子--

的一個C和四個H開始學習,然後你會看到乙烷……然後去掉幾個H,再加上O,你就得到酒精(乙醇)了,有意思吧!不過,酒,并不是這麼造滴~~~~酒精是

可以燃燒滴,燃燒是劇烈的氧化反應(比燃燒更劇烈的是爆炸),我們身體裡的有機燃料(比如血糖和脂肪)也會氧化,不過這種氧化非常緩慢,既保證了我們可以

從中獲得足夠的熱量維持生命、運動和新陳代謝,又保證了我們不至于成為烤全羊或者“肉彈”……血糖氧化不完全就可能産生乳酸,存留在你的肌肉裡就會讓你“

酸疼酸疼”的,直到它們被你的血液從肌肉中洗幹淨……在這些有機小分子上加上一個一個的C原子,就會形成有機大分子,比如脂肪酸分子。脂肪酸分子大了,就

稱之為“進階脂肪酸分子”,進階脂肪酸分子與鈉鹽反應就生成了進階脂肪酸鈉,有了進階脂肪酸鈉,我們就造出了--肥皂,沒錯,是肥皂。是以,如果你看到肥

皂盒上印有“使用進階脂肪酸鈉制造”一定要心知肚明“此進階非彼進階”,這個“進階”是指C鍊的長度,跟肥皂的品質沒關系,隻要是肥皂就肯定是用“進階脂

肪酸鈉”制造的,否則的話那是洗衣粉。

推而廣之,要想學習微積分,你必須從1+1開始;要想研究社會現狀,你必須從“夏商與西周,東周分兩段……”開始;要想論證“奇點”的存在或者“時間旅行

”的可能性,你必須從v=s/t開始……摸清一個事物的來龍去脈才能領略它的複雜之美。從簡單到複雜,每個環節都不能缺失,大概考古學、天文學、地質學的

魅力就在于此吧。

正文:

一.為什麼要學Win32

要回答這個問題,我們就要先搞清楚我們是站在Windows程式開發曆史的哪個階段。當紅的C#及.NET平台技術是建立在“程式集

”(Assembly)子產品上的,這是一種比COM更加進階的封裝形式,據說一開始叫“COM3”來着,可能是Bill不太樂意他的他的Windows老

在COM上打轉轉,于是就叫“.NET

Framework”了吧。在Assembly之前的封裝形式是“COM+”(COM2乎?),在COM+之前自然就是COM封裝了,COM前是OLE封

裝,OLE之前……呃……就沒有封裝了,隻有赤裸裸的Win16/Win32函數可以調用。前面提到的所謂“封裝”就是人們發現有些Win32函數總是一

起使用或總是按一定的結構使用(稱之為“複用”),于是就把它們“攢”起來,形成一個子產品。拜C++語言的OO能力所賜,C語言形式的Win32函數被封

裝在稱為“類”的子產品裡,形成了MFC(Microsoft)及OWL(Borland)等類庫,并以COM元件的形式安裝在使用者的計算機裡供使用者和開發

人員使用。在COM的基礎上又發展出了COM+,其本質仍然是對Win32函數的封裝。COM+之後就是.NET

Framework的Assembly了,它是迄今為止對Win32函數最進階别的封裝了……你基本已經看不到Win32函數的影子了,這就是為什麼我們

說.NET/C#開發不是底層開發的原因。

OK,我們暫且稱基于.NET Framework的程式開發為“第三代Windows程式開發”,基于COM的程式開發(如VC/VB)為“第二代Windows程式開發”,基于Win32函數的程式開發為“第一代Windows程式開發”。

由此可見,無論是想掌握COM程式設計還是.NET

Framework程式設計的真谛,你遲早是要回來學《Windows程式考古學》的。因為,有些問題由于封裝的太厚了,你可能找不到答案--你隻可能在

Win32級别上去找答案。隻有透徹地學習了Win32程式設計之後,你方能體驗到腳踏實地、豁然開朗的感覺,放能體驗那種恍然間的開悟。

Now, let’s go.去剖析一下一個最簡單的Win32程式。

二.熱身運動

    一上來就直接看Win32程式,我怕會吓到你,是以我們先從一個指令行程式開始。以這個程式來示範一下一個Program是如何進化的。

[一級]

main()

{

}

解說:這恐怕是最簡單的C語言程式了--隻有一個main入口點函數,當然,它什麼也不做。

[二級A]

void main()

解說:在[一級]的基礎上,明确地指出了主函數沒有傳回值。沒有傳回值對程式的運作結果不好把握,是以這一支進化到此為止。

[二級B]

int main()

     return 0;

說:其實這是[一級]的完×××式,就算你不寫,計算機也會隐式為你添加int傳回類型和在執行完之後return一個零。注意哦!不寫傳回值類型的C語言

函數預設是傳回int型值,而不是無傳回值的void型函數。詳細資訊你可以去ISO-C90/C99裡去查。不過值得注意的是:C++語言不支援預設的

int型傳回值和return 0,這就意味着,如果你的源檔案是以.c作為擴充名,加不加int和return

0都沒有關系,若是以.cpp為擴充名,你将有可能收到一個warning,不過,程式應該能繼續運作。

[X級]

int main(int argc, char* argv[])

說:在[二級]的基礎上添加了main函數的參數。一個非常重要的而且你必須要知道的一點就是:main入口點函數的參數不像程式内成員函數的參數,成員

函數的參數是由設計程式的程式員“手動”傳遞進去的,也就是程式員調用函數則程式員負責向函數傳遞參數。而main函數不是由程式員調用的,而是程式編譯

完成并傳遞使用者後,使用者通過作業系統來調用的(比如輕按兩下程式的圖示或者在指令行裡輸入程式的名字),是以,main函數的參數不是程式員在設計期能傳遞

的,隻能在main函數被系統調用時,由系統傳遞給它。簡言之就是:誰調用,誰傳參。

[四級]

#include <stdio.h>

解說:添加了#include<stdio.h>這句預編譯指令,注意:這是一句指令,而不是語句,是以沒有分号結尾。

[五級]

     //聲明了一些變量

     int a=100,b=200,x=300,y=400,temp=0;

     //交換a,b的值

     temp=a;

    a=b;

    b=temp;

    //交換x,y的值

     temp=x;

     x=y;

     y=temp;

     //輸出結果

     printf("a=%d,b=%d,x=%d,y=%d\n", a,b,x,y);

解說:用同樣的算法分别交換了a與b、x與y的值。

[六級]

void Exchange(int* arg1, int* arg2)

     int temp=0;

     temp = *arg1;

     *arg1 = *arg2;

     *arg2=temp;

     int a=100,b=200,x=300,y=400;

     //用函數交換值

     Exchange(&a,&b);

     Exchange(&x,&y);

解說:有操作複用的地方,就會有函數的出現。

[七級]

//前置函數聲明

void Exchange(int*, int*);

//函數實作

}   

解說:為了避免過多的子函數出現在main前而将main“埋沒”,采取了函數的“前置聲明”和“後置實作”。特别注意:前置聲明函數的時候,甚至可以隻給出參數的類型而不必給出參數的名稱。

[總結]

    至此,一個結構美觀,功能實用的小程式就進化完成了--從僅僅8個字元進化到十幾行。之是以給大家展示這樣一個程式,就是因為我們下面要看的Win32程式雖然複雜,但也是這樣一點一點進化來的。

三.正式開始

    熱身運動結束之後,我們就要正式剖析一個Win32的程式了。Win32的程式遠比指令行程式複雜,而且變量名和函數名也要長得多,入口點函數的參數也比較多也比較複雜……呃……入門的門檻比較高,做好心理準備哦!

[一級]:一個什麼都不幹的Win32程式

#include <windows.h>

WinMain( HINSTANCE hInstance,

         HINSTANCE hPrevInstance,

         PSTR szCmdLine,

         int iCmdShow)

說:比起指令行下那個隻有8個字元的最簡單程式來,Win32最簡單的程式也足夠複雜了。首先,#include<windows.h>指令

是絕不能缺少的(就算以後你在程式中沒有直接include這個windows.h檔案,那麼也一定是通過别的.h檔案間接地包含了它),不要指望編譯器

會自動為你添加這一句。其次,入口點函數的名稱也不再是main而是WinMain,而且WinMain也不像main那樣能夠支援有參數和無參數兩種形

式,WinMain函數隻有一種形式,那就是接收4個參數(參數的資料類型怪怪的,如果想知道具體是什麼類型,可以參見本人的另一篇掘作《Windows

資料類型探幽--千回百轉你是誰?》)。目前,最重要的是你要盯緊那第一個參數,也就是HINSTANCE類型的hInstance變量。

[二級]

int WINAPI WinMain (HINSTANCE hInstance,

                 HINSTANCE hPrevInstance,

                 PSTR szCmdLine,

                 int iCmdShow)

     return 0 ;

說:在[一級]的基礎上,除了像main一樣添加了int傳回值類型和return

0之外,還添加了一個WINAPI修飾符。這個宏(如果還不了解什麼是“宏”,請學習C/C++語言基礎知識)的實際值是

__stdcall,__stdcall是Microsoft公司對C/C++語言擴充時添加的Keywork,這個Keywork是專門用于呼叫

Win32

API時使用的(是以宏的名字叫“WINAPI”),而且在出現這個Keywork的時候,被修飾函數的參數傳遞順序是從右向左,被修飾函數被調用完後,

還要負責清理自己所占用過的棧記憶體--這些不了解不要緊,并不影響我們的入門學習。

     MessageBox(NULL, TEXT("Hello, Win32!"), TEXT("問候"), MB_OK) ;

     MessageBox(NULL, L"Hello, Win32!",L"問候",0);

    return 0 ;

說:這次是添加了核心代碼(上下兩句其實是完全一樣的,隻是上面一句使用了預先定義的宏友善了記憶,而下面一句是“原始面貌”)。MessageBox函

數會讓程式彈出一個消息框,第一個參數是指出哪個窗體擁有這個消息框,我們的程式還沒有窗體,是以隻能用一個NULL值,接下來的兩個不說你也應該看出

來,一個是内容,一個是标題。不過要注意,由于是32位程式設計,是以要用L(即TEXT()宏的原形)來把16位字元串轉換成32位字元串。最後一個參

數是消息框的按鈕數量--MB_OK就是隻有一個OK按鈕,對應的值是0;MB_YESNO就是有Yes和No兩個按鈕,對應的值是4……總之,用記宏比

你記沒有形象的整數值要友善多了

繼續閱讀