windows.h詳解

大囚長發表於2018-12-28

參考
http://blog.csdn.net/fengningning/article/details/2306650?locationNum=1&fps=1
windows.h解構

剛開頭的一段註釋是對該標頭檔案的描述:

/*++ 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

windows.h中的函式:
參考windows.h的函式

1.獲得當前工作目錄

char* _getcwd( char *buffer, int maxlen );  
// 功  能 : 獲得當前工作目錄.  
// 標頭檔案 : #include <direct.h>  
// 返回值 : 成功返回指向buffer的pointer  
//          失敗返回NULL,且設定errno為以下三個值之一:  
//            ENODEV 無該裝置  
//            ENOMEM 記憶體不夠  
//            ERANGE 結果超出範圍  
// 注  意 : 當第一個引數為 NULL 時, 第二個引數 maxlen 長度設定無效,且函式  
//          使用 malloc 分配足夠記憶體, 需要將函式返回值傳遞給 free() 函式來  
//          釋放記憶體. 當第一個引數不為 NULL 時,maxlen 指定長度不夠函式返回  
//          錯,設定errno為ERANGE  

2.修改當前工作目錄

int _chdir( const char *dirname );  
// 功  能 : 更改當前工作目錄.  
// 標頭檔案 : #include <direct.h>  
// 返回值 : 成功返回0  
//          失敗返回-1,且設定errno如下:  
//            ENOENT 該路徑不存在  

3.檔案遍歷(查詢)

long _findfirst( char *filespec, struct _finddata_t *fileinfo );  
// 功  能 : 提供與filespec指定入口泛式匹配的第一個檔案.通常後繼用_findnext函  
//          數後續使用來完成某泛式下的檔案遍歷.  
// 標頭檔案 : #include <io.h>  
// 參  數 : filespec - 目標檔案規範,可以包含萬用字元  
//          fileinfo - 檔案資訊buffer  
// 返回值 : 成功返回唯一的搜尋控制程式碼  
//          出錯返回-1,且設定errno為如下值:  
//            ENOENT 該泛式無法匹配  
//            EINVAL 無效檔名  
// 注  意 : _finddata_t 說明  

struct _finddata_t  
{  
    unsigned attrib;  
    time_t time_create;  
    time_t time_access;  
    time_t time_write;  
    _fsize_t size;  
    char name[_MAX_FNAME];  
};  
// 其中 :  
//  unsigned atrrib :  檔案屬性的儲存位置。它儲存一個unsigned單元,用於表示檔案的  
//                     屬性。檔案屬性是用位表示的,主要有以下一些:_A_ARCH(存檔)、  
//                     _A_HIDDEN(隱藏)、_A_NORMAL(正常)、_A_RDONLY(只讀)、  
//                     _A_SUBDIR(資料夾)、_A_SYSTEM(系統)。這些都是在<io.h>中  
//                     定義的巨集,可以直接使用,而本身的意義其實是一個無符號整型  
//                    (只不過這個整型應該是2的幾次冪,從而保證只有一位為1,而其他  
//                     位為0)。既然是位表示,那麼當一個檔案有多個屬性時,它往往是  
//                     通過位或的方式,來得到幾個屬性的綜合。例如只讀+隱藏+系統屬性,  
//                     應該為:_A_HIDDEN | _A_RDONLY |_A_SYSTEM 。  
// time_t time_create:這裡的time_t是一個變數型別,用來儲存檔案建立時間。  
// time_t time_access: 檔案最後一次被訪問的時間。  
// time_t time_write :  檔案最後一次被修改的時間。  
// _fsize_t size     :  檔案的大小。這裡的_fsize_t應該可以相當於unsigned整型,表示  
//                      檔案的位元組數。  
// char name[_MAX_FNAME]:檔案的檔名。這裡的_MAX_FNAME是一個常量巨集,它在<stdlib.h>頭  
//                        檔案中被定義,表示的是檔名的最大長度。  

int _findnext( long handle, struct _finddata_t *fileinfo );  
// 功  能 : 按照前面_findfirst中的泛式規則,查詢下一個符合該泛式的檔案,並以此為依據  
//          修改fileinfo中的值  
// 標頭檔案 : #include <io.h>  
// 參  數 : long handle - 搜尋控制程式碼(通常由緊靠其前的_findfirst()返回)  
//          fileinfo    - 檔案資訊buffer  
// 返回值 : 成功返回0  
//          出錯返回-1,且設定errno為如下值:  
//            ENOENT 沒有更多的符合該泛式的檔案  

int _findclose( long handle );  
// 功  能 : 關閉搜尋控制程式碼並釋放相應資源  
// 標頭檔案 : #include <io.h>  
// 參  數 : long handle - 搜尋控制程式碼(通常由緊靠其前的_findfirst()返回)  
// 返回值 : 成功返回0  
//          出錯返回-1,且設定errno為如下值:  
//            ENOENT 沒有更多的符合該泛式的檔案  

4.建立目錄

int _mkdir( const char *dirname );  
// 功  能 : 建立一個新目錄,目錄名為dirname.  
// 標頭檔案 : #include <direct.h>  
// 返回值 : 成功返回0  
//          失敗返回-1,且設定errno為以下三個值之一:  
//            EACCESS 許可權不允許  
//            EEXIST   該目錄已存在  
//            ENOENT   無該檔案或目錄  

5.刪除目錄

int _rmdir( const char *dirname );  
// 功  能 : 刪除名為dirname的目錄.  
// 標頭檔案 : #include <direct.h>  
// 返回值 : 成功返回0  
//          失敗返回-1,且設定errno為以下三個值之一:  
//            EACCESS   : 許可權不允許  
//            ENOTEMPTY : dirname不是資料夾;或者該資料夾不空;或  
//                        者dirname為當前工作資料夾;或者dirname  
//                        為當根資料夾;  
//            ENOENT    : 無該檔案或目錄  

6.其他操作

int _access( const char *path, int mode );  
// 功  能 : 測定檔案/目錄存取許可權.  
// 標頭檔案 : #include <io.h>  
// 參  數 : path - 檔案或者目錄  
//          mode - 許可權設定,其值如下:  
//                   00 Existence only   
//                   02 Write permission   
//                   04 Read permission   
//                   06 Read and write permission  

int _chdrive( int drive );  
// 功  能 : 更改當前工作驅動器.  
// 標頭檔案 : #include <direct.h>  
// 返回值 : 成功返回0  
//          失敗返回-1  
// 注  釋 : 引數說明  
//            drive =1 :  A盤  
//            drive =2 :  B盤  
//           drive =3 :  C盤 ...  

char* _getdcwd( int drive, char *buffer, int maxlen );  
// 功  能 : 獲得指定驅動器的當前工作路徑.  
// 標頭檔案 : #include <direct.h>  
// 返回值 : 成功返回指向buffer的pointer  
//          失敗返回NULL,且設定errno為以下三個值之一:  
//            ENODEV 無該裝置  
//            ENOMEM 記憶體不夠  
//            ERANGE 結果超出範圍  
// 注  意 : 當第一個引數為 NULL 時,該函式設定errno為ERANGE