解構windows.h

 
  
    
      
         關於有沒有必要解構“這麼一個”頭文件,作爲初學者,我實在沒有太多的見解要發表。我在Microsoft Visual Studio .NET 2005中打開的這個只有260多行(實際佔用的行數可能更少)的文件,是所有(我知道用“所有”這個詞似乎不大嚴謹,但據我所知——是的)Windows程序必須先包含的文件,既然是“必須”的,也就是無法規避的,那麼又有什麼理由阻止我去解構它呢?
 
剛開頭的一段註釋是對該頭文件的描述:
/*++ BUILD Version: 0001    Increment this if a change has global effects
 
Copyright (c) 1985-1997, Microsoft Corporation
 
Module Name:
 
 
    windows.h
 
Abstract:
 
    Master include file for Windows applications.
 
--*/
這個文件似乎沒有經歷過重大的變化,至今仍是第一個版本。“Increment this if a change has global effects”,意思大概是“如果某個變化有全局性的影響,就將該變化增添至這個頭文件”。話本身是沒有歧義的,可轉而一想,換做是我,我肯定不會擅作主張來修改這個文件的——道理很簡單,這樣做會導致代碼移植更加複雜,還有其他一些不利因素。那麼,這句話大概是微軟開發人員對“自己人”說的罷。而摘要部分說:“Master include file for windows applications”,就不用多做解釋了。
 
#ifndef _WINDOWS_
#define _WINDOWS_
這種宏定義應該是最常見的了,一個作用是防止重複包含。
 
#ifndef WINVER
#define WINVER 0x0400
#else   
#if defined(_WIN32_WINNT) && (WINVER < 0x0400) && (_WIN32_WINNT > 0x0400)
#error WINVER setting conflicts with _WIN32_WINNT setting
#endif
#endif
WINVER這個宏與Windows版本相關,也就是該宏變量取不同值時對應不同的Windows版本,Platform SDK文檔中的相關說明如下:
Windows Server 2003                                WINVER>=0x0502
Windows XP                                            WINVER>=0x0501
Windows 2000                                          WINVER>=0x0500
Windows NT 4.0                                       WINVER>=0x0400
Windows Me                                            WINVER>=0x0500
Windows 98                                             WINVER>=0x0410
Windows 95                                             WINVER>=0x0400
(如何查看自己Windows操作系統的版本號呢?下面提供其中一種方法:調出任務管理器->幫助—>關於任務管理器,在Windows XP中如上操作可以查得版本號是5.1,而在Windows server 2003中是5.2,Windows 2000中則是5.0。哈哈,確實如此,和Platform SDK文檔中的描述是一致的!)_WIN32_WINNT這個宏其實也代表版本號,因此如果你同時定義了這個宏,卻又與WINVER的定義不一致,那麼,編譯器就提示錯誤“WINVER setting conflicts with _WIN32_WINNT”。
 
#if(WINVER >= 0x0500)
#pragma message ("")
#pragma message ("NOTE: WINVER has been defined as 0x0500 or greater which enables")
#pragma message ("Windows NT 5.0 and Windows 98 features. When these headers were released,")
#pragma message ("Windows NT 5.0 beta 1 and Windows 98 beta 2.1 were the current versions.")
#pragma message ("")
#pragma message ("For this release when WINVER is defined as 0x0500 or greater, you can only")
#pragma message ("build beta or test applications. To build a retail application,")
#pragma message ("set WINVER to 0x0400 or visit http://www.microsoft.com/msdn/sdk")
#pragma message ("to see if retail Windows NT 5.0 or Windows 98 headers are available.")
#pragma message ("")
#pragma message ("See the SDK release notes for more information.")
#pragma message ("")
#endif
如果定義的WINVER>=0x0500,即要求最低的Windows版本是Windows NT 5.0(Windows 2000)和Windows 98,此時編譯器在進行編譯時會提示以#pragma message定義的一系列信息作爲提示。
 
#ifndef _INC_WINDOWS
#define _INC_WINDOWS
 
#if defined (_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif
 
/* If defined, the following flags inhibit definition
 *     of the indicated items.
 *
 * NOGDICAPMASKS     - CC_*, LC_*, PC_*, CP_*, TC_*, RC_
 * NOVIRTUALKEYCODES - VK_*
 * NOWINMESSAGES     - WM_*, EM_*, LB_*, CB_*
 *  NOWINSTYLES       - WS_*, CS_*, ES_*, LBS_*, SBS_*, CBS_*
 * NOSYSMETRICS      - SM_*
 * NOMENUS           - MF_*
 * NOICONS           - IDI_*
 * NOKEYSTATES       - MK_*
 * NOSYSCOMMANDS     - SC_*
 * NORASTEROPS       - Binary and Tertiary raster ops
 * NOSHOWWINDOW      - SW_*
 * OEMRESOURCE       - OEM Resource values
 * NOATOM            - Atom Manager routines
 * NOCLIPBOARD       - Clipboard routines
 * NOCOLOR           - Screen colors
 * NOCTLMGR          - Control and Dialog routines
 * NODRAWTEXT        - DrawText() and DT_*
 * NOGDI             - All GDI defines and routines
 * NOKERNEL          - All KERNEL defines and routines
 * NOUSER            - All USER defines and routines
 * NONLS             - All NLS defines and routines
 * NOMB              - MB_* and MessageBox()
 * NOMEMMGR          - GMEM_*, LMEM_*, GHND, LHND, associated routines
 * NOMETAFILE        - typedef METAFILEPICT
 * NOMINMAX          - Macros min(a,b) and max(a,b)
 * NOMSG             - typedef MSG and associated routines
 * NOOPENFILE        - OpenFile(), OemToAnsi, AnsiToOem, and OF_*
 * NOSCROLL          - SB_* and scrolling routines
 * NOSERVICE         - All Service Controller routines, SERVICE_ equates, etc.
 * NOSOUND           - Sound driver routines
 * NOTEXTMETRIC      - typedef TEXTMETRIC and associated routines
 * NOWH              - SetWindowsHook and WH_*
 * NOWINOFFSETS      - GWL_*, GCL_*, associated routines
 * NOCOMM            - COMM driver routines
 * NOKANJI           - Kanji support stuff.
 * NOHELP            - Help engine interface.
 * NOPROFILER        - Profiler interface.
 * NODEFERWINDOWPOS - DeferWindowPos routines
 * NOMCX             - Modem Configuration Extensions
 */
接下來的所有內容都是用來定義另外一些需要包含的頭文件的,當然也包含了其他信息。
_MSC_VER這個宏定義了編譯器的版本,相關信息如下:
C   Compiler   version   6.0                                               600  
        C/C++   compiler   version   7.0                                          700  
        Visual   C++,   Windows,   version   1.0                                800  
        Visual   C++,   32-bit,   version   1.0                            800  
        Visual   C++,   Windows,   version   2.0                          900  
        Visual   C++,   32-bit,   version   2.x                               900  
        Visual   C++,   32-bit,   version   4.0                            1000  
        Visual   C++,   32-bit,   version   5.0                            1100  
        Visual   C++,   32-bit,   version   6.0                             1200  
這個宏是必須定義的#pragma once指示編譯器在編譯過程中最多包含一次該頭文件。
有意思的是下面一大段註釋,說明了當定義了_MSC_VER這個宏,並且它的版本號>=1020,那麼接下來所列出的一系列標誌是不能夠被定義的。
 
#if defined(RC_INVOKED) && !defined(NOWINRES)
 
#include <winresrc.h>
 
#else
 
#if defined(RC_INVOKED)
/* Turn off a bunch of stuff to ensure that RC files compile OK. */
#define NOATOM
#define NOGDI
#define NOGDICAPMASKS
#define NOMETAFILE
#define NOMINMAX
#define NOMSG
#define NOOPENFILE
#define NORASTEROPS
#define NOSCROLL
#define NOSOUND
#define NOSYSMETRICS
#define NOTEXTMETRIC
#define NOWH
#define NOCOMM
#define NOKANJI
#define NOCRYPT
#define NOMCX
#endif
 
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_PPC_) && !defined(_ALPHA_) && !defined(_MIPS_) && !defined(_X86_) && defined(_M_IX86)
#define _X86_
#endif
 
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_PPC_) && !defined(_ALPHA_) && !defined(_X86_) && !defined(_MIPS_) && defined(_M_MRX000)
#define _MIPS_
#endif
 
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_PPC_) && !defined(_ALPHA_) && !defined(_X86_) && !defined(_MIPS_) && defined(_M_ALPHA)
#define _ALPHA_
#endif
 
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_PPC_) && !defined(_ALPHA_) && !defined(_X86_) && !defined(_MIPS_) && defined(_M_PPC)
#define _PPC_
#endif
 
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_PPC_) && !defined(_ALPHA_) && !defined(_X86_) && !defined(_MIPS_) && defined(_M_M68K)
#define _68K_
#endif
 
#if !defined(_68K_) && !defined(_MPPC_) && !defined(_PPC_) && !defined(_ALPHA_) && !defined(_X86_) && !defined(_MIPS_) && defined(_M_MPPC)
#define _MPPC_
#endif
 
#ifndef _MAC
#if defined(_68K_) || defined(_MPPC_)
#define _MAC
#endif
#endif
 
#ifndef RC_INVOKED
#if     ( _MSC_VER >= 800 )
#pragma warning(disable:4001)
#pragma warning(disable:4201)
#pragma warning(disable:4214)
#pragma warning(disable:4514)
#endif
#include <excpt.h>
#include <stdarg.h>
#endif /* RC_INVOKED */
這一段有些長,但因爲是一體的,所以放在一起分析是必然的。
宏RC_INVOKED似乎是這一段的核心,就從它開始。RC(資源編譯器)定義了這個宏,使得你能夠有選擇地編譯資源頭文件的不同部分。爲什麼會有這種需要呢?當你考慮選擇用C編譯器還是用RC編譯器來編譯的資源頭文件的時候,你就必須直面這個問題。因爲RC所支持的定義語句只是C編譯器的一個子集,因此,如果選擇用RC來編譯,那麼就該注意不能用RC所不支持的語法來編寫資源頭文件。
NO_WINRES這個宏實在winresrc.h這個頭文件裏定義的,而winresrc.h這個文件裏的內容實在是很少:
#ifndef _WINRESRC_
#define _WINRESRC_
 
#include <winuser.rh>
#include <commctrl.rh>
#include <dde.rh>
#include <winnt.rh>
#include <dlgs.h>
#include <winver.h>
 
#endif
呵呵,看過之後,不難理解了。
接下來的一些定義#if !defined(_68K_) && !defined(_MPPC_)……是和平臺相關的,由於大多數人(包括我在內)可能只會在一種硬件平臺下如X86,所以這些定義大可不必太過計較的。
如果沒有定義RC_INVOKED並且_MSC_VER(編譯器的版本號)>=800的話就禁用幾個與編譯器版本相關的幾個警告信息。如果沒有定義RC_INVOKED這個宏,還要包含excpt.h和stdarg.h這兩個頭文件(excpt .h是一個未文檔化的頭文件,包含了關於SEH(結構化異常處理)的一些定義;stdarg.h爲具有多個參數的函數定義了ANSI類型的宏),那麼,在這種情況下爲什麼要包含這兩個頭文件呢?Platform SDK中是這樣解釋的:“RC不支持一些ANSI C型的預定義宏(如__DATE__, __FILE__, __LINE__, __STDC__, __TIME__, __TIMESTAMP__等)”,而excpt.h和stdarg.h這兩個頭文件確實定義了一些ANSI C型的宏,因此,爲避免編譯出錯,只有在不使用RC的情況下(也就是不定義RC_INVOKED這個宏)才包含這些頭文件。
 
#include <windef.h>
#include <winbase.h>
#include <wingdi.h>
#include <winuser.h>
這幾個是windows.h中包含的幾個最重要的和最基本的頭文件:
windef.h——基本型態定義
winbase.h——Kernel函數
wingdi.h——圖形設備接口函數
winuser.h——使用者接口函數
 
#ifdef _MAC
DECLARE_HANDLE(HKEY);
typedef HKEY *PHKEY;
#endif
(小插曲:_MAC是MFC爲蘋果機應用程序開發而使用的兼容性預定義符號。默認情況下MFC的目標平臺是Windows。  
  微軟曾經和蘋果有過合作(蘋果爲此被死忠痛罵,即使當時蘋果的經濟情況並不理想……Sun好像就沒這麼倒黴……),MFC兼容蘋果機就是當時合作的結果之一,但是現在似乎沒有下文了……  作者:謝謝網友提供)
待續…………………………………………………………………………………………………
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章