Window 視窗類別

來源:互聯網
上載者:User

標籤:

視窗類別 WNDCLASS 總結總結為下面的幾個問題:1. 什麼是視窗類別2. 視窗類別的三種類型3. 視窗類別各欄位含義4. 視窗類別的註冊和登出5. 如何使用視窗類別,子類化、超類化是什麼下面分別描述:1. 什麼是視窗類別?    視窗類別定義了一系列屬性,系統使用這些屬性作為模板來建立出一個或多個 window (視窗)。    每個視窗類別都關聯了一個視窗過程函數(window procedure), 由視窗類別建立出的所有視窗(window), 都共用同一個視窗過程函數。    在進程中建立視窗之前必須先註冊視窗類別(RegisterClassEx)。註冊視窗類別時,會將視窗類別與一個視窗類別名(class name)關聯起來。當建立視窗時,只要傳入視窗類別名,系統就能幫你找到對應的視窗類別,從而建立出視窗。        由視窗類別建立出來的視窗具備什麼功能、如何顯示,這些絕大部分取決於視窗過程函數怎麼寫。        視窗類別和視窗的關係,可以類比為類和對象的關係。視窗類別就像類定義,而視窗就像對象。    視窗過程函數就像類的成員函數,視窗過程函數中第一個參數就是視窗的控制代碼,類似於類的成員函數預設有一個 this 指標。    C++ 中的類定義會由連結器幫忙找到,Win32 沒有這回事兒,所以需要進行註冊視窗類別的操作,這樣才能根據類名找到類定義。        問題:自訂了一個視窗類別,比如自己實現了一個 listbox 控制項,怎樣為這個控制項添加外部介面呢? 類似於 listbox.SetListItem() 這樣的介面怎麼實現?此處存疑待考    2. 視窗類別的三種類型    有三種類型的視窗類別,它們在作用範圍、註冊和銷毀時機上有所差別:        1. 系統視窗類別(System Classes)            系統註冊的視窗類別,程式無需註冊就能使用。            系統會在程式第一次調用 GDI 函數時為其註冊系統視窗類別, 每個程式都會得到一份系統視窗類別的拷貝。                    範圍:   系統內所有 Win32 進程            銷毀時機: 無法銷毀                    以下是可以被程式直接使用的一些 System Classes:                Button                ComboBox                Edit                ListBox                MDIClient                ScrollBar                Static            有些 System Classes 是系統內部使用的:                ComboLBox                DDEMLEvent                     Message                #32768                #32769                #32770                #32771                #32772                    2. 程式全域視窗類別(Application Global Classes)            程式自行註冊的視窗類別, WNDCLASS 的 style 指定為 CS_GLOBALCLASS, 註冊後當前進程內全部模組都可以使用。            例如,在 a.dll 的 DllMain 裡註冊一個全域視窗類別,如果 b.exe 載入了 a.dll, 那麼就可以在 b.exe 裡使用那個視窗類別。全域指的就是這個意思。                    範圍:   某進程的全部模組(exe, dll)            銷毀時機: 自行 UnRegisterClass                    作為這個特性的擴充, win32 有一項技術,允許一個第三方控制項在 dll 裡實現,然後把這個 dll 載入到每個 win32 進程地址空間裡。這樣所有進程都可以使用這個控制項,這項技術的細節是,在 dll 的 DllMain 裡註冊第三方控制項,然後把 dll 的名字寫入註冊表裡:                HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\APPINIT_DLLS            這樣當任意一個 win32 進程載入時,系統也同時會把這個 dll 載入這個程式的進程地址空間(這樣做可能有點兒奢侈,並不是每個進程都需要這個第三方控制項)。這樣視窗類別就可以在任意一個 win32 進程裡直接使用了。                    3. 程式局部視窗類別(Application Local Classes)            程式自行註冊的視窗類別, WNDCLASS 的 style 不指定 CS_GLOBALCLASS, 註冊後僅註冊模組可以使用。            例如,在 a.dll 裡註冊的局部視窗類別,只能在 a.dll 裡使用,即使 b.exe 載入了 a.dll, 也不能使用。                    範圍:   僅註冊視窗類別的那個模組可以使用            銷毀時機: 自行 UnRegisterClass                    程式局部視窗類別是使用最頻繁的類(大多數情況下視窗類別不需要給別的模組使用)                    問題:在 dll 裡註冊的全域視窗類別或局部視窗類別, dll 被卸載時,會自動銷毀嗎?如果已經建立出了視窗,視窗會自動銷毀嗎?此處存疑待考。                3. 視窗類別各欄位含義    視窗類別用 WNDCLASS 結構體表示, WNDCLASSEX 是擴充版本,多了一個小表徵圖 hIconSm 成員。    typedef struct tagWNDCLASSEX {        UINT      cbSize;        UINT      style;        WNDPROC   lpfnWndProc;        int       cbClsExtra;        int       cbWndExtra;        HINSTANCE hInstance;        HICON     hIcon;        HCURSOR   hCursor;        HBRUSH    hbrBackground;        LPCTSTR   lpszMenuName;        LPCTSTR   lpszClassName;        HICON     hIconSm;    } WNDCLASSEX, *PWNDCLASSEX;        lpszClassName:        視窗類別名,標識一個視窗類別,相當於視窗類別的 ID;        在同一個模組內,不能註冊同名的局部視窗類別;在同一進程內,不能註冊同名的全域視窗類別;但是,可以註冊跟全域視窗類別或系統視窗類別同名的局部視窗類別,這是由系統尋找視窗類別的順序決定的。根據視窗類別名建立視窗時,系統按照如下順序定位到視窗類別:            1. 根據視窗類別名從局部視窗類別列表中尋找 hInstance 與當前模組的 hInstance 一致的視窗類別(程式的不同模組可以用相同的視窗類別名來註冊局部視窗類別);            2. 如果局部視窗類別列表裡沒有要尋找的視窗類別名,從全域視窗類別列表裡尋找;            3. 如果全域視窗類別列表裡沒有要尋找的視窗類別名,從系統視窗類別列表裡尋找;            按照這個順序,即使局部視窗類別名跟全域視窗類別或系統視窗類別同名,系統依然能正確定位到局部視窗類別。例如,可以註冊一個 "Edit" 的局部視窗類別,在模組內建立 "Edit" 視窗時,會使用局部視窗類別的定義,而不是系統視窗類別。                hInstance:        執行個體控制代碼,標識視窗類別所在的模組,可以是進程的 hInstance, dll 的 hModule, 不能為 NULL;            lpfnWndProc:        視窗過程函數地址,視窗的功能由這個函數實現。                對於從該類建立的視窗,系統會將所有訊息交給此視窗過程函數處理。程式可以使用 SetWindowLong 來改變視窗類別的過程函數,這個操作叫做 “子類化(SubClassing)”, 稍後將具體講述這一操作。            style:        style 成員決定了從該類建立出來的視窗的風格,可以使用下列值的組合:            CS_OWNDC,CS_CLASSDC,CS_PARENTDC:                這幾個標誌決定視窗的預設 DC(device context):                1. 如果使用 CS_OWNDC 標誌,屬於此視窗類別的所有視窗執行個體都由自己的 DC(稱為私人 DC), 所以程式只需要調用一次 GetDC 或者 BeginPaint 擷取 DC, 系統就為視窗初始化一個 DC, 並且儲存程式對其進行的改變。 ReleaseDC 和 EndPaint 函數不再需要了。當選擇了CS_OWNDC,程式改變影射模式(Mapping Mode)的時候必須小心,當由系統擦除視窗的背景時,系統假定和預設其影射模式是 MM_TEXT. 如果私人DC的影射模式不一樣,視窗被擦除的地方將不再可見。                2. 如果使用CS_CLASSDC標誌,所有屬於該類的視窗執行個體共用相同的 DC(稱為類DC). 類 DC 有一些私人 DC 的優點,而更加節約記憶體(因為不需要為每個視窗執行個體都分配800位元組的DC空間了)。每個視窗執行個體都通過 GetDC 或 BeginPaint 得到裝置上下文(DC)控制代碼,如果沒有別的視窗需要該DC,不需要調用 ReleaseDC 或 EndPaint 釋放 DC. 在一個視窗執行個體上通過 GetWindowDC, GetDC, GetDCEx, BeginPaint 獲得 DC,並對其中的一些參數變更的話,所進行的更改除了剪下地區和裝置本身屬性(Device origin)之外對所有其他視窗執行個體都是有效。和 CS_OWNDC 相同的是,必須確保影射模式也是 MM_TEXT, 否則,被系統擦除的背景將不再可見。為NT編寫的程式最好不要使用這個標誌,因為“節約記憶體”的好處根本不明顯。                3. 如果使用 CS_PARENTDC 標誌, 屬於這個類的視窗都使用它的父視窗的控制代碼。和 CS_CLASSDC 相似的是,多個視窗共用一個 DC, 不同的是,這多個視窗(雖然有父子關係並且共用 DC )並不要求都屬於同一個視窗類別。注意如果程式需要改變各子視窗的影射模式,那麼最好不要用 CS_PARENTDC 標誌,否則將很容易引起各子視窗影射模式的混亂,因為所有的子視窗都使用同一個 DC.                4. 如果不指定 CS_OWNDC, CS_CLASSDC, CS_PARENTDC 這幾個標誌,此類的視窗使用一個通用 DC, 共置於 DC 緩衝裡以供使用。通用 DC 在使用前擷取,使用後釋放,在 DC 擷取的時候, DC 裡的上下文按預設值初始化,除非當時該 DC 已經在視窗的 DC 緩衝裡(比如沒有調用 ReleaseDC 或 EndPaint 釋放 DC ),這樣的話 DC 的剪下邊界和裝置屬性都不需要被重新初試化,可以節約一些時間。                                問題:這幾個欄位涉及到尚不瞭解的 DC 和映射模式等概念,存疑待考。            CS_GLOBALCLASS:                CS_GLOBALCLASS 是唯一一個針對類本身起作用而不是對某個視窗起作用的標誌。系統將包含這種標誌的視窗類別作為應用程式全域類儲存。                            CS_BYTEALIGNCLIENT, CS_BYTEALIGNWINDOW:                位元組對齊標誌,據說沒什麼用了。                            CS_HREDRAW, CS_VREDRAW:                CS_HREDRAW 標誌表示當視窗的水平尺寸(寬度)改變的時候,重畫整個視窗。 CS_VREDRAW 則是在視窗垂直尺寸(高度)改變時重畫整個視窗。按鈕和捲軸都有這兩種風格。                                問題:什麼情況下需要指定這兩個標誌?存疑待考。            CS_NOCLOSE:                如果指定了這個標誌,則視窗上的關閉按鈕和系統功能表上的關閉命令失效。                            CS_DBLCLKS:                CS_DBLCLKS 標誌使視窗可以檢測到雙擊事件。視窗響應雙擊的細節如下:                    WM_LBUTTONDOWN                    WM_LBUTTONUP                    WM_LBUTTONDOWN                    WM_LBUTTONUP                其實就是兩個單擊,而如果指定了 CS_DBLCLKS 標誌,則系統想視窗依次發送如下訊息:                    WM_LBUTTONDOWN                    WM_LBUTTONUP                    WM_LBUTTONDBLCLK                    WM_LBUTTONUP                第二次的 WM_LBUTTONDOWN 被替換成了 WM_LBUTTONDBLCLK                 注意,在上述序列中間可能會插入其他的一條或一些訊息,所以這兩個訊息序列不一定是完全連續的。                                其實,在沒有指定 CS_DBLCLKS 標誌時,程式本身也可以檢測到雙擊事件的。參見 MSDN 裡 Dr.GUT 的 "Simulating Mouse Button Clicks" 文章,不過要有一些技巧. 一般的情況下,如果沒有指定 CS_DBLCLKS, 在視窗的訊息迴圈裡將不會得到 WM_LBUTTONDBLCLK 訊息。                所有的標準視窗控制項,對話方塊,桌面視窗類都預設擁有CS_DBLCLKS標誌。第三方控制項最好也加上此風格,以使其在對話方塊編輯器裡可以正常工作。                        CS_SAVEBITS:                菜單,對話方塊,下拉框都擁有 CS_SAVEBITS 標誌。當視窗使用這個標誌時,系統用位元影像形式儲存一份被視窗遮蓋(或灰隱)的螢幕圖象。首先,系統要求顯示驅動儲存圖象位元據,如果顯示驅動本身的儲存空間足夠,儲存操作成功, window 系統也可以使用這些儲存的位元據。如果不夠,系統將位元據在全域記憶體裡以位元影像的方式儲存,並且在 USE 模組局部堆裡為每個視窗分配空間,儲存一些交易資料(比如位元影像資料緩衝的大小)的結構。當程式使遮蓋螢幕的視窗消失時,系統可以很快的從記憶體裡恢複螢幕圖象。                CS_SAVEBITS 的效率本身是很難度量的。 CS_SAVEBITS 提高了“臨時”視窗比如菜單,對話方塊,下拉框的效能。但是,存貯位資訊的開銷也是很明顯的,尤其由系統代替顯示驅動儲存位資訊的時候,系統承擔了速度和儲存開銷。 使用 CS_SAVEBITS 的好處其實依賴於視窗遮蓋的地區發生了什麼事情,如果該地區相當複雜,需要重畫很多的效果,那麼,儲存該地區可能比重畫該地區要來的輕鬆,如果反之,該地區可以相當快速的重畫,或者在被遮蓋的時候還經常發生變化並且變化很顯著,儲存的方案反而影響了整體效能。                                問題:並不理解這部分的內容,可以結合 MSDN 來學習。        以上 style 標記都是可選欄位,視用途決定要使用哪些。如果視窗大小不改變、不響應雙擊操作、不被其他模組使用的話,設 style == 0 也是可以的。        hIcon 和 hIconSm:        視窗表徵圖, hIcon 是大表徵圖,展示在任務切換視窗(Alt+Tab), 大表徵圖模式工作列, explorer 中, hIconSm 是小表徵圖,展示在視窗標題列,小表徵圖模式工作列。        視窗表徵圖的尺寸必須符合一定大小,大小可以通過 GetSystemMetrics 函數指定 SM_CXICON, SM_CYICON, SM_CXSMICON, SM_CYSMICON 來擷取大小表徵圖的標準尺寸。        可以通過 WM_SETICON 訊息來修改表徵圖,通過 WM_GETICON 訊息來擷取表徵圖。        hCursor:        視窗預設滑鼠指標。當設定了該值,當滑鼠移入視窗地區時,系統將指標由系統預設圖案變成所設定的指標形狀。程式可以使用 LoadCursor 函數從標準系統指標庫(比如 IDC_ARROW)或使用者指定指標資源中擷取指標控制代碼。程式可以通過 SetCursor 函數隨時改變指標。如果 hCursor 的值未設定(設定為 NULL), 程式必須在滑鼠指標移入視窗時進行設定,否則將使用系統預設的滑鼠指標形狀。        lpszMenuName:        視窗預設主菜單。如果建立視窗時沒有顯式指定菜單資源,將使用在這裡指定的預設菜單。        可以使用 MAKEINTRESOURCE 宏將資源裡菜單的 識別碼轉換為連續字串賦值給該成員。        hbrBackground:        視窗背景顏色,類型為 HBRUSH, 可以將其賦值為一個畫刷控制代碼,或者顏色值。如果是顏色值的話,必須使用下列標準系統色彩之一:            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        使用顏色賦值時,必須加 1 並強制轉換為 HBRUSH 類型: wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);        如果 hbrBackground 成員設定為 NULL, 程式必須在響應 WM_PAINT 的時候負責畫背景。程式也可以響應 WM_ERASEBKGND 訊息,或根據調用 BeginPaint 函數時填充的 PAINTSTRUCT 結構裡的成員 fErase 的值類判斷是否需要重畫背景。        cbClsExtra, cbWndExtra:        cbClsExtra 指視窗類別額外記憶體大小,這部分記憶體被該類建立的所有視窗共用;        cbWndExtra 指視窗額外記憶體大小,該類建立的每個視窗各自擁有一段額外記憶體;        額外記憶體的大小最多隻能有 40 個位元組。如果不需要的話,必須設為 0, 以防止系統錯誤分配記憶體。        如果用類聲明並註冊一個對話方塊類型的視窗, cbWndExtra 的值必須設定為 DLGWINDOWEXTRA, 系統對話方塊管理器需要這麼多的額外資料對對話方塊進行管理。        4. 視窗類別的註冊和登出    註冊視窗類別使用 RegisterClass 或 RegisterClassEx 函數;        參數:  WNDCLASS 或 WNDCLASSEX 結構體;        傳回值:ATOM 類型的原子值,據說這個值是跟每個視窗類別一一對應的。        注意:  RegisterClass 和 RegisterClassEx 都有 ANSI 和 Unicode 兩種版本。如果使用 ANSI 版本如 RegisterClassA, 訊息中的文本資訊將是 ANSI 格式;如果使用 Unicode 版本如 RegisterClassExW, 訊息中的文本資訊將是 Unicode 格式。             登出視窗類別使用 UnregisterClass 函數;        參數:  lpszClassName, hInstance        傳回值:如果定位不到視窗類別,或者仍然有視窗沒有被銷毀,將失敗並返回非 0 值。        注意:  調用 UnRegisterClass 之前,必須先銷毀由該類建立出的所有視窗。                dll 被卸載後, 這個 dll 所註冊過的視窗類別並不會被登出。        5. 如何使用視窗類別,子類化、超類化是什麼    一旦註冊了一個類,一般來說除了使用該類建立視窗之外就沒有什麼需要做的事情了。當然,如果需要訪問該類資訊,子類化,或者超類化該類,介紹一些方法就是有用的。    類訪問函數:        GetClassInfoEx         擷取視窗類別資訊,輸入 hInstance 和 lpszClassName, 返回視窗類別對應的 WNDCLASSEX 結構體。 也可以輸入 atom        GetClassLong           從 WNDCLASSEX 結構體中擷取一個 long 類型數值, 輸入 hWnd, nIndex, 返回視窗對應視窗類別的 WNDCLASSEX 結構體中的某個欄位的資訊。 比如 GetClassLong(hWnd, GCL_STYLE) 可以擷取視窗類別的 styles        GetClassLongPtr        跟上面那個函數的功能類似,只是以指標方式返回結果。比如 GetClassLongPtr(hWnd, GCLP_WNDPROC) 可以擷取視窗類別的過程函數地址        GetClassName           擷取某視窗所屬視窗類別的視窗類別名, 輸入 hWnd, 返回 lpClassName        GetWindowLong          擷取某個視窗的指定資訊,以 long 類型返回,輸入 hWnd, nIndex, 返回視窗的某個欄位的資訊,比如 GetWindowLong(hWnd, GWL_STYLE) 可以擷取視窗的 styles        GetWindowLongPtr       跟上面那個函數的功能類似,只是以指標方式返回結果。比如 GetWindowLongPtr(hWnd, GWLP_WNDPROC) 可以擷取視窗對應視窗類別的視窗過程函數        SetClassLongPtr        替換視窗類別 WNDCLASSEX 的某個欄位        SetClassWord           跟上面函數功能類似,不過貌似是給 16 位系統用的        SetWindowLong          替換視窗的某個欄位,輸入 hWnd, nIndex, dwNewLong 可以替換指定欄位        SetWindowLongPtr       跟上面函數功能類似        子類化:        術語"子類化"(subclassing)描述的是用一個新的視窗過程代替原視窗過程。        術語"執行個體子類化"(即子類化單個視窗)是指使用 SetWindowLong 函數改變某一個視窗執行個體的視窗過程。        術語"全域子類化"(子類化整個視窗類別)則是指使用SetClassLong改變整個類的預設視窗過程函數。        在 32 位 windows  系統裡,可能難於子類化另一個進程裡的視窗或視窗類別,一般來說,子類化都是發生於同一個進程裡的("打破進程邊界"的相關的主題本文沒有涉及).        超類化:        術語"超類化"(Superclassing)指建立一個新的類,該類使用某個現存類的視窗過程,繼承該類的準系統,並可以在此基礎上進行擴充。        關於子類化和超類化的具體描述,請參閱 MSDN 裡 "safe subclasing in Win32" 一文        http://jdearden.gotdns.org/programming_windows_notebook/safe_subclassing_in_win32.html    參考連結:https://msdn.microsoft.com/en-us/library/ms633574(v=vs.85).aspxhttp://blog.csdn.net/vcbear/article/details/5988

 

Window 視窗類別

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.