今天在用到了一个求最大值的max函数的时候,需要include一个头文件,查找了一下msdn,上面说应该添加windef.h头文件,结果编译的时候却报错了。我也搞不懂是为什么啦,上网查一下,说包含windows.h就可以了。这样是调通了。查看了一下windows.h,的确在这个头文件里又是包含了windef.h的,查了一下windef.h,这里面有的的确确是定义了max的,有点糊涂了。但是网上说最好还是包含windows.h,不管了就先这样吧,以后慢慢再说吧。以下是查到的一些网上的资料(来自http://blog.csdn.net/fengningning/archive/2008/04/19/2306650.aspx),有时间再看。
关于有没有必要解构“这么一个”头文件,作为初学者,我实在没有太多的见解要发表。我在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