探索Win32系統之窗口類(Window Classes in Win32)(異域の蝸牛注:關於win32編程中的窗口類的探討)

  
作者:vcbear
  
 
翻譯說明:以前一直沒有很在意窗口類的使用,我近期在用vc做一些界面時遇到窗口類定義的概念,這次翻MSDN的時候看到了一篇文章,講到窗口類的一些基本概念,匆匆看完,翻譯出來當學習筆記,可能有一些細節上的謬誤,請包涵。放在網上,如果大家覺得值得參考,就看看。
在MFC編程裏,窗口類的概念接觸的已經比較少了,在SDK編程的時候則至少還需要聲明一個WNDCLASS(EX)類RegisterClass(Ex)一下。但是窗口類在windows中是無所不在的,雖然不是很深奧的東西,瞭解一下其中細節,個人覺得對編程應該是有好處的。
vcbear翻譯 [email protected]
 
<正文>
探索Win32系統之窗口類Window Classes in Win32
Kyle Marsh
Microsoft Developer Network Technology Group
MSDN
技術組
  • 摘要
本文主要介紹win32系統裏窗口類的運做和使用機制,探索一些細節問題,使win32窗口類的信息更加明朗化。
在本文中,"""窗口類"這兩個術語等同,都不是指C++類,而是指和窗口相關的一組信息的集合。
  • 簡介
窗口類的風格決定了窗口的外觀和風格。所有的窗口都會屬於某一個窗口類。在創建一個窗口之前,必須註冊(register)一個相應的窗口類。32Windows操作系統類可以註冊被系統裏所有的程序所使用的窗口類。
大部分開發人員認爲窗口類是個麻煩的東西,他們頂多就是從例子或其他代碼中拷貝一個RegisterClass函數,修改一下部分參數而已。這彷彿有些輕視了,沒有發揮窗口類的作用。本文將對此進行探索,並且描述窗口類如何的使應用程序得到優化。
我們的將討論的題目包括:
  • 什麼是窗口類(Windows Classes)
  • 系統全局,應用程序全局,應用程序局部類的區別
  • 類裏包含了那些信息
  • 這些信息如何影響窗口的表現
  • 應用程序如何使用窗口類信息。
 
一:窗口類的類型
window系統提供了三種類型的窗口類
  • 系統全局類(System global classes
  • 應用程序全局類(Application global classes
  • 應用程序局部類(Application local classes
1.系統全局窗口類(System Global Classes
windows 本身註冊了幾個系統全局類供全部的應用程序使用,這些類包括了以下的常用標準窗口控件
  • Listbox (列表框)
  • ComboBox (下拉組合框)
  • ScrollBar (滾動條)
  • Button (按鈕)
  • Static (靜態標籤)
  • Edit (編輯框)
以及其他不那麼常用的控件如TabCtrl等.
還有:
  • 菜單窗口
  • 桌面窗口
  • 對話框窗口
  • 任務條窗口
  • 題頭帶圖標的窗口
  • ComboLBox:ComBoBox控件的下拉列表窗口
  • MDIClient: MDI風格窗口的子窗口
WindowsNTDDEMLDynamic Data Exchange Management Library)增加了DDEMLEvent類,因爲DDEML功能已經結合到USER裏去了。
Windows 95/98不註冊類 #32772,因爲它不使用題頭帶圖標風格的窗口(由於我用的是win2k操作系統,這一點沒有嘗試)
所有的win32應用程序都可以使用系統全局類,但不能增加或刪除一個這樣的類。
應用程序可以通過子類化”(SubClassing)這些類來改變系統全局類的屬性。在Win32裏,應用程序子類化某個系統全局類只會影響本進程內窗口的表現,而不會影響另外一個進程或應用程序。比起相應的操作會影響其他窗口的win16時代,這是一個進步。
Win32裏,Ms鼓勵子類化系統類的行爲。因爲這個技術可以非常有效和方便的改變窗口的表現。例如:如果應用程序希望限制edit控件的輸入和編輯行爲,可以通過子類化edit類並設置一個新的窗口過程(WindowProc)來自己處理處理鍵盤操作來實現。子類化以後,此應用程序裏創建的edit控件將使用新的窗口過程,以代替標準的edit控件窗口過程。
系統全局窗口類實現
現在的win32平臺使系統類和各32位進程互不相干,系統類如何實現的並不會直接影響應用程序。本節將描述系統類的實現,當然,跳過此節並不妨礙全文的閱讀和理解。
  • win9x裏的實現
系統全局類在win9xwin3.1的實現使相當相象的。在系統啓動時,USER模塊創建了系統類。win9x和win3.1不同的是:當發現一個應用程序子類化了某個系統類的時候,win9x將進行如下工作:
  • 如果在debug模式下運行,在debug屏幕上顯示一個 warning 信息
  • 複製一份被子類化的窗口類的信息 。將複製的新類填加到應用程序的私有系統類列表裏。win9x系統裏,系統爲每個進程都保持了這樣的一個列表,以供系統存放系統全局類的克隆信息。
  • 強制進程裏所有的子類化過的窗口實例使用這個系統類的拷貝。但這不影響已經存在的窗口,窗口是使用事先已經拷貝到窗口實例數據區的類信息,並非直接使用進程裏保存的類的信息。子類化只更新了進程的窗口類列表裏的類的信息,而沒有更新窗口實例裏的類。
16位應用程序共享相同的進程空間。在win9x裏,16位程序的表現和它在win3.1裏是一樣的。
  • winNT的實現
winNT則有很多的不同。winNT包括了兩個win32子系統:一個服務進程和一個在各win32進程裏運行的動態連接庫(DLL)。以edit類爲例,winNT在各進程空間裏,從DLL裏導出和註冊edit類。這樣,處理EDIT控件的代碼可以存在於DLL裏,也即在各進程空間裏。不需要系統分配局部過程調用來處理Edit控件,應用程序對控件的頻繁調用所導致的系統開銷也被避免了。因爲EDIT 控件實例僅僅在各進程空間裏操作自身數據,所以對系統魯棒性的衝擊就降低了。
服務進程管理每個win32應用程序的信息,包括應用程序的公有和私有窗口類。當創建一個win32線程的過程開始(即某個線程調用USER模塊或GDI模塊的函數時),USER模塊檢查該線程是否該進程的第一個線程,如果是(一般是主線程),USER模塊爲該進程註冊系統類。當爲了任何一個進程而註冊一個類(服務模塊進程除外),該類就會添加到該進程的公有或私有列表裏。爲了提高效率,windows爲每個進程都註冊系統類,並且把類信息的拷貝儲存在應用程序的空間裏。這增加了魯棒性,但是比起Windows95,增加了需要使用的內存。windowsNT也由此獲得了更高的性能,因爲當子類化一個系統類的時候,winNT不需要象window95那樣重新分配內存和拷貝類信息。
winNT,16位的應用程序依然共享同一進程,也共享所有的系統全局類。16位程序總是不穩定因素的起源。
2.應用程序全局類。
應用程序全局類是註冊的時候指定了CS_GLOBALCLASS標誌的類(該標誌還有後續敘述)。
16位系統比如win3.1的應用程序全局類是真正意義的全局的,一個DLL或應用程序註冊的應用程序全局類,系統內所有的DLL和應用程序都可以使用。一個應用程序全局類在“全局”的意義上和系統全局類一致,只是它是由應用程序創建的而不是系統創建的而已。
Win32的應用系統全局類本質的不同是:應用程序全局類只是在進程內部全局而已。這是什麼意思呢?一個DLL.EXE可以註冊一個類,這個類可以讓在相同的進程空間裏其他.EXEDLL使用。如果一個DLL註冊了一個非應用程序全局類的窗口類,那麼,只有該DLL可以使用該類,同樣的,.EXE裏註冊的非應用程序全局類也適用這個規則,即該類只在該.EXE裏有效。
作爲這個特性的擴展,win32有一項技術,允許一個第三方窗口控件在DLL裏實現,然後把這個DLL載入和初始化到每個Win32進程空間裏。這項技術的細節是,把DLL的名字寫入註冊表的指定鍵值裏:
HKEY_LOCAL_MACHINE/Software/Microsoft/Windows NT/CurrentVersion/Windows/APPINIT_DLLS
這樣當任意一個win32應用程序加載的時候,系統也同時將該dll加載到進程空間裏(這可能有點過於奢侈,因爲很多win32程序不一定會使用該控件)。DLL在初始化的時候註冊應用程序全局類,這樣的窗口類就可以在每個進程空間的.EXEDLL裏使用了。這個技術基於win32系統的這個特性:允許在每個進程空間裏自動的(也是強制的)加載特定的DLL(事實上,這也是打破進程邊界,把你的代碼切入到其他進程裏的一種辦法)。
 
3. 應用程序局部類
WIN32應用程序局部類是使用最頻繁的類(絕大部分的應用程序爲主窗口註冊的類都是應用程序局部類),僅僅在聲明和註冊該類的應用程序模塊或DLL自身裏使用。註冊一個應用程序局部類和應用程序全局類的區別是,局部類不包括CS_GLOBAL CLASS標誌。
 
二:窗口類包含的信息和作用
窗口類都包含些什麼信息呢?讓我們看以下窗口類結構體。
WNDCLASS結構包含的是一般的窗口類的信息
typedef struct tagWNDCLASS {       UINT      style;    WNDPROC   lpfnWndProc;    int       cbClsExtra;    int       cbWndExtra;    HINSTANCE hInstance;    HICON     hIcon;    HCURSOR   hCursor;    HBRUSH    hbrBackground;    LPCSTR    lpszMenuName;    LPCSTR    lpszClassName;} WNDCLASS;
成員
描述
style
一組標誌位的組合。定義比如窗口位置,設備上下文(DC)分配,雙擊的處理等特徵。
lpfnWndProc
指向窗口過程的地址,該窗口過程負責處理窗口類相應的窗口消息
cbClsExtra
指明需要額外分配的內存數量,單位爲byte,系統爲該類分配保留指定數量的額外內存
cbWndExtra
指明需要額外分配的內存數量,單位爲byte,系統爲每個該類所對應的窗口實例分配保留指定數量的額外內存
hInstance
標識註冊該類的DLL或應用程序實例
hIcon
當一個屬於該類的窗口被最小化的時候,顯示的圖標.
hCursor
屬於鼠標該類的窗口裏顯示的指針
hbrBackground
定義當程序打開或重畫某個屬於該類的窗口是,填充窗口客戶區的顏色和樣式
lpszMenuName
如果沒有顯性定義菜單時,窗口的默認菜單
lpszClassName
字符串的類名
以下詳細討論各成員的具體意義:
  • Class Styles (style)
Style成員決定了從該類創建出來的窗口的風格,可以使用下列值的一個或幾個的組合。
CS_BYTEALIGNCLIENT ,CS_BYTEALIGNWINDOW
如果使用這兩個標誌,窗口的的客戶區或整個窗口都在“字節邊界”上對齊,也就是說,系統調整窗口的水平位置,客戶區或整個窗口的左邊座標是8的倍數。win32 SDk的文檔說這兩個標誌影響窗口的寬度,但是實際上筆者沒有發現這個現象,此標誌隻影響窗口的水平位置(左邊)。
看看系統如何擺放一個邊框寬度爲4的窗口:
Original window location
Placement with CS_BYTEALIGNWINDOW
Placement with CS_BYTEALIGNCLIENT
0,y
0,y
4,y
1,y
0,y
4,y
2,y
0,y
4,y
3,y
0,y
4,y
4,y
8,y
4,y
5,y
8,y
4,y
6,y
8,y
4,y
7,y
8,y
4,y
8,y
8,y
12,y
9,y
8,y
12,y
10,y
8,y
12,y
11,y
8,y
12,y
12,y
16,y
12,y
13,y
16,y
12,y
14,y
16,y
12,y
15,y
16,y
12,y
16,y
16,y
20,y
這兩個標誌在以下兩個情況中無效:
  • 如果顯示設備對每個象素使用8個或更多的位數,字節對齊並不會帶來什麼好處。這種情況下,系統在創建和移動的時候忽略了CS_BYTEALIGNCLIENTCS_BYTEALIGNWINDOW這兩個標誌。
  • 如果使用SetWindowPos函數改變窗口的位置,此函數忽略窗口的CS_BYTEALIGNCLIENTCS_BYTEALGNWINDOW標誌指定的位置限定。在Win32 SDK文檔關於WM_WINDOWPOSCHANGEING的描述讓人費解,它說:對於一個有WS_OVERLAPPEDWS_THICKFRAME風格的窗口來說,DefWindowProc函數響應WM_WINDOWPOSCHANGING消息,並向窗口發送一個WM_GETMINMAXINFO消息,此消息的處理是驗證窗口的新位置和尺寸,迫使窗口接受CS_BYTEALIGNCLIENTCS_BYTEALIGNWINDOW限定
其實當DefWindowProc接受到WM_WINDOWPOSCHANGING消息後,確實發送一條WM_GETMINMAXINFO消息,但是並不迫使窗口接受字節對齊的風格,爲了確保字節對齊風格,應用程序必須通過計算再改變其水平位置
CS_BYTEALIGNCLIENT CS_BYTEALIGNWINDOW的存在是爲了優化程序的性能,尤其在是3.0版本的windows系統以前。那時侯,所有的系統字體寬度都是固定的,系統可以通過使窗口字節對齊優化字體的顯示。在3.0以後的window裏,這一點已經被忽略了。
如果程序使用BitBlt函數從一個窗口向另一個窗口,或者從窗口的某個矩形區域向另一個矩形區域拷貝象素,還是可以通過設置CS_BYTEALIGNCLIENT標誌來獲得更好的性能。如果客戶區是字節對齊的,程序也可以儘量保證BitBlt操作發生在字節對齊的矩形裏, BitBlt操作將會比發生於非字節對齊的矩形裏的操作更快。當然,字節對齊僅僅是對窗口的左邊界而言的,如果要進一步的提高性能,應該連寬度都進行字節對齊。
其實對於可以顯示256及以上顏色的視頻卡或顯示器,對字節對齊的需求已經微乎其微了。256色顯示器已經自己實現了字節對齊,實際上,一些16色的顯示器也是字節對齊的。在大部分顯示器上根本看不出來字節對齊限定對窗口位置的影響。一句話,字節對齊標誌已經沒有什麼重要的作用了。
使用CS_BYTEALIGNWINDOW的時候也等同於包含了CS_BYTEALIGNCLIENT;對話框類本身已經默認包含了CS_BYTEALIGNWINDOW標誌
這幾個標誌決定窗口的默認DC
  • 如果使用CS_OWNDC標誌,屬於此窗口類的窗口實例都有自己的DC(稱爲私有DC),私有DC僅屬於該窗口實例,所以程序只需要調用一次GetDCBeginPaint獲取DC,系統就爲窗口初始化一個DC,並且保存程序對其進行的改變。ReleaseDCEndPaint函數不再需要了,因爲其他程序無法訪問和改變私有DC。當選擇了CS_OWNDC,程序改變影射模式(Mapping Mode)的時候必須小心,當由系統擦除窗口的背景時,系統假定和默認其影射模式是MM_TEXT。如果私有DC的影射模式不一樣,窗口被擦除的地方將不再可見。CS_OWNDC標誌在WinNTWin9x的作用也是有差別的。在WinNT,win32子系統和其他NT進程有相同的地址空間(4GB),應用程序使用此地址空間裏的2GB,每個有CS_OWNDC標誌窗口實例佔用800個字節,在NT下,這裏面沒有什麼問題。而Win9xGDI保留了64K的局部堆,DC進入這個堆之後,即使其他GDI對象數據被釋放,DC依然在。這意味着每個CS_OWNDC的窗口都在這個寶貴的內存空間裏佔用800個字節。所以,爲win9x寫的程序最好儘量少的使用CS_OWNDC這個標誌。win9x的修正版打算解決這個問題,但效果不明顯(而且win9x已經開始式微了)如果使用CS_CLASSDC標誌,所有屬於該類的窗口實例共享相同的DC(稱爲類DC.DC有一些私有DC的優點,而更加節約內存(因爲不需要爲每個窗口實例都分配800字節的DC空間了)。每個窗口實例都通過GetDCBeginPaintde得到設備上下文(DC)句柄,如果沒有別的窗口需要該DC,不需要調用ReleaseDCEndPaint釋放DC。在一個窗口實例上通過GetWindowDC,GetDC,GetDCEx,BeginPaint獲得 DC,並對其中的一些參數進行更改的話,所進行的更改除了剪切區域和設備本身屬性(Device origin)之外對所有其他窗口實例都是有效的。和CS_OWNDC相同的是,必須確保影射模式也是MM_TEXT,否則,被系統擦除的背景將不再可見。NT編寫的程序最好不要使用這個標誌,因爲節約內存的好處根本不明顯。對於win9x來說,卻是有用的,因爲對於win9x64KGDI局部堆來說,節約的空間意義更重大一些。
  • 如果使用了CS_PARENTDC標誌,屬於這個類的窗口都使用它的父窗口的句柄。和CS_CLASSDC相似的是,多個窗口共享一個DC,不同的是,這多個窗口(雖然有父子關係並且共享DC)並不要求都屬於同一個窗口類。WIN9x下,所有的標準窗口控件都有CS_PARENTDC標誌。WinNT下,除了ComBoBox之外的窗口控件都有此標誌。因此,比如Edit控件和ListBox控件都共享他們的父窗口(比如對話框)的DC(注:這一段是我根據原文再加上自己的理解,不一定完全正確:) CS_PARENTDC帶來的好處就一個字:速度。Win9x系統爲每個線程預留了5DC緩衝區,如果一個窗口(比如一個對話框)有多於5個的字窗口(比如有6個或以上的編輯框),而每個子窗口都有自己的DC的話,DC緩衝區就失去了效力,系統得爲每個子窗口根據剪切邊界和設備屬性重新初始化一個DC,這多於5個的DCDC緩衝裏不停交換,不能確保在緩衝裏被訪問。而如果每個子窗口都和父窗口共享一個DC,在頻繁訪問該DC時,該DC在於DC 緩衝裏被找到的機率顯然相當的高,從而可以被高速的訪問,顯著的提高了速度,所以一般來說標準窗口控件都和父窗口共享DCWinNT可以擁有多於5個的DC緩衝,所以它可能可以提供足夠的DC緩衝 -- 但是不保證時時如此。使用CS_PARENTDC的另一個效果是,子窗口可以在父窗口的客戶區隨意做畫,就象畫在自己的客戶區一樣. 負責表現Edit控件和ListBox控件周圍的3D效果的CTL3D庫就是利用了這個特性。注意如果程序需要改變各子窗口的影射模式,那麼最好不要用CS_PARENTDC標誌,否則將很容易引起各子窗口影射模式的混亂,因爲所有的子窗口都使用同一個DC
  • 如果不指定CS_OWNDCCS_CLASSDCCS_PARENTDC這幾個標誌,此類的窗口使用一個通用DC,並置於DC緩衝裏以供使用。通用DC在使用前獲取,使用後釋放,在DC獲取的時候,DC裏的上下文按默認值初始化,除非當時該DC已經在窗口的DC緩衝裏(比如沒有調用ReleaseDCEndPaint釋放DC),這樣的話DC的剪切邊界和設備屬性都不需要被重新初試化,可以節約一些時間。WinNT裏,DC緩衝沒有確定的數量。如果所有的DC緩衝都在使用中,而程序調用了GetDCBeginPaint,NT則再分配一個緩衝。出於對win9x的兼容考慮,win32程序最好把 DC的使用限制在5個以下,並且儘可能快的釋放DC.如果要忽略類創建時由標誌位決定的窗口的默認DC,程序可以使用GetDCEx函數,指定DCX_CACHE標誌,則完全忽略CS_OWNDCCS_CLASSDC標誌,並從緩衝裏返回一個通用DC.ScrollWindowScrollWindowEx函數處理DC的方法則有所不同:
  • ScrollWindow使用窗口默認的DC。因此,如果窗口使用CS_OWNDCCS_CLASSDC標誌,ScrollWindow使用相應的DC(窗口私有DC或類DC,其影射模式有可能被程序改變,不爲MM_TEXT.傳遞給ScrollWindow的座標值必須和DC的影射模式相一致。
  • ScrollWindowEx使用系統通用DC(這種DC使用MM_TEXT的影射模式),而忽略窗口的標誌。傳遞給ScrollWindowEx的座標值必須是MM_TEXT影射模式下的客戶區座標值。
CS_DBLCLKS標誌使窗口可以檢測到雙擊事件。窗口響應雙擊的細節如下:
  • 如果窗口沒有CS_DBLCLKS標誌,系統向窗口依次發送如下消息:
WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_LBUTTONDOWN
  • WM_LBUTTONUP.
其實相當於兩個單擊。
  • 如果窗口有CS_DBLCLKS標誌,則系統向窗口依次發送如下消息:
WM_LBUTTONDOWN
  • WM_LBUTTONUP
  • WM_LBUTTONDBLCLK
  • WM_LBUTTONUP.
第一種情況中的第二個WM_LBUTTONDOWNWM_LBUTTONDBLCLK代替了。
注意,在上述序列中間可能會插入其他的一條或一些消息,所以這兩個消息序列不一定是完全連續的。
其實,在沒有指定CS_DBLCLKS標誌時,程序本身也可以檢測到雙擊事件的。參見MSDNDr.GUT"Simulating Mouse Button Clicks"文章,不過要有一些技巧.一般的情況下,如果沒有指定CS_DBLCLKS,在窗口的消息循環裏將不會得到WM_LBUTTONDBLCLK消息。
所有的標準窗口控件,對話框,桌面窗口類都默認擁有CS_DBLCLKS標誌。第三方控件最好也加上此風格,以使其在對話框編輯器裏可以正常工作。
CS_GLOBALCLASS是唯一一個針對類本身起作用而不是對單個窗口起作用的標誌.系統將包含這種標誌的窗口類作爲應用程序全局類保存,這樣類可以應用於註冊該類的進程內的所有EXEDLL,當EXEDLL退出或卸載,或對該類調用UnregisterClass後,該類則被銷燬。在註冊該窗口的程序退出前,所有從該類創建的窗口都必須先關閉(對於DLL來說,這是自動進行的)。
CS_HREDRAW標誌表示當窗口的水平尺寸(寬度)改變的時候,重畫整個窗口。CS_VREDRAW則是在窗口垂直尺寸(高度)改變時重畫整個窗口。按鈕和滾動條都有這兩種風格。
如果指定了CS_NOCLOSE標誌,則窗口上的關閉按鈕和系統菜單上的關閉命令失效。
菜單,對話框,下拉框都擁有CS_SAVEBITS標誌。當窗口使用這個標誌時,系統用位圖形式保存一份被窗口遮蓋(或灰隱)的屏幕圖象。首先,系統要求顯示驅動保存圖象位數據,如果顯示驅動本身的存儲空間足夠,保存操作成功,window系統也可以使用這些保存的位數據。如果不夠,系統將位數據在全局內存裏以位圖的方式保存,並且在USE模塊局部堆裏爲每個窗口分配空間,保存一些事務數據(比如位圖數據緩衝的大小)的結構。當程序使遮蓋屏幕的窗口消失時,系統可以很快的從內存裏恢復屏幕圖象。
CS_SAVEBITS的效率本身是很難度量的。CS_SAVEBITS提高了“臨時”窗口比如菜單,對話框,下拉框的性能。但是,存貯位信息的開銷也是很明顯的,尤其由系統代替顯示驅動存儲位信息的時候,系統承擔了速度和存儲開銷。使用CS_SAVEBITS的好處其實依賴於窗口遮蓋的區域發生了什麼事情,如果該區域相當複雜,需要重畫很多的效果,那麼,存儲該區域可能比重畫該區域要來的輕鬆,如果反之,該區域可以相當快速的重畫,或者在被遮蓋的時候還經常發生變化並且變化很顯著,保存的方案反而影響了整體性能。
 
以上都是style成員的可選標誌。
 
WNDCLASS結構裏的lpfnWndProc成員保存了該窗口類的窗口過程地址。該窗口類的所有窗口都使用該過程地址,對於從該類創建的窗口,系統將所有相關的消息交給此窗口過程來處理。窗口過程實現窗口的功能,程序可以使用SetClassLong函數來改變窗口類的窗口過程。這個操作叫“子類化”(Subclassing)。當程序改變了該過程的地址,在改變前已經創建的窗口還是使用原來的地址,而以後創建的窗口才使用新的過程地址。
當一個程序或DLL子類化一個窗口或設置窗口過程函數,必須在模塊定義文件裏輸出該新窗口過程。
  • Extra Class and Window Bytes (cbClsExtra and cbWndExtra)
cbClsExtra成員指明爲每個窗口類多分配的額外數據量,cbWndExtra成員則是指明爲每個窗口實例分配的額外數據量。如果程序不需要分配額外的數據,這兩個成員的值都應該設置爲 0,以免產生不確定的數(比如一個非常大的數)而使系統進行錯誤分配,如果是負數,則該窗口類將不會被註冊。
Win9xNT,分配40個及以下字節是沒有多大意義的,當然,開發人員可以根據需要分配額外數據大小。
如果用類聲明並註冊一個對話框類型的窗口,cbWndExtra的值必須設置爲DLGWINDOWEXTRA,系統對話框管理器需要這麼多的額外數據對對話框進行管理。
  • Instance Handle (hInstance)
WNDCLASShInstance成員標識類所在的模塊。此成員可以爲進程的hInstance,DLLhInstance,但不可以爲NULL.
  • Class Icon (hIcon)
WNDCLASShIcon成員標識此窗口類的圖標。程序一般使用LoadIcon,從系統標準圖標庫(如IDI_APPLICATION)或用戶指定的圖標資源中來獲取一個圖標句柄。如果hIcon的值爲NULL,當系統給程序發送WM_ICONERASEBKGND消息的時候,程序給窗口畫上程序的主圖標.
  • Class Cursor (hCursor)
WNDCLASS裏的hCursor成員表示屬於該類窗口的默認鼠標指針。當設置了該值,當鼠標移入窗口區域時,系統將指針由系統默認形狀變成所設置的指針形狀。程序可以使用LoadCursor函數從標準系統指針庫(比如IDC_ARROW)或用戶指定指針資源中獲取指針句柄。程序可以通過SetCursor函數隨時改變指針。如果hCursor的值未設置(設置爲NULL,程序必須在鼠標指針移入窗口時進行設置,否則將使用系統默認的鼠標指針形狀。
  • Class Background Brush (hbrBackground)
WNDCLASS裏的hbrBackground成員變量表示背景顏色的,類型爲HBRUSH,即GDI刷子句柄。可以對其賦值爲一個刷子句柄(比如用GetStockObject獲取系統內置刷子對象句柄,或者自己創建指定顏色和風格的刷子的句柄)或者顏色值。如果是選擇顏色值的話,必須使用下列系統標準顏色之一。
COLOR_ACTIVEBORDER
COLOR_HIGHLIGHTTEXT
COLOR_ACTIVECAPTION
COLOR_INACTIVEBORDER
COLOR_APPWORKSPACE
COLOR_INACTIVECAPTION
COLOR_BACKGROUND
COLOR_INACTIVECAPTIONTEXT
COLOR_BTNFACE
COLOR_MENU
COLOR_BTNSHADOW
COLOR_MENUTEXT
COLOR_BTNTEXT
COLOR_SCROLLBAR
COLOR_CAPTIONTEXT
COLOR_WINDOW
COLOR_GRAYTEXT
COLOR_WINDOWFRAME
COLOR_HIGHLIGHT
COLOR_WINDOWTEXT
用顏色值賦值時,必須強制類型轉換爲HBRUSH類型:
cls.hbrBackground   = (HBRUSH)(COLOR_WINDOW+1);
如果hbrBackground成員設置爲NULL,程序必須在響應WM_PAINT的時候負責畫背景。程序也可以響應WM_ERASEBKGND消息,或根據調用BeginPaint函數時填充的PAINTSTRUCT結構裏的成員fErase的值類判斷是否需要重畫背景。
  • Class Menu (lpszMenuName)
WNDCLASS裏的lpszMenuName成員表示默認主菜單。可以使用MAKEINTRESOURCE宏把資源裏菜單項的ID號轉變成連續的字符串值賦給該成員。如果使用CreateWindowCreateWindowEx函數從該類創建窗口時沒有在函數參數裏指定別的菜單資源,那麼出現在各窗口上的主菜單就是lpszMenuName指定的菜單。如果lpszMenuNameNULL,窗口則沒有默認的主菜單。
  • Class Name (lpszClassName)
WNDCLASS裏的lpszClassName結構表示類的名字。局部窗口類名在進程範圍必須爲唯一的,由於僅要求“在進程內唯一”,對於兩個不同的程序來說,其中的窗口類名有可能相同,比如,兩個程序都可能擁有各自的"Main Wnd"窗口。全局窗口類的名字必須在全局窗口類和系統窗類口範圍裏唯一,比如,程序可以註冊一個名字爲"Edit"的局部窗口類,但是無法註冊同樣名字的一個全局窗口類。
 
三 系統如何定位窗口類
當程序需要根據一個類創建一個窗口,系統通過以下步驟來定位該類
1.系統在本進程的局部窗口類列表裏尋找有相同名字的類。如果找到,由於局部窗口類是屬於某一個.EXE模塊或DLL的,所以所找到的類的進程實例句柄(hInstance)和本模塊的實例句柄應該保持一致。這樣的規則是爲了防止進程內一個模塊或DLL裏註冊的局部類被進程其他及其的模塊或DLL發現。
2.如果系統無法發現一個同名的局部類,那麼,就繼續搜索進程的全局類列表,這次的搜索不比較實例句柄。
win9x,如果在程序全局類空間裏無法發現同名類,則繼續查詢進程內的系統窗口類列表。
3.      如果系統還是無法發現同名窗口類,則繼續搜索全局系統類列表。
winNT將此過程應用於所有由程序創建的窗口,包括從主窗口再創建出的其他窗口,比如對話框和消息框。
win9x 同樣將此過程應用於所有的窗口,除了消息框以外。當程序彈出一個消息框,Win9x不對局部類列表進行搜索,而是直接進行後續步驟。
四 應用程序的如何使用窗口類信息。
一旦註冊了一個類,一般來說除了使用該類創建窗口之外就沒有什麼需要作的事情了。當然,如果需要訪問該類信息,子類化,或者超類化該類,介紹一些方法就是有用的。
  • 類訪問函數
如果需要獲取和改變類的信息,可以使用以下函數:
  • GetClassLong, 從類信息讀回來一個Long類型的值(比如,窗口類的窗口過程地址)
  • SetClassLong 對該類的某成員寫入一個long類型的值。比如,寫入一個新的窗口過程的地址。
  • GetClassWord 類信息讀回來一個word類型的值。比如以下調用得到類的額外數據量:
nClassExtra = GetClassWord(hwnd,GCW_CBCLSEXTRA);
SetClassWord 向類信息寫入一個word類型的值。比如改變窗口類圖標。
  • GetClassName 獲取窗口類的名字。
  • GetClassInfo 獲取除類名和菜單以外的全部類信息。
具體的調用參數以及其他相關的一些類函數可以參見MSDNwindows classs Functions 一節。
  • 子類化:(SubClassing)
術語“子類化”(subclassing)描述的是用一個新的窗口過程代替原窗口過程。術語“實例子類化”(即子類化單個窗口)是指使用SetWindowLong函數改變某一個窗口實例的窗口過程。“全局子類化”(子類化整個窗口類)則是指使用SetClassLong改變整個類的默認窗口過程函數。
32windows 系統裏,可能難於子類化另一個進程裏的窗口或窗口類,一般來說,子類化都是發生於同一個進程裏的(“打破進程邊界”的相關的主題本文沒有涉及)。
  • 超類化:(Superclassing)
術語"超類化"(Superclassing)指創建一個新的類,該類使用某個現存類的窗口過程,繼承該類的基本功能,並可以在此基礎上進行擴展。
關於子類化和超類化的具體描述,請參閱MSDN"safe subclasing in Win32"一文
 
結束語: win32的窗口類給開發人員帶來了很多有用的信息,在win32平臺上開發程序,最好是注意使用兼容winNT和win9x的一些特性以保證程序的兼容性。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章