VC中使用高版本API的方法——undeclared identifier引发的问题

来源:互联网 发布:新手水彩白夜知乎 编辑:程序博客网 时间:2024/04/29 15:32

以下内容来自如下链接:

http://www.easewe.com/Article/document/879.htm

http://www.rupeng.com/forum/thread-3214-1-1.html

 

有同学在如鹏论坛提问,在VC6中使用GetListBoxInfo这个API函数的时候编译提示: 'GetListBoxInfo' : undeclared identifier
详见:http://www.rupeng.com/forum/thread-3214-1-1.html

他已经将GetListBoxInfo所在的头文件WinUser.h直接或者间接的包含进来了,打开WinUser.h文件,看到GetListBoxInfo就活生生的躺在那里呢,为什么还是报“'GetListBoxInfo' : undeclared identifier”呢?难道VC眼瞎了吗???
一开始就解决GetListBoxInfo的这个问题可能比较麻烦,咱们先来看另外一个API函数:LockWorkStation,他就在GetListBoxInfo函数的定义下面:

复制内容到剪贴板
代码:
WINUSERAPI  BOOL WINAPI  LockWorkStation(VOID);

这个函数用来锁定操作系统。LockWorkStation安静的的躺在WinUser.h中,按理来说应该能轻松的调用,因此我在文件中调用LockWorkStation却无情的报告了“LockWorkStation undeclared identifier”。为什么呢?
我们仔细看LockWorkStation函数的定义,看更大范围的:

复制内容到剪贴板
代码:
#if(_WIN32_WINNT >= 0x0500)
WINUSERAPI
BOOL
WINAPI
LockWorkStation(
    VOID);
#endif /* _WIN32_WINNT >= 0x0500 */

对C/C++比较熟悉的同学应该知道“#if”是条件编译的意思,也就是说被“#if”、“#endif”包围的代码只有满足“#if”的条件的时候才会被编译,这个if判断是在编译的时候执行的,而不是运行的时候执行的。

_WIN32_WINNT是一个宏定义,它表示Windows的版本,它有很多取值,取值列表如下:

复制内容到剪贴板
代码:
Windows XP   _WIN32_WINNT>=0x0501     
Windows 2000   _WIN32_WINNT>=0x0500     
Windows NT 4.0  _WIN32_WINNT>=0x0400     

因此“#if(_WIN32_WINNT >= 0x0500)”这句话的意思是只有Windows的版本大于Windows 2000 的时候LockWorkStation函数的定义才会起作用,才会被编译。
为什么要根据操作系统的版本决定函数的定义是否编译呢?道理很简单,因为有的API函数是高版本的Windows下才提供的,低版本的Windows没有那个API函数,所以需要根据操作系统进行判断。

不对呀!我的操作系统是WindowsXP,确实比Windows 2000高呀,为什么不能编译呢?
原因就是“_WIN32_WINNT”并不是像大家想象的那样真的会代表当前编译程序电脑的操作系统的版本,它需要程序员去指定,当然VC也给了“_WIN32_WINNT”一个默认值,不过这个默认值是VC从微软那发布的时候微软定义的:

复制内容到剪贴板
代码:
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0400
#endif

VC6是1998年发布的,那时候Windows98刚发布,微软当时肯定认为大部分人还是Windows   NT   4.0操作系统,为了“主流”,微软就把VC6中_WIN32_WINNT宏的默认值设置成了Windows NT 4.0的0x0400

时光荏苒,日月如梭,现在已经是2009年了,主流的Windows桌面操作系统已经是WindowsXP了,还有相当一部分人用上了Vista甚至Windows7,那么“ _WIN32_WINNT 0x0400”这个默认值已经Out了!
那么咱们怎么修改_WIN32_WINNT的默认值呢?打开StdAfx.h文件,在文件最开始加入“#define _WIN32_WINNT 0x0501”就ok了,也就是设置为WindowsXP。再编译LockWorkStation函数就通过了!!!

晕呀,怎么GetListBoxInfo函数还是不能编译通过???还是“undeclared identifier”,难道LockWorkStation是“这个可以有”,而GetListBoxInfo是“这个真没有”吗?搞技术的不信邪,慢慢琢磨,是不是还是有其他的宏定义控制的条件编译呢?顺着GetListBoxInfo的定义向上搜“#if”,终于发现这么一句“#if(WINVER >= 0x0500)”,WINVER是什么宏呢?也是表示Windows的版本,可取值列表如下:

复制内容到剪贴板
代码:
Windows 95、98 and Windows NT   4.0     WINVER=0x0400         
Windows 98 and Windows 2000         WINVER=0x0500     
Windows 2000                             WINVER=0x0500
Windows xp                             WINVER=0x0501  

估计WINVER还是和_WIN32_WINNT一样的问题,因此我们同样打开StdAfx.h文件,在文件最开始加入“#define WINVER  0x0501”就ok了,也就是设置为WindowsXP。再编译GetListBoxInfo函数就通过了!!!
有同学问,怎么微软还弄了_WIN32_WINNT、WINVER两个宏来表示Windows版本呢?详见这篇文章,我就不详细讲了:
http://blogs.msdn.com/oldnewthing/archive/2007/04/11/2079137.aspx

引用:
The WINVER symbol is the earliest one. That's the symbol that 16-bit Windows used to control the versioning of its header files, and its use carried forward into the 32-bit header files, presumably from the people who did the initial conversion of the header files to 32-bit and who grew up with the WINVER symbol. This symbol is still used a lot in the header files that can trace their origins to 16-bit Windows, such as winuser.h, wingdi.h, and mmsystem.h.
The _WIN32_WINNT symbol came next. I'm not sure where it came from, but from its name it probably was invented by the Windows NT team in order to allow them to block off sections of the header file that are available only in the Windows NT implementation of Win32. Don't forget that in the early days, there was also Win32s, a subset of Win32 that could run on 16-bit Windows 3.1. The single WINVER symbol wasn't enough to specify exactly what you wanted to be compatible with. For example, a function available only in Windows NT 3.1 would be guarded with #if _WIN32_WINNT >= 0x030A so that programs that wanted to run on Win32s could set _WIN32_WINNT to zero and keep that function off-limits.
Similarly, both Windows 95 and Windows NT 4 identified themselves as Windows major version 4, so the WINVER symbol was insufficient to distinguish them. Functions that existed in Windows NT 4 but not in Window 95 were therefore guarded with _WIN32_WINNT.
On the other hand, there were also functions that were first introduced in Windows 95 and did not exist in the original version of Windows NT 4. The _WIN32_WINDOWS symbol let you specify that you wanted access to stuff that was new for Windows 95 and which would also be ported to Windows NT 4 and future versions of Windows NT.

我承认我很卑鄙,在这等着你们呢。我相信80%的同学跳过了刚才我贴的这段英文,“全是英文,密密麻麻,不看了”,就像上面这段英文像梵文一样你看不懂。呵呵。学计算机的要锻炼自己阅读英文资料的能力,所以请跳过这段文章的同学现在回去读一读,“浪子回头金不换”!每个阅读的同学都把它翻译出来,最好落实到纸面上。
除了_WIN32_WINNT、WINVER这两个宏,还有一个重要的宏_WIN32_IE,显而易见,它表示IE的版本,可选值如下:

复制内容到剪贴板
代码:
Internet Explorer 3.0   _WIN32_IE=0x0300      
Internet Explorer 4.0   _WIN32_IE=0x0400      
Internet Explorer 5.0   _WIN32_IE=0x0500   
Internet Explorer 6.0  _WIN32_IE=0x0600   

有部分函数也是依赖于IE的版本进行条件编译的,因此最好也把_WIN32_IE设置为0x0600。
总结一下,为了避免麻烦,最好每次新建项目的时候把下面几个宏定义加到StdAfx.h中,这样就免除了后顾之忧:

复制内容到剪贴板
代码:
#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0600  
#define WINVER  0x0501

我也考虑更新“如鹏版对话框程序生成向导”(Rupeng DialogBased Application wizard),把上面的宏定义自动加到StdAfx.h中,在“如鹏版对话框程序生成向导”升级之前同学们先手动加吧。

有同学问:

引用:

你给的这些宏的数值可以更改吗?按照我上面搜集的资料,vista系统的应该是:
   _WIN32_WINNT>=0x0600
    WINVER>=0x0600

为何你给的那些数还要小呢?

当然可以改,因为我的电脑是xp,所以我设的是0x0600。其实VC6中设成0x0500以上的任意值已经没区别,因为翻遍了VC6中所有的头文件没有发现依赖于0x0501以上的函数,很简单,VC6第一个版本发布的的时候是1998年,而VC6配套的SDK头文件最新的版本发布的时候还只有Windows2000,那时候根本没有WindowsXP、Vista这些东西,所以哪怕 _WIN32_WINNT设成0x0900都行,只不过没意义,VC6下设成0x0500就足够了。

原创粉丝点击