亂彈:
在學習的時候,人們對結構陌生而且複雜的東西總是心存恐懼。比如學習《植物學》,如果一上來就讓你研究被子植物雌花的構造,那你肯定當場暈菜;又或如學習
《有機化學》,不由分說先讓你分析一下“進階脂肪酸鈉”分子中的共價鍵/離子鍵結構,我保證你下半輩子都不再打算學化學。然而,雌花結構之美,大分子結構
之玄妙是我們無法想象的--在電子顯微鏡下,一個一個細胞如冰雕般晶瑩,如玉石般溫潤,如晚霞般五彩缤紛;在原子級别上,大分子中的電子雲會在不同的能量
級下顯現出各種奇幻的形狀來……為什麼這些美輪美奂的知識卻少人有問津呢?原因就在于他們的複雜,複雜就代表着難度。其實,難度并不是問題,問題在學學習
的方法,如果方法不正确那麼就會“難上加難”。真不知道有多少科學天才正是因為學習方法不正确,而在一次一次失敗的打擊下喪失了熱情與愛好,與攀登科學的
最高峰、摘取科學皇冠上的明珠失之交臂。
那麼,學習複雜知識最好的辦法是什麼呢?其實答案很簡單:從它的發展/演變曆史入手開始學習。想了解被子植物雌花的結構,那就要從單細胞植物(比如藍藻)
學起,然後是多細胞植物,然後是細胞的分化,細胞分化為器官,器官中有“葉”,葉上長了“孢子”就有了裸子植物,葉子卷曲起來保護這些孢子就成了“花”的
雛形,花(為了吸引蟲蟲們來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……總之,用記宏比
你記沒有形象的整數值要友善多了