国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
《白手起家Win32SDK應(yīng)用程序》(完整版+目錄)

《白手起家Win32SDK應(yīng)用程序》

 

《白手起家Win32SDK應(yīng)用程序》

第一篇、預(yù)備知識(shí)

第二篇、創(chuàng)建Win32工程和主函數(shù)

第三篇、增加一個(gè)回調(diào)函數(shù)

第四篇、注冊(cè)一個(gè)窗口類

第五篇、利用已注冊(cè)的窗口類來創(chuàng)建一個(gè)窗口

第六篇、顯示你創(chuàng)建的窗口

第七篇、獲取消息及對(duì)消息缺省處理

第八篇、關(guān)閉窗口的同時(shí)退出程序

第九篇、窗口標(biāo)題欄上顯示自定義圖標(biāo)(手動(dòng)編輯代碼)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

第一篇、預(yù)備知識(shí)

 

                                    白云小飛
1 說在前面
    由于VC6及MFC的特點(diǎn),我們?cè)S多人從標(biāo)準(zhǔn)C++學(xué)習(xí)到VC6MFC應(yīng)用程序的編程學(xué)習(xí)的過度會(huì)有一個(gè)很大的夸躍,從而感到非常的吃力。
    究其原因之一:MFC類庫(kù)設(shè)計(jì)雖然精巧,但我們?cè)谑褂肕FC設(shè)計(jì)程序時(shí),會(huì)發(fā)現(xiàn)MFC到處是API函數(shù)的影子。MFC并沒有象Delphi的VCL類庫(kù),VB的控件庫(kù)一樣封裝得讓人幾乎完全不用知道還有Win32API函數(shù)及其操作原理,所以要想利用VC6的MFC編程,我認(rèn)為就一定要先學(xué)習(xí)如何直接用Win32API函數(shù)來編程。對(duì)API編程中的一些關(guān)鍵的概念和原理要有一定認(rèn)識(shí),這樣才會(huì)有一個(gè)比較平滑的過渡。以上就是我寫這個(gè)系列的初衷。

2 我假設(shè)你已有的知識(shí):
    這里我假設(shè)你已經(jīng)掌握了如下的知識(shí),如果你在如下方面知識(shí)有點(diǎn)不太清楚,那要去補(bǔ)一補(bǔ)羅,否則你看到相關(guān)的內(nèi)容時(shí)會(huì)有麻煩的。
    下面說是我對(duì)你知識(shí)的假設(shè):
2.1 Windows系統(tǒng)的文件、文件夾、路徑的概念
2.2 C語(yǔ)言的基本知識(shí)(基本以等級(jí)考試二級(jí)C語(yǔ)言為準(zhǔn),還要有所擴(kuò)充)
  2.2.1 指針的概念。
  2.2.2 函數(shù)指針概念。
  2.2.3 各種自定義類型(最重要的是struct類型)的概念。
  2.2.4 要知道函數(shù)的各種參數(shù)傳遞形式(值、地址、引用傳遞)。
  2.2.5 typedef及其應(yīng)用。
  2.2.6 #include及其應(yīng)用。
  2.2.7 十進(jìn)制、二進(jìn)制、十六進(jìn)制。
  2.2.8 按位與、或、非運(yùn)算的實(shí)質(zhì)。
  2.2.9 宏定義概念、使用及意義。
  (每個(gè)人總是學(xué)完了C或C++語(yǔ)法后才會(huì)開始用VC6進(jìn)行Windows編程學(xué)習(xí)的。但是你的基礎(chǔ)又是如何呢?這是一個(gè)關(guān)鍵。因此我對(duì)你的C知識(shí)做了具體的假設(shè)。)
2.3 會(huì)安裝VC6.0并安裝到一臺(tái)機(jī)上
2.4 VC6編譯界面的各組成部分及基本操作(至少會(huì)用VC6寫控制臺(tái)程序)。
2.5 VC6調(diào)試中至少要會(huì)設(shè)置斷點(diǎn)哦。
  (呵呵!我的要求不過份吧!)

3 還必須預(yù)備的知識(shí):
    以上知識(shí)是你看本系列的前提,不過我還要給你預(yù)備一下我們?cè)龠@個(gè)階段學(xué)習(xí)中會(huì)遇到的新東西。
3.1 你將會(huì)接觸到的Win32API函數(shù)庫(kù):
    以前的DOS下或Windows的控制臺(tái)程序下,你要在顯示器上輸出文字,要用printf(),或cout的函數(shù)對(duì)象來完。但如果你要顯示一個(gè)圖形或圖象或?yàn)槟愕某绦蛟O(shè)計(jì)一個(gè)圖形化的操作界面等等的,那可就慘了,一切都要你自已完成。復(fù)雜得很了!(唉!誰(shuí)叫DOS是字符界面的操作系統(tǒng)呢?。?br>    現(xiàn)在好了,在Windows下編程你可就輕松得多了。因?yàn)閃indows操作系統(tǒng)都為我們準(zhǔn)備好了,它提供給我們多達(dá)數(shù)千個(gè)函數(shù)(??!我要昏倒了。這么多的函數(shù)要學(xué)。),我們通過這些函數(shù)來操作Windows系統(tǒng)提供給我們的各種功能。比如我要在桌面上創(chuàng)建并顯示一個(gè)窗口。就只要調(diào)用幾個(gè)相關(guān)的被稱為API的函數(shù),讓W(xué)indows來幫助我們完成這些事。我們是通過這些函數(shù)與Windows系統(tǒng)交互的,所以這些函數(shù)被稱作Win32應(yīng)用程序接口函數(shù),簡(jiǎn)稱Win32API函數(shù)。
    請(qǐng)不用害怕喲!其實(shí),這么多的函數(shù)我們不必都馬上一一學(xué)過,只要掌握了不多的具有代表性的函數(shù)的使用方法,并知道大體API函數(shù)都提供了哪些功能就可以了。以后要用時(shí)再去查。
    Window擁有現(xiàn)成的各種各樣的系統(tǒng)功能,供我們的程序調(diào)用。那么又是通過什么方式來調(diào)用這些系統(tǒng)功能呢?原來,Window還現(xiàn)成提供一個(gè)接口,好讓我們的程序來使用這些系統(tǒng)功能,這個(gè)結(jié)口就是Win32API函數(shù)了(注:API是應(yīng)用程序接口的英文縮寫)。Win32API函數(shù)是我們的應(yīng)用程序與Windows系統(tǒng)交互的唯一途徑。
    我并不打算這時(shí)就介紹任何一個(gè)具體的API函數(shù)。你現(xiàn)在只要知道你又要接觸一個(gè)函數(shù)庫(kù)了——被稱為Win32API的函數(shù)庫(kù),如同你以前所學(xué)的C/C++函數(shù)庫(kù)。
    哈哈,這真是太好了,我們不用再象DOS一樣,自已來完成程序界面的繪制了。我們現(xiàn)在又增加一個(gè)全新的函數(shù)庫(kù),只要調(diào)用幾個(gè)相關(guān)API函數(shù),剩下的一切由Windows來完成就可以啦?。ó?dāng)然還有很多其它功能。)

3.2  “新”的數(shù)據(jù)類型:
    學(xué)完C、C++之后,我們就可以開始進(jìn)入VC6的Windows編程學(xué)習(xí)了。但是在接下來的學(xué)習(xí)中我們會(huì)發(fā)現(xiàn)在Windows編程中有許多“新”的數(shù)據(jù)類型。看下面:
    BOOL、BYTE、INT、UINT、WORD、DWORD、FLOAT、CHAR、LPSTR、HINSTANCE、HWND、HMENU、HICON等等。
    你看這些大寫的數(shù)據(jù)類型,你以前有見過嗎?還有很多哦!我們以后的學(xué)習(xí)過程中還會(huì)見到的。(呵呵!你可要有思想準(zhǔn)備了?。?br>    這真是讓我們初學(xué)者迷惑呀!難道VC6中對(duì)C/C++的基本數(shù)據(jù)類型又有重大的擴(kuò)充了嗎?
    其實(shí)不用害怕,只是用新瓶裝舊酒而已了。在VC6的windef.h頭文件中已有這些定義:
typedef int                 BOOL;
typedef unsigned char       BYTE;
typedef int                 INT;
typedef unsigned int        UINT;
typedef unsigned short      WORD;
typedef unsigned long       DWORD;
typedef float               FLOAT;
在winnt.h中有
typedef long LONG;
typedef char CHAR;
typedef CHAR *LPSTR, *PSTR;
    你看其中(粗體字)CHAR只不過是char 的別名而已,也就是說它們是等價(jià)的。只要你包含了相關(guān)的頭文件,然后你就可以這樣申明一個(gè)變量:
INT i;    //等同于int i;
CHAR a;   //等同于char a;
LPSTR pa;   //等同于char *pa;
    明白了嗎?
    我想你一定會(huì)問:為什么要這樣轉(zhuǎn)義呢?我們直接用int 、unsighed int、char等等不就行了嗎?我一句兩句也說不清,你只要知道,微軟這樣做一定是要道理的。
    哦!還有這些HINSTANCE、HWND、HMENU、HICON我沒說呢!今后你還會(huì)見到許多這樣以H為開頭的數(shù)據(jù)類型,下面就讓我在下一節(jié)的“句柄”概念中說給你聽。

3.3 “句柄”概念
    由windows系統(tǒng)創(chuàng)建出來的或加載的對(duì)象(如應(yīng)用程序進(jìn)程、線程、窗口、菜單、圖標(biāo)、光標(biāo)等等的對(duì)象),windows系統(tǒng)都會(huì)分配給它們一個(gè)唯一的標(biāo)識(shí)值,作為這些對(duì)象的標(biāo)志,稱之為句柄。我們程序中對(duì)這些對(duì)象的操作其實(shí)就是對(duì)其句柄的操作。請(qǐng)記住,句柄就是這些對(duì)象的“代號(hào)”了。
    在編程序中,我們需要用相應(yīng)的句柄變量來保存這些句柄值,那么用什么類型的句柄變量呢?
    就是我們前面提到過的HINSTANCE、HWND。
    像其它變量一樣(如:int a;)申明句柄變量,如下:
HINSTANCE  hst;  //hst變量可以保存某個(gè)應(yīng)用程序?qū)嵗匆粋€(gè)進(jìn)程)的句柄。
HWND  hwFirst;  //hwFrist變量可以保存某個(gè)窗體句柄。
HMENU hMenu;   //hMenu變量可以保存某個(gè)菜單句柄。
HICON hIcon;     //hIcon變量可以保存某個(gè)圖標(biāo)句柄。
    具體的使用讓我以后再慢慢與你道來啦。
    那么這些類型的實(shí)質(zhì)又是什么?
    目前,它們都只是一個(gè)int類型(小語(yǔ):我聽說微軟也許以后會(huì)改變它的類型)。不過不管怎樣,你現(xiàn)在只要把HINSTANCE、HWND、HMENU、HICON當(dāng)做是一個(gè)獨(dú)立的數(shù)據(jù)類型就可以了。

3.4 消息標(biāo)識(shí)
    Windows系統(tǒng)是一個(gè)基于消息的系統(tǒng)。這樣的機(jī)制導(dǎo)致我們的程序與以往DOS下的程序流程會(huì)有很大的不同。(這可是很考我們的智慧嘍?。?br>    
    從軟件使用者角度看一個(gè)Win32窗口程序運(yùn)行的過程:
    1) 我們運(yùn)行一個(gè)應(yīng)用程序,程序創(chuàng)建并顯示一個(gè)我們想要的程序窗口。
    2) 當(dāng)我們對(duì)窗口進(jìn)行操作時(shí)(如單擊、雙擊、右擊、按下鍵盤、最大化、最小化、關(guān)閉窗口等等),程序會(huì)完成特定的操作,如:?jiǎn)螕糇畲蠡?、最小化按鈕時(shí),窗口會(huì)最大化、最小化操作;對(duì)窗口中菜單項(xiàng)的選取時(shí),會(huì)完成該菜單的相應(yīng)功能。

    從程序員的角度看一個(gè)Win32窗口程序運(yùn)行的過程:
    1) 我們運(yùn)行一個(gè)應(yīng)用程序,程序中我們通過Win32API函數(shù)創(chuàng)建并顯示一個(gè)我們想要的程序窗口。(由我們的程序來調(diào)用函數(shù)實(shí)現(xiàn))
    2) 當(dāng)我們對(duì)窗口進(jìn)行操作時(shí)(如單擊、雙擊、右擊、按下鍵盤、最大化、最小化、關(guān)閉窗口等等),窗口會(huì)自動(dòng)產(chǎn)生一系列相應(yīng)的消息(這是由操作系統(tǒng)實(shí)現(xiàn)的)。
    3) 具體地講:當(dāng)我們改變窗口大小時(shí),會(huì)產(chǎn)生WM_SIZE消息;單擊關(guān)閉按鈕關(guān)閉窗口時(shí),會(huì)產(chǎn)生WM_CLOSE消息;選取某一菜單項(xiàng)時(shí),會(huì)產(chǎn)生WM_COMMAND消息;按下鍵盤時(shí),會(huì)產(chǎn)生WM_CHAR、WM_KEYDOWN、WM_KEYUP消息;單擊鼠標(biāo)左鍵時(shí),會(huì)產(chǎn)生WM_LBUTTONUP、WM_LBUTTONDOWN消息等等。啊,很多很多,我也不必全部羅列出來了。(我說過了,這些都是由操作系統(tǒng)實(shí)現(xiàn)的)
    4) windows系統(tǒng)會(huì)將這些消息排入我們窗口所在線程的消息隊(duì)列中(你會(huì)明白線程是什么嗎?)(也由Window操作系統(tǒng)實(shí)現(xiàn)),這樣我們的程序才有機(jī)會(huì)獲取并處理這些產(chǎn)生的消息。
    5) 我們的程序可以通過Window操作系統(tǒng)提供的API函數(shù)來獲取這些消息及相關(guān)的信息。然后通過我們學(xué)過的條件判斷語(yǔ)句來判斷是什么消息及其相關(guān)的操作信息并可編寫相應(yīng)的程序代碼,從而實(shí)現(xiàn)對(duì)窗口操作的不同反應(yīng)。(由我們的程序來實(shí)現(xiàn))
    看上述的過程描述,你可能要有點(diǎn)的抽象思維能力了。你現(xiàn)在只要有對(duì)程序流程有如上的大體認(rèn)知就可以了。慢慢地我會(huì)將上述流程變成確實(shí)的程序代碼噢!
    (等等,還是有個(gè)問題:這些WM_CLOSE、WM_COMMAND、WM_CHAR、WM_KEYDOWN、WM_KEYUP、WM_LBUTTONUP、WM_LBUTTONDOWN等等的以WM_開頭的消息到底又是什么東西呢?)
    看VC6的頭文件winuser.h中的片段:
……
#define WM_CLOSE                        0x0010
……
#define WM_LBUTTONDOWN                  0x0201
#define WM_LBUTTONUP                    0x0202
#define WM_LBUTTONDBLCLK                0x0203
#define WM_RBUTTONDOWN                  0x0204
……
#define WM_KEYDOWN                      0x0100
#define WM_KEYUP                        0x0101
#define WM_CHAR                         0x0102
……
#define WM_INITDIALOG                   0x0110
#define WM_COMMAND                      0x0111
#define WM_SYSCOMMAND                   0x0112
……
    哦!這些WM_開頭的所謂的消息只不過是一系列16進(jìn)制整型數(shù)值的符號(hào)常量而已。每一個(gè)不同的整型數(shù)值代表著一個(gè)窗口某一操作的標(biāo)識(shí),因此我們將這些數(shù)值或者說以WM_開頭的符號(hào)常量稱之為消息了。
    也就說,我們?cè)诖翱谥凶鞲鞣N不同的操作,Windows系統(tǒng)會(huì)產(chǎn)生各種相應(yīng)的數(shù)值。我們就是通過條件語(yǔ)句比較這些數(shù)值來判斷我們?cè)诖翱谥兴龅牟僮鞯摹?br>
3.5 資源標(biāo)識(shí)
    (你看我沒完沒了地介紹一個(gè)個(gè)概念,覺得煩不煩?不用你說,我自已也有點(diǎn)煩了。唉!不過這些似乎是必要的,所以我不得不堅(jiān)持下去。不過,還好,剩下的不多了。)
    那么VC6中資源是什么一種概念呢?
    我們的程序中可能要用到各種圖標(biāo)(*.ico文件)、各種形狀的鼠標(biāo)(*.cur文件)、各種圖像(*.bmp/*.gif等等)、各種聲音(*.wav等)、各種菜單……,這些就是我們這里所說的資源了。
    每一個(gè)要用到資源,我們都要給它分配一個(gè)編號(hào)或名稱,作為這個(gè)資源的標(biāo)識(shí)。之后我們的程序只是通過這個(gè)編號(hào)或名稱來訪問這些資源了。所以這些編號(hào)或名稱我們稱之為資源標(biāo)識(shí)。好了,現(xiàn)在你也只要有了一個(gè)大體的映象就可以了,具體的形式和應(yīng)用讓我慢慢再與你說了。
    (各位可以提出你的疑問,白云小飛一定會(huì)盡力回復(fù)的。)
   ??!終于結(jié)束冗長(zhǎng)的概念解說了,看到這里,請(qǐng)先回顧一下我們前面講的東西。然后嘛——我們可以開工啦!。

 

 

 

 

第二篇、創(chuàng)建Win32工程和主函數(shù)

 

                   白云小飛
1 在D:\創(chuàng)建一個(gè)空的工程(工程名為MyApp)
    要編寫一個(gè)程序,我們就要首先用VC6應(yīng)用程序向?qū)?chuàng)建一個(gè)工程,下面我將給你創(chuàng)建一個(gè)空工程(也就是沒有任何源文件及代碼的工程)
1.1 操作:
    =>文件->新建…->”工程”標(biāo)簽->位置:”D:\”(你可以設(shè)置你想要?jiǎng)?chuàng)建的位置)->工程名:MyApp(你可以自己指定其它名)->選擇”創(chuàng)建新的工作空間”->確定->一個(gè)空工程->完成
1.2 請(qǐng)查看指定位置下生成的文件:打開D:\MyApp
    我們發(fā)現(xiàn),它在D:\下生成了一個(gè)MyApp文件夾。
    并在MyApp文件夾下生成了幾個(gè)文件,如下:
MyApp.dsp
MyApp.dsw
MyApp.ncb
MyApp.opt
    其中,MyApp.dsp是項(xiàng)目文件和MyApp.dsw是工作區(qū)文件,它們是不能刪除。
    項(xiàng)目文件的作用:生成一個(gè)EXE或DLL程序的所有相關(guān)源文件、有頭文件的位置信息都記錄在MyApp.dsp項(xiàng)目文件中。
    工作區(qū)文件的作用:如果一個(gè)復(fù)雜的軟件工程可能是由多個(gè)EXE和多個(gè)DLL程序組成,這樣每個(gè)項(xiàng)目文件的位置信息又記錄在MyApp.dsw工作區(qū)文件中。
    當(dāng)然,我們的這個(gè)工程只有一個(gè)EXE程序,所以只有一個(gè)項(xiàng)目,這個(gè)項(xiàng)目文件MyApp.dsp也同樣要記錄在MyApp.dsw中了。
    MyApp.ncb和MyApp.opt雖刪除后會(huì)自動(dòng)生成,但是還是請(qǐng)不要這樣做哦!以后我還會(huì)說它們的作用。
    我們下次要編輯源程序時(shí),只要打開工作區(qū)文件MyApp.dsw就可。

2 在D:\MyApp下創(chuàng)建一個(gè)C++源文件,文件名為MyAppMain(當(dāng)然你也可以自己定義一個(gè)文件名),并同時(shí)加入到MyApp工程中
2.1 操作:
     =>文件->新建…->”文件”標(biāo)簽->選”添加到工程”->選”MyApp”->文件名:MyAppMain->”位置”默認(rèn)->確定
2.2 查看指定生成的文件:
    可以看到,在D:\MyApp文件夾下生成了MyAppMain.cpp源文件。

3 在MyAppMain.cpp文件中輸入一個(gè)主函數(shù)
3.1 代碼如下:
#include <windows.h>
#include<windowsx.h>

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
  // 將會(huì)在這里輸入主函數(shù)的代碼
  return 0;
}
3.2 包含必要的頭文件:
    首先你只要包含下面兩個(gè)頭文件就可,因?yàn)樗鼈円呀?jīng)包含了絕大多數(shù)的MyApp應(yīng)用程序必要的頭文件。
#include <windows.h>
#include<windowsx.h>
3.3 主函數(shù)名:
    函數(shù)頭定義的書寫格式很有趣:
int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
    寫成四行,其實(shí)沒什么,只不過寫在一行里太長(zhǎng)了,如下:
int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance,                 LPSTR lpcmdline,  int ncmdshow)
    并且寫成上面的四行反而可讀性更強(qiáng),所以以后你會(huì)經(jīng)常看到這樣書寫的。
    WinMain函數(shù)相當(dāng)于Dos下的main函數(shù),Windows應(yīng)用程序的主函數(shù)不用main而是用WinMain。一個(gè)程序必有并只能有一個(gè)WinMain函數(shù)。這個(gè)主函數(shù)可以寫在本工程中的任何一個(gè).cpp文件中。
3.4 參數(shù):
hinstance:類型是HINSTANCE,這種類型就是前面我們可是提到過的應(yīng)用程序句柄啦。hinstance的值就是你的當(dāng)前應(yīng)用程序進(jìn)程的句柄。
    你的程序每次運(yùn)行,它的hinstance值是不會(huì)一樣的。不過我們并不關(guān)心hinstance的值是多少。我們只要知道hinstance里的值就是代表本應(yīng)用程序進(jìn)程的句柄值就可以了。
    我們等一會(huì)兒就會(huì)用到它了,請(qǐng)看好哦!
    hprevinstance現(xiàn)在已經(jīng)不用了。我們可以完全不理它。
    lpcmdline這是一個(gè)命令行參數(shù)。與main(int argc,char **argv)中的命令行參數(shù)相似。由于與本題無關(guān),我們也不可完全不理它,對(duì)此不再進(jìn)一步討論下去。
    至于它的數(shù)據(jù)類型LPSTR,我在前面已經(jīng)說明了,它其實(shí)就是char *類型。
    ncmdshow一個(gè)整型值。啟動(dòng)過程中被傳遞給應(yīng)用程序,帶有如何打開應(yīng)用程序主窗口的信息。這樣,程序的使用者有了一點(diǎn)點(diǎn)的控制應(yīng)用程序如何啟動(dòng)的能力了。作為一個(gè)程序員,如果想忽略它也可以,想使用它也行。哦,那我們這里也先忽略它了。
3.5 返回值:
    是一個(gè)int值,當(dāng)我們的程序正常結(jié)束退出時(shí),一定要返回一個(gè)0值。所以我們的WinMain函數(shù)體內(nèi)最后有return 0;了
3.6 函數(shù)名前的WINAPI是什么?
    最后還有一個(gè)要說明的——就是WINAPI。這是什么呢?
    在VC6的Windef.h頭文件中是如下定義的:
#define WINAPI      __stdcall
    也就是WINAPI等于__stdcall了。
    你知道嗎,凡是提供給Windows操作系統(tǒng)調(diào)用的函數(shù)都得是__stdcall調(diào)用的。WinMain主函數(shù)當(dāng)然是由Windows系統(tǒng)調(diào)用的,因此定義WinMain前要用__stdcall(即WINAPI)修飾。(你會(huì)明白函數(shù)調(diào)用方式的具體含義嗎?不知道也沒關(guān)系,現(xiàn)在只要記得WINAPI要放在WinMain前就行了。)
    另外說明一點(diǎn),int 與WINAPI哪個(gè)在前哪個(gè)在后都是可以的。
    好了,我們現(xiàn)在對(duì)主函數(shù)定義處的各個(gè)部分有了必要的了解。你可以休息一會(huì)兒,然后回顧一下我們前面操作的過程:
    想想我們?cè)诒酒凶隽四男┦拢?br>    生成了哪些文件?
    這些文件是什么作用的?
    主函數(shù)定義的各個(gè)部分是什么作用?
    如果你已經(jīng)對(duì)上面所做的感到比較清晰,那太好了,Come on!我們繼續(xù)吧!

 

 

 

 

第三篇、增加一個(gè)回調(diào)函數(shù)

 

白云小飛

1 請(qǐng)?jiān)賱?chuàng)建一個(gè)函數(shù)。
LRESULT CALLBACK WinProc(HWND hwnd,
          UINT msg,
         WPARAM wparam,
         LPARAM lparam)
{
  //
這里可以輸入響應(yīng)消息的代碼
    return 0;
}
    此函數(shù)人稱回調(diào)函數(shù)也稱自定義窗口過程函數(shù)。但其實(shí)它與我們一般函數(shù)無異,你千萬不要把這個(gè)函數(shù)想得太復(fù)雜了??!
    記住,函數(shù)名WinProc及各參數(shù)變量名是可以由你來指定,但是參數(shù)類型、個(gè)數(shù)、順序、及返回值類型是不能改變的。
    那么,這個(gè)函數(shù)是干什么的???
    它是一個(gè)用來與某窗口關(guān)聯(lián)的函數(shù)。當(dāng)我們?cè)诖翱谏系拿恳粋€(gè)操作,都會(huì)調(diào)用這個(gè)函數(shù)。這在個(gè)函數(shù)里,我們可以寫上代碼來完成我們?cè)诖翱谏喜僮鞯南鄳?yīng)功能。
    先來看參數(shù)部分:
    UINT msg UINT類型不是我們?cè)谇懊嬉惨娺^嗎?就是unsigned int類型了。每次程序運(yùn)行時(shí)用戶在窗口上操作所產(chǎn)生的消息都會(huì)通過msg傳遞進(jìn)來。msg里保存的值就是我們對(duì)窗口操作時(shí)產(chǎn)生的消息值(即WM_CLOSE、WM_CHAR、WM_COMMAND、……的值)。
    HWIND hwnd 哦!這是我在第一篇中講過的一個(gè)窗口句柄類型的變量。我們可以通過傳入的hwnd的值知道m(xù)sg中的消息對(duì)應(yīng)的是哪一個(gè)窗口。
     WPARAM wparam , LPARAM lparam wparam和lparam這兩個(gè)參數(shù)變量的類型分別是WPARAM和LPARAM。呵呵,我不是說過,你還會(huì)見到許多“新”的數(shù)據(jù)類型的嗎?你可要有心理準(zhǔn)備噢。
    在windef.h中有定義:
typedef UINT WPARAM;   //看,WPARAM其實(shí)就是unsigned int。
typedef LONG LPARAM;   //看,LPARAM其實(shí)就是long類型
typedef LONG LRESULT;   //LRESULT與LPARAM一樣也是長(zhǎng)整型long
    返回值:類型為L(zhǎng)RESULT,看windef.h中的定義可以知道是一個(gè)長(zhǎng)整型long現(xiàn)在你大體知道這些就行了。至于更進(jìn)一步的認(rèn)識(shí),讓我們先放到一邊去吧!
    CALLBACK這又是什么呢?
    在windef.h中的定義如下:
#define CALLBACK    __stdcall
#define WINAPI      __stdcall
    哦!它與WINAPI是一樣的,也是__stdcall了,這個(gè)WinProc函數(shù)是提供給操作系統(tǒng)調(diào)用的。我說過操作系統(tǒng)調(diào)用的函數(shù)都得在前加__stdcall。
    (那為什么不直接用__stdcall呢?)
    呵呵,這至少可以增加代碼的可讀性及易改性。

    我猜也許你還會(huì)繼續(xù)下一個(gè)疑問:那我又如何具體地使用這個(gè)函數(shù)呢?慢慢來,現(xiàn)在你只要輸入到你的.cpp文件中就可以了。
    到此為止,我們的MyAppMain.cpp文件代碼如下:
//Begin MyAppMain.cpp
#include <windows.h>
#include<windowsx.h>

LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
  //這里可以輸入響應(yīng)消息的代碼
    return 0;
}

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
  // 將會(huì)在這里輸入主函數(shù)的代碼
  return 0;
}
//End MyAppMain.cpp
    目前為止,這個(gè)程序還是一個(gè)什么事都沒做的Win32應(yīng)用程序。當(dāng)然它是不會(huì)顯示任何窗口的,不過我會(huì)一點(diǎn)一點(diǎn)地完善它的。
    最后請(qǐng)?jiān)僬J(rèn)真瀏覽一下上述代碼吧!重點(diǎn)回顧各參數(shù)的類型和作用。

 

 

 

第四篇、注冊(cè)一個(gè)窗口類

 
白云小飛

一 創(chuàng)建并顯示一個(gè)窗口的“遐想”。
首先,要顯示的窗口在哪里呢?
    
要想顯示你自己的窗口,顯然你得事先創(chuàng)建一個(gè)自己的窗口。當(dāng)你想要一個(gè)窗口時(shí),Window系統(tǒng)才會(huì)為你創(chuàng)建窗口。不要時(shí),Window再銷毀這個(gè)窗口。噢,這是多么相當(dāng)然的一種機(jī)制啊,你說是吧!也就是說我們得先創(chuàng)建一個(gè)窗口才能顯示。(否則哪里來的窗口給你顯示呢?)
其次,你想創(chuàng)建什么樣的窗口呢?
    那么,創(chuàng)建什么樣的窗口呢?創(chuàng)建前,Window系統(tǒng)可不知道你要的是什么類型的窗口?。ū热鐦?biāo)題欄上顯示什么圖標(biāo),鼠標(biāo)形狀是什么,窗口背景顏色等等)。這些類型信息應(yīng)在你創(chuàng)建前事先告訴Window系統(tǒng)。可以采用這種方法:就是我們事先寫一份要?jiǎng)?chuàng)建窗口的類型申請(qǐng)表,提交(注冊(cè))給Window系統(tǒng)。然后在創(chuàng)建時(shí),可以讓W(xué)indows按這個(gè)申請(qǐng)表來創(chuàng)建你所要的窗口了。也就是說我們還應(yīng)該先提交一個(gè)申請(qǐng)表,申請(qǐng)成功后再根據(jù)這個(gè)表創(chuàng)建一個(gè)窗口。
    依據(jù)上述的理由,我假想了以下幾個(gè)步驟要做:
    第一. 你得先填寫一份你想創(chuàng)建的窗口類的“申請(qǐng)表”。
    第二. 然后將這“申請(qǐng)表”通過一個(gè)API函數(shù)提交給Windows系統(tǒng)(即注冊(cè)到Windows系統(tǒng)中)。
    第三. 如果提交(注冊(cè))成功,就說明Window系統(tǒng)通過了你的“申請(qǐng)表”,Windows系統(tǒng)中就有了一份你所申請(qǐng)的窗口類(注:這個(gè)注冊(cè)成功的已經(jīng)注冊(cè)在系統(tǒng)中的“申請(qǐng)表”我們稱之為窗口類)。這樣你就可以利用這個(gè)申請(qǐng)成功的窗口類,通過一個(gè)專門的API函數(shù)讓W(xué)indows系統(tǒng)創(chuàng)建一個(gè)或多個(gè)的同一窗口類的窗口。
    第四. 創(chuàng)建成功后,我們有了窗口。但是,雖然窗口已存在在內(nèi)存中,并不一定就馬上顯示在屏幕上(這根據(jù)你的意愿了),所以之后的某時(shí)你可以用一個(gè)API函數(shù)來讓W(xué)indows系統(tǒng)顯示剛才創(chuàng)建的窗口。
    Window系統(tǒng)就是這樣設(shè)計(jì)的噢?。ê呛牵@樣設(shè)計(jì)不算壞,我可以接受。)
    以上就是創(chuàng)建一個(gè)窗口的大致過程。請(qǐng)記住,在Window系統(tǒng)下你的程序要顯示一個(gè)你想自定的窗口總是得經(jīng)歷如此步驟的。還要記住一點(diǎn),我們的代碼只是通過調(diào)用Window系統(tǒng)所提供的API函數(shù)來完成對(duì)窗口間接的管理。實(shí)際上窗口的管理操作都由Window系統(tǒng)直接完成的。
    好,讓我們?cè)诒酒邢葋硗瓿傻谝?、二步驟吧!

二 第一步 填寫一份“申請(qǐng)表”

1 用什么來作為這種“申請(qǐng)表”呢?
    我想,C語(yǔ)言中的struct結(jié)構(gòu)體類型的變量來充當(dāng)這個(gè)“申請(qǐng)表”是再合適不過的了。呵呵,真是這樣,VC6下早已為我們準(zhǔn)備好了這樣的“申請(qǐng)表”了。那就是WNDCLASSEX(我們稱之為窗口類結(jié)構(gòu)體)。
    看看這個(gè)WNDCLASSEX結(jié)構(gòu)體的底細(xì)吧!
    在windef.h中已經(jīng)有定義:(下面所列的與真實(shí)文件中會(huì)有點(diǎn)不同,但目前你只要理解我這份就可以了。)
typedef struct tagWNDCLASSEX {
    UINT        cbSize;        //用來保存本結(jié)構(gòu)體的所占字節(jié)數(shù)
UINT        style;   //窗口類型風(fēng)格。
                    //比如,可設(shè)置“若移動(dòng)窗口寬度時(shí),則刷新整個(gè)窗口。
    WNDPROC     lpfnWndProc; //回調(diào)函數(shù)指針,用以指向前面那個(gè)回調(diào)函數(shù)。
    int         cbClsExtra;       //略,我們可不必使用它,只要賦值為0就可
    int         cbWndExtra;      //略,同上
    HINSTANCE   hInstance;      //窗口所屬的應(yīng)用程序?qū)嵗浔?br>    HICON       hIcon;          //大圖標(biāo),(這個(gè)圖標(biāo)會(huì)顯示在窗口的哪里呢?)
    HICON       hIconSm;       //小圖標(biāo),(這個(gè)又會(huì)顯示在窗口的哪里呢?)
    HCURSOR     hCursor;       //鼠標(biāo)句柄,用以指定鼠標(biāo)移入窗口時(shí)的樣式
HBRUSH      hbrBackground;  //用來刷背景顏色的畫刷句柄,
                             //窗口的顏色就會(huì)按這個(gè)顯示。
LPCSTR      lpszMenuName;   //用來指向菜單資源名稱字符串的指針,
                              //可讓你的窗口有一個(gè)菜單。
LPCSTR      lpszClassName;   //用來指向窗口類名字符串的指針
                              //作為這個(gè)窗口類的標(biāo)識(shí)
} WNDCLASSEX
    哦!是一個(gè)有12個(gè)成員變量的struct結(jié)構(gòu)體的類型。(看來我們又要一個(gè)一個(gè)耐心地搞定它。)

2 創(chuàng)建一個(gè)“申請(qǐng)表”
現(xiàn)在就先讓我用這個(gè)WNDCLASSEX結(jié)構(gòu)體申明一個(gè)變量吧!(看粗體字部分)
    HWND hWnd;
    MSG msg;
WNDCLASSEX winclass;   //變量名可由你自己定。
    順便解釋一下:
    這里我順便地同時(shí)申明了hWnd的窗口句柄變量,用以保存將要?jiǎng)?chuàng)建出來的窗口的句柄。
    還申明了一個(gè)msg:它的類型是MSG,也是一個(gè)已預(yù)定義好的結(jié)構(gòu)體,用來保存窗口操作的各種消息信息。
    好了好了,以后再說這兩個(gè)東東啦。

3 填寫“申請(qǐng)表”
    接下來就到了填寫這個(gè)“申請(qǐng)表”的時(shí)候啦!也就是要對(duì)winclass這個(gè)結(jié)構(gòu)體變量的各成員賦值。
winclass.cbSize = sizeof (WNDCLASSEX);
    這是將本結(jié)構(gòu)體的大?。ㄕ甲止?jié)數(shù))賦值給其成員變量cbSize,一定要這樣做哦!好處是:以后Windows系統(tǒng)只要訪問cbSize就可知道wndclass的大小了,就不必每次都要用sizeof(WNDCLASSEX)來獲取大小。(哦,不錯(cuò),這真是一個(gè)很值得學(xué)習(xí)的做法)
winclass.stype=  CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLICKS;
    我們這里將這四個(gè)值同時(shí)賦值給成員變量stype。
    新問題:CS_VREDRAW、CS_HREDRAW、CS_OWNDC、CS_DBLCLICKS是什么??!
    stype是一個(gè)UINT即unsigned int的類型(共32位二進(jìn)制位)。stype的可能值為如下的組合:
             表二.3  窗口類的類型標(biāo)志
標(biāo)識(shí)             描述
CS_HREDRAW    若移動(dòng)或改變了窗口寬度,則刷新整個(gè)窗口
CS_VREDRAW    若移動(dòng)或改變了窗口高度,則刷新整個(gè)窗口
CS_OWNDC    為該類中的每個(gè)窗口分配一個(gè)單值的設(shè)備描述表
CS_DBLCLKS    當(dāng)用戶雙擊鼠標(biāo)時(shí)向窗口程序發(fā)送一個(gè)雙擊的信息,同時(shí),光標(biāo)位于屬 于該類的窗口中
CS_PARENTDC    略
CS_NOCLOSE    禁止系統(tǒng)菜單上的關(guān)閉命令
CS_SAVEBITS    略

    關(guān)于更詳細(xì)的類型標(biāo)志描述,請(qǐng)自行參考相關(guān)書籍。
    你可能看完后仍還不能完全明白這些窗口類的類型標(biāo)志的意思。沒有關(guān)系,它不妨礙我們對(duì)整個(gè)程序框架的理解(我們可是要善于把握輕重緩急噢?。?,現(xiàn)在你只要按我上面賦值就可以了。
    還有一個(gè)問題:符號(hào)“|”是按位或的運(yùn)算符,即表示CS_VREDRAW、CS_HREDRAW、CS_OWNDC、CS_DBLCLICKS的值同時(shí)都賦值給stype
    (只能解釋到這里了,如果還不太明白“|”的運(yùn)算,請(qǐng)自己去看關(guān)于C語(yǔ)言中“按位或”的相關(guān)知識(shí)了。)
winclass.lpfnWndProc=WinProc;
    lpfnWndProc是一個(gè)函數(shù)指針(第一篇我已經(jīng)說過你要對(duì)函數(shù)指針有一定認(rèn)識(shí)。),它是WNDPROC函數(shù)類型,這個(gè)函數(shù)類型在winuser.h文件中已有定義。如下:
    typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
    發(fā)現(xiàn)沒有,WNDPROC類型的格式與我在第三篇中寫的那個(gè)自定義窗口過程函數(shù)的格式是一樣的。現(xiàn)在我們將我們的這個(gè)函數(shù)WinPro的地址賦值給lpfnWndProc函數(shù)指針變量。
    將前面的那個(gè)回調(diào)函數(shù)WinProc的首地址賦值給成員變量lpfnWndProc。這樣這個(gè)窗口類就與這個(gè)回調(diào)函數(shù)相關(guān)聯(lián)起來了。
    這里,你可能有個(gè)疑問:為窗口類指定一個(gè)窗口過程函數(shù)有什么用處呢?這是Window系統(tǒng)的消息機(jī)制的關(guān)鍵。但我現(xiàn)在不想說太多,因?yàn)檎娴默F(xiàn)在無法說清楚的。只有等到我將整個(gè)程序框架建立起來后,我們?cè)賮砝斫馑伞#ò?!我知道這種說了卻又沒能完全說清楚的情況不應(yīng)該有,但真的是不得不為之?。。?br>wndclass.hInstance = hinstance;
    第二篇中可是說過主函數(shù)參數(shù)中hinstance的值就是本應(yīng)用程序?qū)嵗浔?,我們現(xiàn)在將這個(gè)hinstance的句柄值賦值給wndclass.hInstance。這樣,由這個(gè)窗口類結(jié)構(gòu)創(chuàng)建的窗口就與本程序?qū)嵗嚓P(guān)聯(lián)了。
wndclass.cbClsExtra=0;
wndclass.cbWndExtra =0;
    我們現(xiàn)在只是對(duì)cbClsExtra和cbWndExtra兩個(gè)成員簡(jiǎn)單賦值為0。你現(xiàn)在也不用管它們是做什么的??梢员WC絕大多數(shù)的程序只要這樣就行。
wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
    hCursor是一個(gè)鼠標(biāo)光標(biāo)句柄變量,用來為這個(gè)窗口類指定一個(gè)鼠標(biāo)句柄值(也就是想讓這個(gè)窗口顯示一個(gè)什么樣的鼠標(biāo)形狀了)。LoadIcon(NULL, IDC_ARROW);這個(gè)函數(shù)是加載一個(gè)光標(biāo)給hCursor的。你現(xiàn)在只要造就輸入就可,其它的以后我再說了。
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
    hIcon和hIconSm都是圖標(biāo)句柄變量,前一個(gè)是用以指定大圖標(biāo),后一個(gè)是用以指定小圖標(biāo)。
    hIconSm中設(shè)置的圖標(biāo)會(huì)顯示在你的窗口的標(biāo)題欄左邊。
    hIcon中設(shè)置的圖標(biāo)是會(huì)顯示在哪兒啊?不好意思,我真的不知道。如果有人知道,請(qǐng)告之,本人非常感謝!
    關(guān)于LoadIcon函數(shù),它是加載一個(gè)圖標(biāo)給hIcon及hIconSm的?,F(xiàn)在你只要造就輸入就可以了。
wndclass.lpszMenuName =NULL;
    lpszMenuName是一個(gè)字符串指針,這是用來指向一個(gè)菜單資源名字符串。我們這個(gè)窗口暫時(shí)不要菜單,所以這里先指定NULL。
wndclass.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);
    hbrBackground是一個(gè)畫刷句柄變量(類型HBRUSH)(呵呵,又一個(gè)新句柄。)。窗口客戶區(qū)的背景顏色是用這個(gè)變量指定的畫刷來刷的??床幻靼?HBRUSH)GetStockObject(BLACK_BRUSH)這個(gè)函數(shù)沒有關(guān)系,這里它提供了一個(gè)白色畫刷給我們。你現(xiàn)在只要造就輸入就可以。
wndclass.lpszClassName ="WINCLASS1";
    lpszClassName是一個(gè)字符串指針,它是為這個(gè)窗口類(申請(qǐng)表)指定個(gè)字符串,這個(gè)字符串作為這個(gè)窗口類的名稱。這里我們指定”WINCLASS1”字符串作為這個(gè)窗口類的名稱。(要注意一點(diǎn)的是,一個(gè)程序中如果申請(qǐng)(注冊(cè))了多個(gè)窗口類,那么每個(gè)窗口類的這個(gè)字段值是不能相同的。)
    到此為止,我們終于(有點(diǎn)稀里糊涂地)結(jié)束了這個(gè)結(jié)構(gòu)體各成員變量的賦值了。以上成員變量賦值代碼匯總?cè)缦拢?br>
wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.cbClsExtra=0;
wndclass.cbWndExtra =0;
wndclass.lpfnWndProc = WinProc;
wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
wndclass.hInstance = hinstance;
wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wndclass.lpszMenuName = NULL;
wndclass.hbrBackground =(HBRUSH) GetStockObject(BLACK_BRUSH);
wndclass.lpszClassName ="WINCLASS1";
   
   好了,我們已經(jīng)填好了我們想要?jiǎng)?chuàng)建的窗口的“申請(qǐng)表”了,現(xiàn)在可以透一口氣了!
   然后……再繼續(xù),因?yàn)槲覀兊娜蝿?wù)還沒用完成呢!
三 第二步 提交“申請(qǐng)表”給Window系統(tǒng)
    到了提交“申請(qǐng)表”的時(shí)候了。我們現(xiàn)在要將wndclass中的各項(xiàng)數(shù)據(jù)信息提交(注冊(cè))到Windows系統(tǒng)中。有一個(gè)API函數(shù)是專門用來注冊(cè)窗口類信息的,原型如下:
ATOM WINAPI RegisterClassEx(CONST WNDCLASSEX *);
    參數(shù)是WNDCLASSEX *類型的指針,只要將要注冊(cè)的WNDCLASSEX結(jié)構(gòu)體變量地址代入就行。
    返回值是一個(gè)ATOM類型,其實(shí)它也是一個(gè)unsigned short類型的別名而已。如果注冊(cè)不成功則程序返回0值,否則表示注冊(cè)成功。(注:我們暫時(shí)不關(guān)心其它返回值的意義。)
    所以我們注冊(cè)窗口類的代碼可以如下:
    if (!RegisterClassEx(&winclass))
        return 0;     //不成功程序就直接結(jié)束了

四 最后總括代碼
    本篇中所增加的全部代碼現(xiàn)總括如下(粗體字部分):
WINAPI int WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
    HWND hWnd;   //窗口句柄變量
   MSG msg;      //消息結(jié)構(gòu)體變量
    WNDCLASSEX wndclass;   //窗口類結(jié)構(gòu)體變量
    //以下是對(duì)wndclass各成員賦值
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.cbClsExtra=0;
    wndclass.cbWndExtra =0;
    wndclass.lpfnWndProc = WinProc;
    wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
    wndclass.hInstance = hinstance;
    wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon =LoadIcon(NULL,IDI_APPLICATION);
         wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION) ;
    wndclass.lpszMenuName = NULL;
    wndclass.hbrBackground =(HBRUSH) GetStockObject(BLACK_BRUSH);
    wndclass.lpszClassName =WND_CLS_NAME;
//下面是利用wndcalss結(jié)構(gòu)體的信息注冊(cè)一個(gè)窗口類
    if (!RegisterClassEx(&wndclass))
        return 0;
        //……
return 0;
}

 

 

第五篇、利用已注冊(cè)的窗口類來創(chuàng)建一個(gè)窗口

  

白云小飛

一 用CreateWindowEx函數(shù)來創(chuàng)建窗口

1
參數(shù)及返回值說明:
上篇中我們完成了向Windows系統(tǒng)進(jìn)行窗口的“申請(qǐng)”工作(即注冊(cè)一個(gè)窗口類)。本篇就是要用這個(gè)窗口類來創(chuàng)建一個(gè)窗口。
    下面這個(gè)API函數(shù)就是專門用來創(chuàng)建一個(gè)窗口的:
HWND  WINAPI  CreateWindowEx(
                        DWORD dwExStyle,
                        LPCSTR lpClassName,
                        LPCSTR lpWindowName,
                        DWORD dwStyle,
                        int X,  int Y,
                        int nWidth,  int nHeight,
                        HWND hWndParent ,
                        HMENU hMenu,
                        HINSTANCE hInstance,
                        LPVOID lpParam);
    這是一個(gè)擁有一大串參數(shù)的函數(shù)。(唉!我又要一個(gè)個(gè)地介紹了。)
    DWORD dwExStyle這是用來指定擴(kuò)展樣式標(biāo)志,絕大多數(shù)情況,我們只要指定為NULL,所以我不想多說。
    LPCSTR lpClassName我們要用前篇中注冊(cè)的窗口類來完成創(chuàng)建窗口,所以lpCassName所指字符串值要與前篇中注冊(cè)時(shí)所用的窗口類名值相同(即wndclass.lpszClassName的值)。本例中就是"WINCLASS1"字符串值。
    LPCSTR lpWindowName此指針?biāo)傅淖址畷?huì)顯示在標(biāo)題欄上(即標(biāo)題欄文字)。
    DWORD dwStyle這是用來指定窗口外觀類型的。以下是它可能的值(部分):

                 表 dwStyle可以設(shè)置的值
WS_POPUP             彈出式窗口
WS_OVERLAPPED    帶有標(biāo)題欄和邊界的重疊式窗口。
WS_OVERLAPPEDWINDOW    具有WS_OVERLAPPED、WS_CAPTION、WS_SYSMENU、WS_THICK、FRAME、WS_MINIMIZEBOX、WS_MAXIMIZEOBX樣式的重疊式窗口
WS_VISIBLE    創(chuàng)建之后就立即顯示窗口
……    (還有很多其它值呢!請(qǐng)自行參看其它參考書)

    還記得上篇中注冊(cè)窗口類時(shí)窗口類結(jié)構(gòu)體成員中有一項(xiàng):
     wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
    你可不要搞糊涂啦!wndclass.style所指的是針對(duì)窗口內(nèi)在特性的類型,而CreateWindowEx參數(shù)中的dwStyle窗口類型是窗口外觀的類型。
    int X,  int Y,這是指定相對(duì)于桌面的窗口左上角位置(坐標(biāo))。
    int nWidth,  int nHeight,指定窗口的寬度與高度。
    (上面這四個(gè)參數(shù)應(yīng)該好理解吧!)
    HWND hWndParent 父窗口句柄,不明白它的作用吧?現(xiàn)在你只要賦值為NULL就可。
    HMENU hMenu你還記得嗎?我們?cè)谇捌拇翱陬惤Y(jié)構(gòu)體變量賦值中有這么一句: wndclass.lpszMenuName =NULL; 它是可以為窗口指定一個(gè)菜單的。
    CreateWindowEx函數(shù)的這個(gè)hMenu菜單句柄(這又是一個(gè)句柄噢!)也可以為窗口指定一個(gè)菜單,它將代替前面的設(shè)置。不過我們現(xiàn)在暫時(shí)不要菜單,所以也賦值為NULL。
    HINSTANCE hInstance要與wndclass.hInstance值相同,本應(yīng)用程序的句柄代入這里。
    LPVOID lpParam高級(jí)特征,現(xiàn)在我們只要設(shè)置為NULL就行。
     (噢,好不容易介紹完這些參數(shù)了。)
    返回值:如果窗口成功創(chuàng)建出來,則返回這個(gè)窗口的句柄,如果創(chuàng)建不成功,則返回0值。

2 具體實(shí)現(xiàn)代碼:
    下面我就給出具體的創(chuàng)建窗口代碼:
    hWnd=CreateWindowEx(NULL,”WINCLASS1”,
                "這是我的第一個(gè)窗口",
                WS_OVERLAPPEDWINDOW ,
                0, 0,
                400,400,
                NULL,
                NULL,
                hinstance,
                NULL );
    if (!hWnd)   //這里應(yīng)該判斷是否創(chuàng)建成功
        return 0;

3 調(diào)試及結(jié)果:
    程序?qū)懙竭@里似乎窗口就可以創(chuàng)建出來了。那就讓我們調(diào)試調(diào)試?OK,Let’s go!請(qǐng)?jiān)?br>    if (!hWnd)
    return 0;   //設(shè)斷點(diǎn)
    中的return 0處設(shè)置一個(gè)斷點(diǎn)??纯茨愕某绦驎?huì)不會(huì)執(zhí)行到這個(gè)return 0;當(dāng)創(chuàng)建不成功,則hWnd==0,程序會(huì)執(zhí)行到這個(gè)return 0。反之,程序成功地創(chuàng)建了這個(gè)窗口。
    (哦!我并不知道你是否會(huì)用VC6來設(shè)置斷點(diǎn)并進(jìn)行調(diào)試。
    只要光標(biāo)放在return 0 處按F9就可以設(shè)置此處的斷點(diǎn)。如果你再按F9一次,則會(huì)取消這個(gè)斷點(diǎn)。
    設(shè)置完斷點(diǎn)后,按F5編譯運(yùn)行這個(gè)程序試試。)
    矣?不太對(duì)勁啊,調(diào)試中我們發(fā)現(xiàn)hWnd值為0,即說明窗口并沒有創(chuàng)建成功
    (在這里請(qǐng)?jiān)侔碏5,可以從斷點(diǎn)處繼續(xù)運(yùn)行,程序結(jié)束了。)
    原因是?……

二 CreateWindowEx的一個(gè)條件:調(diào)用缺省窗口過程DefWindowProc函數(shù)

1 在WinProc中增加調(diào)用DefWindowProc函數(shù)

   
原因是:我們還要在這個(gè)窗口的自定義窗口過程WinProc中增加如下代碼(黑體字部分):
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
//……
   return DefWindowProc(hwnd, msg, wparam, lparam);
}
    真讓我疑惑??!
    CreateWindowEx函數(shù)創(chuàng)建一個(gè)窗口與這個(gè)DefWindowProc函數(shù)什么關(guān)系。
    又為什么要把這個(gè)函數(shù)寫在WinProc回調(diào)函數(shù)里呢?
    首先,看DefWindowProc在Winuser.h中的原型定義:
LRESULT CALLBACK DefWindowProc(
                        HWND hWnd,
                        UINT Msg,
                        WPARAM wParam,
                        LPARAM lParam);
    你有沒有看出,它的函數(shù)格式與我們自定義WinProc回調(diào)函數(shù)的格式一完全一樣的?只不過DefWindowProc是Window系統(tǒng)提供給我們的一個(gè)API函數(shù)。
    那么在整個(gè)調(diào)用CreateWindowEx函數(shù)中到底發(fā)生了什么事???讓我來告訴你吧!

2 原因何在?
    從我們的自定義窗口過程WinProc函數(shù)說起:
    當(dāng)應(yīng)用程序運(yùn)行時(shí),用戶對(duì)該程序窗口的操作,都會(huì)自動(dòng)產(chǎn)生一系列的消息。比如創(chuàng)建一個(gè)窗口會(huì)產(chǎn)生WM_CREATE消息;移動(dòng)窗口會(huì)產(chǎn)生WM_MOVE消息;按下鍵盤時(shí)會(huì)產(chǎn)生WM_KEYDOWN消息;關(guān)閉窗口時(shí)會(huì)產(chǎn)生WM_CLOSE消息等等。也就是說,在執(zhí)行CreateWindowEx函數(shù)期間會(huì)產(chǎn)生若干個(gè)消息(先別管都是些什么消息)。
    Window系統(tǒng)會(huì)將這些由一系列的消息添加到該進(jìn)程的消息隊(duì)例中(噢,你可要有一點(diǎn)想象力喲?。?。在CreateWindowEx函數(shù)中同時(shí)會(huì)調(diào)用我們寫的WinProc函數(shù)若干次,并把消息的各項(xiàng)信息通過WinPro函數(shù)參數(shù)傳遞進(jìn)來。(我再說一遍:你得有點(diǎn)想象力?。?br>    說白了,就是在執(zhí)行CreateWindowEx創(chuàng)建窗口過程中會(huì)引發(fā)對(duì)WinProc函數(shù)的多次調(diào)用。
    創(chuàng)建窗口過程中要用到缺省窗口過程函數(shù)DefWindowProc:
    前面說過,在執(zhí)行CreateWindowEx創(chuàng)建窗口過程中會(huì)引發(fā)對(duì)WinProc函數(shù)的多次調(diào)用。嘿嘿,這可不是可有可無的調(diào)用??!在這里,我們要讓缺省窗口過程DefWindowProc來完成一些默認(rèn)的消息處理操作。你不必知道它做了什么事,只要把這一切消息都“扔”給它就行啦!只有讓 DefWindowProc函數(shù)完成必要的消息處理,CreateWindowEx函數(shù)才能全程地完成窗口的創(chuàng)建(否則,嘿嘿!窗口的創(chuàng)建必將失敗。)。所以我們添加了調(diào)用DefWindowProc的代碼。(DefWindowProc的返回值返回的是對(duì)消息處理的結(jié)果,我們?cè)賹⑺鳛閃inProc的返回值。)

3 本篇增加的全部代碼
    最后,本篇中所增加的全部代碼總括如下(黑體部分):
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
   return DefWindowProc(hwnd, msg, wparam, lparam);
}

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
       HWND hWnd;
    MSG msg;
    WNDCLASSEX wndclass;  
   //……  這里省略了前篇所述的注冊(cè)窗口類的過程
    hWnd=CreateWindowEx(NULL,WND_CLS_NAME,
                "這是我的第一個(gè)窗口",
                WS_OVERLAPPEDWINDOW|WS_VISIBLE ,
                CW_USEDEFAULT, 0,
                400,400,
               NULL,
                NULL,
                hinstance,
                NULL );
   if (!hWnd)
       return 0;
  //……
  return 0;
}

4 具體分析
    現(xiàn)在現(xiàn)具體地分析一下調(diào)用CreateWindowEx函數(shù)的過程:
    1. 首先從調(diào)用CreateWindowEx開始,程序進(jìn)入了CreateWindowEx函數(shù)體內(nèi)的代碼(就是Window系統(tǒng)的代碼)。
    2. 在此期間,CreateWindowEx會(huì)產(chǎn)生若干個(gè)消息(我們不用管是什么消息)。
    3. 每當(dāng)一個(gè)消息產(chǎn)生后,CreateWindowEx會(huì)自動(dòng)調(diào)用我們自己寫的WinProc函數(shù),將消息信息傳遞進(jìn)來。
    4. 這些消息我們自己不作處理而是直接在WinProc調(diào)用缺省窗口過程DefWindowProc來作缺省處理(你不用去管DefWindowProc做了什么),并返回處理結(jié)果。
    5. 每次WinProc結(jié)束后回到CreateWindowEx代碼處,CreateWindowEx會(huì)根據(jù)返回值果進(jìn)行判斷,完成最終的窗口創(chuàng)建。
    怪不得嘛!沒有調(diào)用DefWindowProc這個(gè)函數(shù),CreateWindowEx無法完成全部的窗口創(chuàng)建過程,所以最終創(chuàng)建窗口失敗了。

5 再調(diào)試
    很好!現(xiàn)在再按前面講的調(diào)試步驟試試看。
    if (!hWnd)
        return 0;   //這里設(shè)斷點(diǎn)
    程序不再執(zhí)行這個(gè)return 0;了。太好啦!這說明窗口創(chuàng)建成功了。當(dāng)然并沒把窗口顯示出來,所以你只看到程序停都不停一下就馬上就結(jié)束了。
    關(guān)于窗口的顯示我就留到下一篇了。



第六篇、顯示你創(chuàng)建的窗口

白云小飛


    
哈!到了顯示窗口的時(shí)候啦!
    看,下面這個(gè)函數(shù)就是用來顯示窗口的:
BOOL ShowWindow( HWND hWnd, int nCmdShow);

一 ShowWindow函數(shù)的參數(shù)及返回值
    hWnd就是你要顯示的窗口的句柄:
    nCmdShow是窗口的顯示方式,其可能的值如下:

SW_HIDE             隱藏應(yīng)用程序窗口
SW_SHOWNORMAL    激活并顯示窗口,如果窗口被最大化或最不化,系統(tǒng)恢復(fù)窗口到原始大小和位置(與SW_RESTORE)
SW_RESTORE    同SW_SHOWNORMAL
SW_NORMAL    
SW_SHOWMINIMIZED    激活并最小化窗口
SW_SHOWMAXIMIZED    激活并最大化窗口
SW_SHOW    激活窗口,并按其當(dāng)前大小和位置顯示
SW_MAXINIZE    最大化應(yīng)用程序窗口
SW_MINIMIZE    最不化應(yīng)用程序窗口
SW_SHOWNOACTIVATE    按最近大小和位置顯示窗口,但不改變激活特性
SW_SHOWMINNOACTIVE    最小化窗口,但不改變其激活特性
SW_SHOWNA       按當(dāng)前大小各位置顯示窗口,但不改變其激活特性
返回值:成功則返回TRUE,不成功則返回FALSE。

二 先來兩個(gè)例子
    例一:現(xiàn)假設(shè)已經(jīng)創(chuàng)建了一個(gè)窗口,并且該窗口句柄已保存在hWnd變量中。我希望將窗口最大化并使該窗口為當(dāng)前窗口(即激活該窗口)。請(qǐng)寫出ShowWindow函數(shù)的具體實(shí)現(xiàn)代碼。(注:可不必處理它的返回值)
    解:ShowWindow(hWnd, SW_SHOWMAXIMIZED);
    例二:我希望隱藏一個(gè)原來是顯示著的窗口。該窗口的句柄在hWnd變量中。應(yīng)如何寫ShowWindow函數(shù)呢?
    解:ShowWindow(hWnd, SW_HIDE);

三 本系列程序中的實(shí)現(xiàn)
    (快一點(diǎn)嘍,我想馬上動(dòng)手啦?。?br>    好,我們現(xiàn)在繼續(xù)完善我們的這個(gè)Win32SDK程序吧!
    具體代碼如下(注意粗體字部分):
int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
       HWND hWnd;
    MSG msg;
    WNDCLASSEX wndclass;  
   //……  這里省略了前面所述的注冊(cè)窗口類的過程
   //
    hWnd=CreateWindowEx(NULL,WND_CLS_NAME,
                "這是我的第一個(gè)窗口",
                WS_OVERLAPPEDWINDOW|WS_VISIBLE ,
                CW_USEDEFAULT, 0,
                400,400,
                NULL,
                NULL,
                hinstance,
                NULL );
    if (!hWnd)
        return 0;
    ShowWindow(hWnd, ncmdshow);   //本篇只添加這一句
  return 0;    //這里設(shè)置一個(gè)斷點(diǎn),調(diào)試看看
}
    一點(diǎn)說明:這里的ncmdshow就是WinMain主函數(shù)中的傳入?yún)?shù)ncmdshow。當(dāng)我們通過雙擊*.exe文件來執(zhí)行程序時(shí),ncmdshow里的值就會(huì)是SW_SHOWNORMAL。

四 調(diào)試看看
    太棒啦!我終于可以親眼所見我的窗口了!
    好,讓我們來調(diào)試一下吧!看看程序運(yùn)行后會(huì)發(fā)生什么。(如果不這樣調(diào)試而只是運(yùn)行它,那么還來不及等你看清窗口,程序就會(huì)馬上結(jié)束的。這不用我說明原因吧?。?br>    請(qǐng)?jiān)谧詈笠粋€(gè)return 0處設(shè)置一個(gè)斷點(diǎn)(光標(biāo)放在return 0處,按F9)。然后F5運(yùn)行程序。
    程序暫停在最后一個(gè)return 0處。
    然后最小化桌面上所有其它無關(guān)窗口(包括VC6窗口)(這樣才能看到這個(gè)程序的窗口噢?。?。
    認(rèn)真研究,我發(fā)現(xiàn)目前的代碼有以下幾個(gè)問題:
    1. 窗口雖然顯示,但窗口不能自動(dòng)被激活(即成為當(dāng)前窗口)。只有最小化桌面上其它應(yīng)用程序的窗口后,才能看到我們的這個(gè)窗口。(注意:在ShowWindow(hWnd, ncmdshow);函數(shù)中ncmdshow值我說過是SW_SHOWNORMAL值,應(yīng)該會(huì)將窗口激活才對(duì)??!這可是個(gè)大問題。)
    2. 窗口無法進(jìn)行調(diào)整大小,移動(dòng)位置等的操作。

    不過我并不想在這里解決這個(gè)問題(為什么?),那是因?yàn)檫@是一個(gè)大問題,它將引出Window程序的一個(gè)重要機(jī)制——消息處理機(jī)制。嘿嘿,到了關(guān)鍵一擊的時(shí)候了!我請(qǐng)你務(wù)必帶著這兩個(gè)問題看下篇吧!

 

 

 

第七篇、獲取消息及對(duì)消息缺省處理

 

白云小飛

一 重提上篇的問題:
    
還記得在上一篇的最后,經(jīng)過調(diào)試后我們發(fā)現(xiàn)的問題嗎?
    1. 窗口不能自動(dòng)被激活(即成為當(dāng)前窗口)。只有最小化其它應(yīng)用程序的窗口后,才能看到我們的這個(gè)窗口。(注意:在ShowWindow(hWnd, ncmdshow);函數(shù)中ncmdshow值我說過是SW_SHOWNORMAL值,應(yīng)該會(huì)將窗口激活才對(duì)?。。?br>    2. 無法進(jìn)行調(diào)整窗口大小,移動(dòng)窗口位置等等的所有對(duì)窗口的操作。
    你認(rèn)為是什么原因呢?

二 且聽我說:
    1. ShowWindow(hWnd, SW_SHOWNORMAL)函數(shù)作用雖然是顯示窗口并激活窗口。它確實(shí)完成了窗口的顯示,但是,它并沒有去激活窗口,只是發(fā)了一系列與激活窗口有關(guān)的消息,這才是ShowWindow(…)函數(shù)所做的事。(那又由誰(shuí)來激活呢?)
    2. 真正激活窗口的任務(wù)是由DefWindowProc( )完成的。哦,也就是說我們得讓DefWindowProc( )收到ShowWindow( )產(chǎn)生的消息并根據(jù)消息來執(zhí)行相應(yīng)任務(wù)。
    3. 但ShowWindow( )產(chǎn)生的消息只是加入到該線程的消息隊(duì)列中,并沒有自動(dòng)地去調(diào)用WinProc( )的回調(diào)函數(shù)對(duì)消息處理。因此,DefWindowProc( )也就沒被調(diào)用,自然地就沒法激活窗口了。(請(qǐng)邊看代碼邊分析我的這段話吧!呵呵,你得有點(diǎn)想像力了!)
    4. 哦,這不由地讓我想起前面的創(chuàng)建窗口函數(shù)CreateWindowEx( ) 。這兩個(gè)函數(shù)在執(zhí)行中都會(huì)產(chǎn)生若干的消息。但CreateWindowEx會(huì)自動(dòng)地調(diào)用了WinProc( ) 函數(shù),而ShowWindow( )沒有這樣。
    5. 同理,我們?cè)诖翱谥械母鞣N操作(調(diào)整大小、移動(dòng)窗口等等),雖然會(huì)產(chǎn)生各種消息,但操作系統(tǒng)也只是把它們排入我們線程的消息隊(duì)列中,并不自動(dòng)去調(diào)用   Proc,所以同樣的理由,DefWindowProc也沒有去處理我們的消息了,窗口也就沒法完成如調(diào)整大小,移動(dòng)等等的操作了(你要知道:這些操作的實(shí)現(xiàn)都是由DefWindowProc( )完成的)。(還有,由于消息因?yàn)闆]有得到處理,就會(huì)一直留在消息隊(duì)列中,這樣,你的線程消息隊(duì)列中的消息將會(huì)越積越多噢?。?br>    看來,上述的兩個(gè)問題都源于同一個(gè)原因:那就是對(duì)窗口操作產(chǎn)生的消息只是排入消息隊(duì)列中,操作系統(tǒng)并沒有自動(dòng)地調(diào)用WinProc回調(diào)函數(shù)來處理我們的窗口消息。
    哦!那我們只有自力更生了。
    我猜你一定迫切想知道如何解決吧!

三 介紹三個(gè)函數(shù)給你認(rèn)識(shí):
    我們到了一個(gè)Window窗口程序框架最關(guān)鍵的部分了——消息處理機(jī)制。
    消息隊(duì)列中的本窗口大量消息并不會(huì)被自動(dòng)取出,也沒有自動(dòng)地調(diào)用WinProc函數(shù)對(duì)消息加以處理,但是,Window系統(tǒng)提供了三個(gè)API函數(shù)給我們,讓我們自己去完成這件事??窗?!
GetMessage( …);
TranslateMessage(…);
DispatchMessage(…);
    下面就讓我分別對(duì)這三個(gè)函數(shù)解釋解釋。

1 GetMessage( …)
原型如下:
BOOL GetMessage(
    LPMSG lpMsg,
    HWND hWnd ,
    UINT wMsgFilterMin,
    UINT wMsgFilterMax);
功能:這個(gè)API函數(shù)用來從消息隊(duì)列中“摘取”一個(gè)消息信息放到lpMsg所指的變量里。(注:如果所取窗口的消息隊(duì)列中沒有消息,則程序會(huì)暫停在GetMessage(…) 函數(shù)里,不會(huì)返回。)

參數(shù)及返回值:
    LPMSG lpMsg是傳出參數(shù)。消息結(jié)構(gòu)MSG的指針。如果該函數(shù)執(zhí)行成功,則從消息隊(duì)列中“摘”取的一個(gè)消息信息會(huì)放入lpMsg所指的MSG結(jié)構(gòu)變量中。
    在Winuser.h中有定義如下:
typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
    其中的成員變量message里才是我所說的WM_SIZE、WM_COMMAND、WM_QUIT等等消息標(biāo)識(shí)。
    hwnd中是這個(gè)消息所在的窗口句柄。
    好了,其它成員變量我就暫時(shí)省略不說。
    HWND hWnd傳入?yún)?shù)。你要獲取你程序中哪個(gè)窗口的消息,那就把相應(yīng)的窗口句柄代入其中。
    UINT wMsgFilterMin,UINT wMsgFilterMax這兩個(gè)參數(shù)我就不介紹。你只要用0值代入就可了。
    返回值:如果取的是WM_QUIT消息,則返回值為0,如果取的是其它消息,返回值為非0值。WM_QUIT消息是退出程序的消息。當(dāng)我們想讓程序退出時(shí),我們就可以發(fā)一個(gè)WM_QUIT消息,讓GetMessage返回0值。

2 TranslateMessage(…)
    原型:BOOL TranslateMessage( CONST MSG *lpMsg);
    功能:對(duì)GetMessage取得的MSG消息結(jié)構(gòu)中的信息進(jìn)行必要的預(yù)處理(你可不用管它做了什么。)。
    參數(shù):
    CONST MSG *lpMsg它是用來對(duì)你取的消息結(jié)構(gòu)MSG變量進(jìn)行必要的預(yù)處理。GetMessage函數(shù)取得的消息,要經(jīng)過TranslateMessage處理一下,然后才可以傳給DispatchMessage函數(shù),因此,TranslateMessage必放在GetMessage與DispatchMessage之間。
    返回值:它雖然有一個(gè)返回值,我們總是忽略它,所以我也就不說了。

3 DispatchMessage(…)
    原型: LONG DispatchMessage( CONST MSG *lpMsg);
    功能:用來完成調(diào)用WinPro回調(diào)函數(shù)并把由GetMessage取得的消息結(jié)構(gòu)MSG變量中的信息傳遞給WinPro回調(diào)函數(shù):(原型如下)
    參數(shù)及返回值:
    MSG *lpMsg 傳入?yún)?shù),MSG消息結(jié)構(gòu)體類型指針,指向你已經(jīng)取出的消息結(jié)構(gòu)體變量。
    返回值:同上,我們總是忽略它。

四 Come on,行動(dòng)起來吧!
    看我添加的代碼(粗體部分):
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
//這里可以添加你的消息處理代碼
   return DefWindowProc(hwnd, msg, wparam, lparam);
}

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
       HWND hWnd;
    MSG msg;
    WNDCLASSEX wndclass;  
   //……  這里省略了前面所述的注冊(cè)窗口類的過程
   //
    hWnd=CreateWindowEx(NULL,WND_CLS_NAME,
                "這是我的第一個(gè)窗口",
                WS_OVERLAPPEDWINDOW|WS_VISIBLE ,
                CW_USEDEFAULT, 0,
                400,400,
                NULL,
                NULL,
                hinstance,
                NULL );
    if (!hWnd)
        return 0;
    ShowWindow(hWnd, ncmdshow);
       while(GetMessage(&msg, NULL, 0, 0)) //獲取一個(gè)消息,成功后會(huì)放在msg中。
    {       
      TranslateMessage(&msg); //消息進(jìn)行必要的處理轉(zhuǎn)換。      
      DispatchMessage(&msg); //調(diào)用WinProc,將msg的各項(xiàng)信息傳遞給WinProc
    }
  return 0;    
}
    看清楚了:這是一個(gè)消息循環(huán)。只有當(dāng)GetMessage(…)收到一個(gè)WM_QUIT時(shí)返回0值,程序才會(huì)退出。

五 程序流程分析(粗體部分)
    1. 當(dāng)用戶進(jìn)行如調(diào)整窗口大小,移動(dòng)窗口位置等等的操作時(shí)。會(huì)產(chǎn)生不同的消息。(當(dāng)然包括了ShowWindow( )函數(shù)產(chǎn)生的若干個(gè)消息。
    2. Window系統(tǒng)會(huì)將這些消息排入本線程的消息隊(duì)列中。(在操作系統(tǒng)中完成)
    3. 只要該窗口消息隊(duì)列中有消息,我們的GetMessage(&msg,NULL,0,0)就會(huì)從消息隊(duì)列中摘取一個(gè)消息的信息,填入msg結(jié)構(gòu)體變量中,如果不是WM_QUIT則返回非零值,就執(zhí)行循環(huán)體。(注意:如果消息隊(duì)列中沒有任何消息可取時(shí),則程序會(huì)停在GetMessage函數(shù)里,直到消息隊(duì)列中有了消息,GetMessage函數(shù)才會(huì)取一個(gè)消息信息并返回。)
    4. 用TranslateMessage(&msg)對(duì)msg中的數(shù)據(jù)進(jìn)行預(yù)處理(你先不必知道它具體做了什么,但不要忘記這個(gè)函數(shù)。)。
    5. 然后是調(diào)用DespatchMessage(&msg),這個(gè)函數(shù)里會(huì)調(diào)用WinProc,并將msg中的數(shù)據(jù)通過WinProc的參數(shù)傳遞給WinProc;
    6. 程序轉(zhuǎn)入執(zhí)行WinProc回調(diào)函數(shù)體內(nèi)的代碼。
    7. 看代碼處,WinProc此時(shí)只有一句 return DefWindowProc(hwnd, msg, wparam, lparam);這里,我們只是將WinProc傳入的參數(shù)原樣地傳給了API函數(shù)DefWindowProc。所有的消息都讓DefWindowProc進(jìn)行缺省默認(rèn)的處理。(你不用理會(huì)DefWindowProc都做了些什么。)
    8. DefWindowProc完成一個(gè)消息處理后,返回消息處理的結(jié)果。
    9. 我們的WinProc也原樣地將DefWindowProc返回值返回。
    10. WinProc執(zhí)行完成后,程序又返回到DispatchMessage(&msg)函數(shù)體內(nèi)。(因?yàn)槭窃贒ispathMessage( )中調(diào)用WinProc的。)
    11. 退出DispatchMessage(&msg);函數(shù)后程序又執(zhí)行下一個(gè)循環(huán)。即
while(GetMessage(&msg, NULL, 0, 0))
    又取下一個(gè)消息,進(jìn)行下一個(gè)消息的處理。
    12. 直到GetMessage “摘取”到了退出程序的消息WM_QUIT,返回零值,退出循環(huán),結(jié)束程序。
    (哦,整個(gè)流程是通過我們的程序與Window系統(tǒng)相互協(xié)作來完成的。你可要多加理解羅?。?br>
六 調(diào)試這個(gè)程序
    不設(shè)任何斷點(diǎn),按F5運(yùn)行程序,
    看來一切正常??梢砸苿?dòng)窗口;可以調(diào)整窗口大??;可以最大化最小化;總之可以進(jìn)行窗口的基本的操作了。(這些動(dòng)作都是由DefWindowProc來完成的噢?。?br>    哈!我們可是漸入佳境啦!這就是Window系統(tǒng)消息處理機(jī)制帶給我們的成果。
    不過……只是……有一個(gè)問題。
    你有沒有注意到,點(diǎn)擊窗口右上角的關(guān)閉按鈕時(shí),窗口是關(guān)閉了,但程序并沒有退出(看來點(diǎn)擊關(guān)閉按鈕時(shí)并沒有產(chǎn)生WM_QUIT的消息。)。
    你只能點(diǎn)擊VC菜單的:Debug->Stop Debugging來退出程序了。
   (欲知此為如何,請(qǐng)聽下回分解!)

 

 

 

第八篇、關(guān)閉窗口的同時(shí)退出程序

白云小飛

    還記得上篇中最后說到一個(gè)問題嗎?當(dāng)我們關(guān)閉程序窗口時(shí),窗口確實(shí)是關(guān)閉了,可是程序并沒有退出啊。為什么呢???

一. 理解程序的退出條件:
    首先,我們要先明白程序退出的條件,看上篇中的這段代碼:
    while(GetMessage(&msg, NULL, 0, 0)) //獲取一個(gè)消息,成功后會(huì)放在msg中。
    {  
      TranslateMessage(&msg); //消息進(jìn)行必要的預(yù)處理轉(zhuǎn)換。      
      DispatchMessage(&msg); //調(diào)用WinProc回調(diào)函數(shù),將msg傳遞給WinProc函數(shù)
    }
    如果程序一直在這個(gè)消息循環(huán)中,程序就沒能退出。只有當(dāng)GetMessage收到一個(gè)WM_QUIT的消息,則返回值才會(huì)為零,退出循環(huán),程序得以結(jié)束。(這個(gè)道理應(yīng)該好理解吧?)

二. 點(diǎn)關(guān)閉按鈕時(shí),發(fā)生了什么
    當(dāng)我們點(diǎn)窗口右上角的關(guān)閉按鈕時(shí),到底發(fā)生了什么事呢?(請(qǐng)邊看源代碼,邊體會(huì)下面的分析噢?。?br>    第一. 它并沒有(或最終沒有導(dǎo)致)發(fā)出WM_QUIT的消息。因此GetMessage函數(shù)不會(huì)收到WM_QUIT消息,就沒法跳出循環(huán)了。(那么又產(chǎn)生了什么消息呢?)
    第二. 點(diǎn)關(guān)閉按鈕時(shí),產(chǎn)生WM_CLOSE的消息。GetMessage會(huì)收到WM_CLOSE消息的MSG結(jié)構(gòu)信息。
    第三. 按前篇所述的消息處理流程可知:DespatchMessage會(huì)調(diào)用WinProc回調(diào)函數(shù),并把WM_CLOSE消息的相關(guān)信息傳遞給WinProc函數(shù)參數(shù)中。
    第四. 現(xiàn)在我們的WinProc里只有一句:
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
//這里可以添加你的消息處理代碼
   return DefWindowProc(hwnd, msg, wparam, lparam);
}
    它將WM_CLOSE繼續(xù)傳遞給缺省窗口過程函數(shù)DefWindowProc。
    第五. 在DefWindowProc函數(shù)里,判斷是WM_CLOSE消息后,就會(huì)對(duì)參數(shù)hwnd所代表的窗口進(jìn)行銷毀。(看吧,銷毀窗口的事也是由DefWindowProc來完成了。)
    第六. 成功銷毀窗口后,DefWindowProc里接著還會(huì)發(fā)一個(gè)WM_DESTROY的消息到消息隊(duì)列中(表示說窗口已經(jīng)被銷毀了)。然后DefWindowProc函數(shù)才結(jié)束。
    第七. 回到我們的消息循環(huán)的GetMessage函數(shù)。這個(gè)函數(shù)又會(huì)獲得WM_DESTROY消息的信息,開始了下一個(gè)消息處理過程。
    第八. 這個(gè)WM_DESTROY可在WinProc函數(shù)中由我們處理。但在WinProc函數(shù)體的代碼中我們沒有自己去處理它。仍然是讓DefWindowProc去處理。
    第九. 然而,DefWindowProc只是簡(jiǎn)單地把它給“扔掉”了。
    第十. 整個(gè)點(diǎn)窗口右上角的關(guān)閉按鈕作所的所有動(dòng)作就這樣完成了。
    你看,上述中,上述程序始終沒有產(chǎn)生WM_QUIT的消息,所以窗口確實(shí)是銷毀了,但程序并沒有退出這個(gè)消息處理循環(huán)。
    哦,怪不得我們的程序沒法結(jié)束了。(那該怎么辦呢?)

三. 如何使程序結(jié)束。
    退出程序的三點(diǎn)說明:
1. 我們希望是通過單擊這個(gè)窗口右上角的關(guān)閉按鈕時(shí)來退出程序。
2. 應(yīng)該在窗口成功銷毀后,才讓程序退出。
3. 只要讓程序產(chǎn)生一個(gè)WM_QUIT消息,就可以退出循環(huán)而結(jié)束程序。
    終上所述,程序應(yīng)在收到WM_DESTROY消息后才能退出程序。因?yàn)椋祝蚠DESTROY消息表示窗口已經(jīng)銷毀。
    那么我們又如何才能產(chǎn)生一個(gè)WM_QUIT的消息呢?用下面這個(gè)--API函數(shù):
PostQuitMessage(0);
    參數(shù)代入0值就可。它將產(chǎn)生一個(gè)WM_QUIT消息。WM_QUIT消息最終會(huì)被GetMessage函數(shù)“摘取”到并返回0值。從而退出循環(huán),結(jié)束程序??次覍?shí)現(xiàn)代碼:
LRESULT CALLBACK WinProc(HWND hwnd,
                         UINT msg,
                         WPARAM wparam,
                         LPARAM lparam)
{
   switch (msg)   //msg中保存的就是正要處理的消息
   {
      case WM_DESTROY:      //這是我們自行處理的第一個(gè)消息
      {
          PostQuitMessage(0);   //發(fā)出一個(gè)WM_QUIT消息
          return 0;             //然后直接返回。
       }break;
  default:break;
  }
   return DefWindowProc(hwnd, msg, wparam, lparam);
}

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
       HWND hWnd;
    MSG msg;
    WNDCLASSEX wndclass;  
   //……  這里省略了前面所述的注冊(cè)窗口類的過程
   //
    hWnd=CreateWindowEx(NULL,WND_CLS_NAME,
                "這是我的第一個(gè)窗口",
                WS_OVERLAPPEDWINDOW|WS_VISIBLE ,
                CW_USEDEFAULT, 0,
                400,400,
                NULL,
                NULL,
                hinstance,
                NULL );
    if (!hWnd)
        return 0;
    ShowWindow(hWnd, ncmdshow);
    while(GetMessage(&msg, NULL, 0, 0))
    {       
      TranslateMessage(&msg);    
      DispatchMessage(&msg);
    }  
  return 0;    
}
    WinProc函數(shù)參數(shù)中的UINT msg就是程序傳遞進(jìn)來的消息標(biāo)識(shí)。我們只要判斷msg是否為WM_DESTROY消息,如果是就發(fā)一個(gè)WM_QUIT消息。
    補(bǔ)充說明一點(diǎn):
    WinProc函數(shù)參數(shù)中有一個(gè)msg變量,而WinMain函數(shù)中也定義了一個(gè)msg。不要把它們給混了??!它們可是不同的變量啊!WinMain中定義的msg類型是MSG,在Winuser.h中已定義如下:
typedef struct tagMSG {
    HWND        hwnd;
    UINT        message;
    WPARAM      wParam;
    LPARAM      lParam;
    DWORD       time;
    POINT       pt;
} MSG, *PMSG, NEAR *NPMSG, FAR *LPMSG;
    你看,它是一個(gè)結(jié)構(gòu)體。而WinProc參數(shù)中的msg是一個(gè)UINT類型,它其實(shí)只是WinProc函數(shù)里的msg結(jié)構(gòu)成員message的值。
    你看看DispatchMessage(&msg);是如何傳遞這個(gè)msg給WinProc的:
    它在調(diào)用WinProc時(shí),會(huì)——
    將msg.hwnd值傳遞給WinProc參數(shù)中的HWND hwnd;
    將msg.message值傳遞給WinProc參數(shù)中的UINT msg;
    將msg.wParam傳遞給WinProc參數(shù)中的WPARAM wParam;
    將msg.lParam值傳遞給WinProc參數(shù)中的LPARAM lParam。

    整個(gè)程序?qū)崿F(xiàn)退出的流程還是讓你自行去分析啦!應(yīng)該不會(huì)太難吧???
    至此,終于完成了這個(gè)SDK程序的基本框架了?。ü嬷档梦覀?nèi)ヅe杯慶祝啦!不過,我的任務(wù)還沒完??!還有許多要解決的問題等著我噢?。?/span>

 

 

 

第九篇、窗口標(biāo)題欄上顯示自定義圖標(biāo)(手動(dòng)編輯代碼)

  

白云小飛


    
本篇想通過手動(dòng)直接編輯代碼的方式(而不是可視化的方式)來操作使用自定義的圖標(biāo)。(以在窗口標(biāo)題欄上顯示自定義圖標(biāo)為例)
    通過本篇,你將知道如何使用圖標(biāo)資源及實(shí)質(zhì),并有助于你理解在可視化方式編輯使用圖標(biāo)資源過程中的代碼實(shí)質(zhì)。
    另外,可以觸類旁通,明白VC中的Window應(yīng)用程序?qū)Ω鞣N類似資源(如光標(biāo)資源、位圖資源、聲音資源等)操作的一般機(jī)制。

一 開始我們的思考:程序所要用到的各類圖像、聲音等資源應(yīng)放在什么地方為好?
    一個(gè)程序可能需要使用各種圖像、聲音。那么,你認(rèn)為應(yīng)把這些東西放在什么地方呢?
    一種很顯然的做法:就是讓這些圖形、圖像、聲音仍以文件形式(如.bmp .jpg .ico .cur .wav等等擴(kuò)展名的文件,)保存在磁盤中,不包含在應(yīng)用程序(.exe)中。當(dāng)我的.exe程序執(zhí)行時(shí)需要這些文件時(shí)再把它們從磁盤中加載到內(nèi)存里并對(duì)它們操作。
    如果你是想寫一個(gè)看圖軟件或播放音樂之類的軟件,這當(dāng)然是一種最佳的做法啦。但是,有時(shí)候你的程序可能要顯示一個(gè)代表你程序的圖像等等的情況。由于前述的方法中,圖形、圖像、聲音是以獨(dú)立的文件形式,所以軟件的使用者是很容易就改動(dòng)及刪除它們的,唉!我可不希望這樣啊!
    那么,還有什么辦法呢?
    還有一種可以想到的做法:就是讓這些圖形、圖像、聲音等數(shù)據(jù)在編譯時(shí)就把它們編譯進(jìn).exe文件中。
    顯然,由于程序要用到的各種圖像、聲音等的數(shù)據(jù)已經(jīng)包含在.exe里面了,這樣,軟件使用者只要擁有這個(gè).exe文件就可以。同時(shí)使用者要想改動(dòng)程序所用的這些圖形、圖像、聲音也就不是那么容易。
    啊!exe程序文件里居然也能存儲(chǔ)各種類型圖像、聲音!嗯,這可是Windows的一個(gè)設(shè)計(jì)目標(biāo):可執(zhí)行程序里不僅只含有程序代碼,還可以存儲(chǔ)各種圖像、聲音等的數(shù)據(jù)呢!也可能第一次讓你有了這個(gè)念頭!但不管怎么說,程序是可以這樣設(shè)計(jì)的!
    好了,那么又如何創(chuàng)建一個(gè)包含有圖標(biāo)資源并使用這個(gè)圖標(biāo)的.exe文件呢?

二 圖像、聲音資源操作的一般步驟。

1 顯然,你得先創(chuàng)建一個(gè)自己想要的圖標(biāo)(提示:這里我以圖標(biāo)為例)
    但具體應(yīng)如何做呢?
    這個(gè)問題好解決也好理解:我們只要用一個(gè)能編輯圖標(biāo)的圖形圖像軟件就可以啦。制做完圖標(biāo)后,把它們以.ico文件的形式(.ico 是圖標(biāo)文件的擴(kuò)展名)保存在磁盤的某個(gè)文件夾中。
    當(dāng)然啦,VC本身也提供了一個(gè)圖標(biāo)編輯器。

2 然后你要給這個(gè)圖標(biāo)定義一個(gè)字符串ID或者整數(shù)ID
    為什么要給它定義一個(gè)字符串ID或整數(shù)ID呢?
    那是因?yàn)檫@個(gè)圖標(biāo)是要被編譯鏈接進(jìn)可執(zhí)行程序中(即.exe文件,當(dāng)然也可能是.dll文件),這樣程序就不能通過文件名來訪問該圖標(biāo)了。
    所以,我們就得給這個(gè)圖標(biāo)定義一個(gè)字符串ID或整數(shù)ID了,用以代表這個(gè)圖標(biāo)。(你可以選擇使用字符串ID,也可以使用整數(shù)ID。)
    這個(gè)定義是寫在一個(gè)叫資源腳本文件(擴(kuò)展名為 .rc)里的。
    當(dāng)然,還要記住把.rc文件加入工程。
3 使用圖標(biāo)第一步:通過ID號(hào)加載圖標(biāo)
    請(qǐng)先想想前面兩部做了什么?前面兩步產(chǎn)生了一個(gè)圖標(biāo)并定義了一個(gè)圖標(biāo)ID。這樣我們的程序就可以使用這個(gè)圖標(biāo)了:程序首先通過以下這個(gè)API函數(shù):
    HICON  LoadIcon ( HINSTANCE hInstance,  LPCSTR lpIconName);
    來加載圖標(biāo)。
    暫略第一個(gè)參數(shù),先來說一下第二個(gè)參數(shù)LPCSTR lpIconName。該參數(shù)是代表要加載圖標(biāo)的字符串ID。
    加載成功后,返回系統(tǒng)分配給該圖標(biāo)的一個(gè)句柄值(類型是HICON)。
    (該函數(shù)具體的使用,后面還會(huì)有介紹。)

4 加載后,程序都是通過圖標(biāo)句柄來操作該圖標(biāo)
    有了圖標(biāo)句柄值,就可以通過這個(gè)值來操作相應(yīng)的圖標(biāo)了。(還記得“句柄”這個(gè)概念及作用嗎?Window系統(tǒng)總是通過句柄來操作已加載到內(nèi)存的某個(gè)對(duì)象。)

5 最后,我們的程序都編好后,只要把資源與程序代碼一起編譯到.exe文件中
    VC會(huì)自動(dòng)用一個(gè)專門的資源編譯器會(huì)把.rc文件及相關(guān)的資源文件(*.ico、*.bmp、*.wav等)編譯生成一個(gè)擴(kuò)展名為.res的二進(jìn)制中間文件。然后再用連接程序與程序代碼的二進(jìn)制中間文件一同連接成可執(zhí)行程序了。
    這看上去比較復(fù)雜。不用害怕啊!其實(shí)你只要按原來的方式編譯連接就行了。

    到此,最好重新瀏覽一下上述1~5的步驟,并多加體會(huì)這個(gè)操作流程。
    現(xiàn)在頭腦有了概念沒有啊?有,那就開始動(dòng)手吧?。ㄗ⒁猓罕酒形沂怯檬謩?dòng)編輯代碼的方式來完成的。)

三 具體實(shí)現(xiàn)范例:本例為圖標(biāo)定義一個(gè)字符串ID(注意:不是整數(shù)ID)
    任務(wù):在窗口標(biāo)題欄上顯示一個(gè)自定義的圖標(biāo)。
    (提醒:務(wù)必請(qǐng)先對(duì)前一篇所完成工程做一個(gè)備份。因?yàn)橐院笪疫€要從上一篇的工程狀態(tài)開始新的內(nèi)容呢?。?br>
1 首先我們要有一個(gè)自定義圖標(biāo)(文件名以myicon.ico為例)
    在我們的工程文件夾(即D:\MyApp)下創(chuàng)建一個(gè)myResLib文件夾(用以集中存放各種資源文件)。
    然后,你可用一個(gè).ico編輯器創(chuàng)建一個(gè)圖標(biāo)文件myicon.ico,把它放在D:\MyApp\myResLib文件夾下。
    不過,也可能你并不懂得使用任何一款.ico編輯器,那也沒關(guān)系,隨便找一個(gè).ico(16*16或32*32的)文件(這不應(yīng)成為問題吧?)。把它復(fù)制到D:\MyApp\myResLib文件夾下,并改名為myicon.ico。
    好了,現(xiàn)在我們有一個(gè)圖標(biāo)文件,請(qǐng)你記住它的路徑和名稱。

2 用記事本程序創(chuàng)建一個(gè)資源腳本文件(文件名為myRes.rc),并在這個(gè)文件中為myicon.ico定義一個(gè)字符串ID(本例為:IDI_MYICON)作為這個(gè)資源的名稱。
    之所以用記事本來創(chuàng)建而不用VC本身來創(chuàng)建,是因?yàn)槲也幌M孷C生成一些無關(guān)碼,以便于解說和理解。
    另外一點(diǎn)要提醒:如果你的工程里已經(jīng)包含有一個(gè).rc的資源腳本文件,那么在下面的操作會(huì)出現(xiàn)一些不同的情況。但如果從第一篇就按我所述的來操作,本工程是沒有.rc文件的。
    操作:
    =>開始->程序->附件->記事本
    =>在記事本中輸入如下一行:
    IDI_MYICON    ICON  DISCARDABLE  " myResLib\ myicon.ico"
    看,在ICON  DISCARDABLE的左邊寫上ID名,右邊寫上圖標(biāo)所在的相對(duì)路徑字符串。這樣也就將myResLib\myicon.ico圖標(biāo)定義ID號(hào)為IDI_MYICON,并且這樣定義的ID就是字符串ID。(等一下你就會(huì)知道如何使用字符串ID了?。?br>    =>點(diǎn)擊“記事本”程序菜單“文件”->另存為->在“保存在”框中選D:  ->雙擊打開MyApp文件夾->在“保存類型”框中選“所有文件(*.*)->在“文件名”框中輸入:myRes.rc->點(diǎn)“保存”(操作完成)
    現(xiàn)在我們已經(jīng)為myicon.ico定義了一個(gè)字符串ID:IDI_MYICON 。接下來,要干什么呢?哦,你要知道,這樣方式創(chuàng)建腳本文件是不會(huì)自動(dòng)加到我們的工程中,所以你要記得自己把myRes.rc加到你的工程中。

3 將myRec.rc加入到工程中。
    =>在工作區(qū)(Workspace)視圖中選FileView選項(xiàng)卡->在其中右擊Source Files -> 單擊“添加文件到目錄… ->雙擊“myRes.rc”
    本操作的目的:將資源腳本文件myRes.rc加入到該工程中。下面我們就可以通過代碼來訪問這個(gè)圖標(biāo)了。

4 要使用圖標(biāo)資源,得先用LoadIcon函數(shù)加載圖標(biāo)資源。
    LoadIcon原型:
    HICON  LoadIcon ( HINSTANCE hInstance,  LPCSTR lpIconName);
    參數(shù):
    HINSTANCE hInstance要加載圖標(biāo)是存在那個(gè)應(yīng)用程序里,就代入這個(gè)應(yīng)用程序?qū)嵗木浔?br>    LPCSTAR lpIconName是你要加載的圖標(biāo)的字符串ID,就是我們?cè)诘?步中定義的。
    返回值:加載成功后會(huì)返回一個(gè)圖標(biāo)句柄值,其類型是HICON。加載后,我們就可以通過這個(gè)句柄值來操作對(duì)應(yīng)圖標(biāo)了。
    下面就是原來在窗口類結(jié)構(gòu)體中設(shè)置窗口標(biāo)題欄圖標(biāo)的代碼(應(yīng)該還記得下面一行代碼在哪里吧?。?br>wndclass.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE( IDI_APPLICATION ) ) ;
    你先別理上面LoadIcon中的參數(shù)使用,現(xiàn)在我們把它改成如下:
    wndclass.hIconSm = LoadIcon(hinstance,” IDI_MYICON”) ;
    其中,hinstance就是本應(yīng)用程序?qū)嵗木浔!盜DI_MYICON”就是我們要加載的圖標(biāo)。
    現(xiàn)在,我們把WinMain主函數(shù)里的代碼修改如下:
int APIENTRY WinMain(HINSTANCE hinstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    //……
         wndclass.hInstance = hinstance;
    wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon =NULL;
//wndclass.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE( IDI_APPLICATION ) ) ;
//注釋了上句
    wndclass.hIconSm = LoadIcon(hinstance,"IDI_ICON1") ;    //添加了此句
    wndclass.lpszMenuName =NULL;
    wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);
    wndclass.lpszClassName =WND_CLS_NAME;

    if (!RegisterClassEx(&wndclass))
        return 0;
//……
}

5 最后Build我們的程序
    直接F5就行了。
    看到了沒,程序的窗口已經(jīng)變成我們想要的圖標(biāo)了。

四 繼續(xù)上例:將圖標(biāo)資源由使用字符串ID改為使用整數(shù)ID
    前面我反復(fù)地說到字符串ID和整數(shù)ID。你首先要明白,所有資源的ID號(hào)可以定義為字符串的,也可以定義為整數(shù)型的。我在前例中使用的是一個(gè)字符串ID的例子,現(xiàn)在我又要改為使用整數(shù)ID。

1 創(chuàng)建一個(gè)名為Resource.h頭文件,內(nèi)容如下:
#define IDI_MYICON 100              

    將IDI_MYICON 定義為一個(gè)整數(shù)型符號(hào)常量。這個(gè)數(shù)值應(yīng)是以1以上一個(gè)數(shù)值。
    (注意:頭文件最后要有一空行,也是是說 #define IDI_MYICON 100 后要按一個(gè)回車鍵。)

2 修改myRes.rc文件。
//myRes.rc
#include “Resource.h”   //包含頭文件resource.h
IDI_MYICON    ICON  DISCARDABLE  " myResLib\ myicon.ico"
    
    經(jīng)過上述兩處的添加后,IDI_MYICON就不在是字符串ID了,而是整數(shù)ID。因?yàn)镽esource.h已經(jīng)將IDI_MYICON定義為一個(gè)整數(shù)的符號(hào)常量。

3 在WinMain( )函數(shù)所在的源文件中添加包含Resources.h頭文件,并修改LoadIcon()函數(shù)。
// MyAppMain.cpp :主函數(shù)及回調(diào)函數(shù)

#include
#include
#include "resource.h"   //包含resource.h

int WINAPI WinMain(HINSTANCE hinstance,
                 HINSTANCE hprevinstance,
                 LPSTR lpcmdline,
                 int ncmdshow)
{
    //……
wndclass.hInstance = hinstance;
    wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon =NULL;
//wndclass.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE( IDI_APPLICATION ) ) ;
//上句注釋了
wndclass.hIconSm = LoadIcon(hinstance, (LPCTSTR)IDI_ICON1) ;
   //第二個(gè)參數(shù)由” IDI_ICON1”字符串改成(LPCTSTR) IDI_ICON1
    wndclass.lpszMenuName =NULL;
    wndclass.hbrBackground =(HBRUSH) GetStockObject(WHITE_BRUSH);
    wndclass.lpszClassName =WND_CLS_NAME;

}
    分析:
    凡是要使用整數(shù)ID資源的文件都要包含Resource.h 頭文件,這是因?yàn)檎麛?shù)ID是由Resource.h定義的。(這個(gè)好理解!)
    修改LoadIcon()調(diào)用。因?yàn)長(zhǎng)oadIcon的第二個(gè)參數(shù)接受的是字符串ID(就是LPCTSTR指針),所以,我們得把整數(shù)ID轉(zhuǎn)化成LPCTSTR。(LPCTSTR) IDI_MYICON目的就是將IDI_MYICON強(qiáng)制轉(zhuǎn)化成LPCTSTR。
    我們可是辛辛苦苦地把字符串ID改成整數(shù)ID,現(xiàn)在調(diào)用LoadIcon()時(shí)又要將IDI_MYICON 強(qiáng)制類型轉(zhuǎn)化成LPCTSTR類型。嘻嘻,真有意思,好似我們?cè)谙拐垓v似的,到頭來又要回到了原來的狀態(tài)。但不管怎么說,這也是一種方式噢!
    好了,現(xiàn)在你可以編譯運(yùn)行試試了。哈哈,也是相同的作用喲!

 

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
孫鑫VC視頻教程筆記之第一課“Windows程序的內(nèi)部運(yùn)行原理”
windows窗口的創(chuàng)建
Win32編程基礎(chǔ)知識(shí)_天空總是藍(lán)色的
windows編程——背景圖片和透明特效的使用2
Windows 程序內(nèi)部運(yùn)行機(jī)制
Windows程序基本結(jié)構(gòu)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服