windows.h分析

windows.h与windef.h

vc++ 2009-03-15 11:14:58 阅读566 评论0 字号:

今天在用到了一个求最大值的max函数的时候,需要include一个头文件,查找了一下msdn,上面说应该添加windef.h头文件,结果编译的时候却报错了。我也搞不懂是为什么啦,上网查一下,说包含windows.h就可以了。这样是调通了。查看了一下windows.h,的确在这个头文件里又是包含了windef.h的,查了一下windef.h,这里面有的的确确是定义了max的,有点糊涂了。但是网上说最好还是包含windows.h,不管了就先这样吧,以后慢慢再说吧。以下是查到的一些网上的资料(来自http://blog.youkuaiyun.com/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
<think>我们注意到用户的问题是关于在Linux环境下使用VSCode编译时遇到`windows.h`头文件的问题。由于`windows.h`是Windows特有的头文件,在Linux下不可用,因此我们需要指导用户如何解决在Linux下编译依赖于Windows API的代码的问题。 用户的问题可能有两种情况: 1. 用户试图在Linux下编译一个原本为Windows设计的程序,该程序使用了`windows.h`。 2. 用户的代码中错误地包含了`windows.h`,而实际上在Linux下并不需要。 根据引用[1][2][3]的内容,这些引用主要解决了在VSCode中关于标准头文件(如`stdio.h`)的路径配置问题。但用户的问题是关于`windows.h`,这是一个非标准的、特定于Windows的头文件,因此在Linux下无法直接使用。 解决方案思路: 1. 如果用户需要在Linux下运行原本为Windows编写的程序,可以考虑使用跨平台库(如Qt、SDL等)重写相关部分,或者使用Wine来运行Windows程序(但这不是编译,而是模拟运行)。 2. 如果用户是在开发跨平台程序,应该使用条件编译来区分Windows和Linux环境,避免在Linux中包含`windows.h`。 3. 如果用户确实需要在Linux下使用Windows API,可以考虑使用Wine的开发库(但通常不推荐,因为这样会限制程序在Linux上的原生性能)。 下面我们将详细说明如何通过条件编译来解决。 步骤: 1. 在代码中使用预处理器指令来检查操作系统,如果是Windows则包含`windows.h`,如果是Linux则包含相应的Linux头文件(如`unistd.h`等)或者实现相应的功能。 2. 在VSCode中配置正确的编译环境,确保在Linux下使用gcc或g++编译时能够正确识别。 示例代码: 假设用户需要使用多线程功能,在Windows下使用`CreateThread`,而在Linux下使用`pthread_create`。那么可以这样写: ```c #ifdef _WIN32 #include <windows.h> #else #include <pthread.h> #include <unistd.h> #endif void my_thread_function() { // 线程要执行的代码 } int main() { #ifdef _WIN32 HANDLE thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)my_thread_function, NULL, 0, NULL); WaitForSingleObject(thread, INFINITE); #else pthread_t thread; pthread_create(&thread, NULL, (void *(*)(void *))my_thread_function, NULL); pthread_join(thread, NULL); #endif return 0; } ``` 3. 在VSCode中配置任务(tasks.json)来编译这个程序。在Linux下,我们需要链接pthread库,因此编译命令需要加上`-lpthread`。 4. 关于VSCode的配置,可以参考引用[2]和[3]中关于配置编译器路径的方法,确保VSCode使用正确的gcc/g++编译器。同时,对于Linux下的头文件路径,VSCode应该能够自动识别,无需额外配置(除非有特殊位置的头文件)。 但是,如果用户只是不小心包含了`windows.h`,那么应该直接删除这行包含,并替换为Linux下相应的头文件或代码。 如果用户确实需要在Linux环境下开发与Windows API功能类似的程序,那么应该寻找Linux下的替代API。例如: - 文件操作:使用POSIX API(如`open`、`read`、`write`)或C标准库(`fopen`等) - 线程:使用pthread库 - 网络:使用socket API(在Linux和Windows下都可用,但包含的头文件不同:Linux下是`<sys/socket.h>`,Windows下是`<winsock2.h>`) 总结:在Linux下无法使用`windows.h`,因此需要修改代码以适应Linux环境。 相关问题: 1. 如何编写跨平台的C/C++程序? 2. Linux下有哪些常用的API可以替代Windows API? 3. 如何在VSCode中配置Linux下的C/C++开发环境? 4. 如何使用条件编译来处理平台相关的代码? 5. 在Linux下编译C程序时如何链接特定的库(如pthread)?</think>在Linux环境下使用VSCode编译涉及`windows.h`头文件的代码时,会遇到平台兼容性问题。以下是系统化的解决方案和替代方案: ### 原因分析 `windows.h`是**Windows专属头文件**,包含Windows API的定义(如窗口管理、进程控制等)。Linux系统没有此头文件,直接包含会导致编译错误: ```bash fatal error: windows.h: No such file or directory ``` --- ### 解决方案 #### 1️⃣ 代码层改造(推荐) 使用**条件编译**区分平台,并替换为Linux等效API: ```c #ifdef _WIN32 #include <windows.h> // Windows专用 #else #include <unistd.h> // Linux/POSIX替代 #include <pthread.h> // 线程替代 #include <sys/time.h> // 时间函数替代 #endif int main() { #ifdef _WIN32 Sleep(1000); // Windows睡眠 #else sleep(1); // Linux睡眠(秒) usleep(1000000); // 微秒级睡眠 #endif return 0; } ``` #### 2️⃣ 跨平台库替代 | Windows API | Linux替代方案 | 跨平台库 | |----------------------|----------------------------|---------------------| | 线程 (`CreateThread`) | `pthread_create` | C++11 `<thread>` | | 文件操作 (`CreateFile`) | `open`/`fopen` | C++17 `<filesystem>`| | 网络 (`Winsock`) | BSD sockets | Boost.Asio | | GUI (`WinAPI`) | GTK/Qt | Qt/WxWidgets | #### 3️⃣ Wine兼容层(不推荐生产环境) 通过Wine模拟Windows环境: ```bash # 安装Wine开发包 sudo apt install wine64-development-tools # 编译时链接Wine库 gcc -mwindows your_code.c -lwine ``` ⚠️ 注意:性能较低且不支持所有API,仅适合临时测试。 --- ### VSCode配置验证 确保编译器正确识别Linux头文件路径(参考引用[2]): 1. 打开`c_cpp_properties.json`(Ctrl+Shift+P → `C/C++: Edit Configurations`) 2. 检查Linux路径配置: ```json { "configurations": [ { "name": "Linux", "includePath": [ "/usr/include/**" // Linux系统头文件路径 ], "compilerPath": "/usr/bin/gcc" // 确认指向Linux的gcc } ] } ``` --- ### 关键注意事项 1. **平台检测宏**: - `_WIN32`:检测Windows(32/64位均定义) - `__linux__`:检测Linux系统 2. **编译命令**: ```bash gcc -D__linux__ your_code.c -lpthread # 显式定义Linux宏并链接库 ``` 3. **调试技巧**: ```bash gcc -E -dM - < /dev/null | grep -i linux # 验证编译器预定义宏 # 输出应有 #define __linux__ 1 ``` --- ### 相关问题 1. 如何在Linux中实现Windows API的等效功能(如`CreateProcess`或`RegisterClassEx`)? 2. 使用跨平台库(如Qt)开发GUI时如何避免平台相关代码? 3. Wine的工作原理是什么?它在哪些场景下适用? 4. 如何为跨平台C/C++项目配置CMake以自动处理平台差异? 5. 在VSCode中如何快速切换Windows/Linux的开发环境配置? > 引用说明:Linux下VSCode的编译器路径配置需指向本地GCC,避免包含Windows专属路径[^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值