SystemParametersInfo 关于VERIFY 出错

今天程序调试中遇到了一个问题,在这里记录下,学习!

 

 

SystemParametersInfo 关于VERIFY 出错

源代码:

int i,j,numitems,maxlength;
CString string,newstring;
 CSize t;
 CFont m_fontMenu;
 LOGFONT m_lf;
 
 ZeroMemory ((PVOID) &m_lf,sizeof (LOGFONT));
 NONCLIENTMETRICS nm;
 nm.cbSize = sizeof (NONCLIENTMETRICS) - sizeof(nm.iPaddedBorderWidth);
 VERIFY (SystemParametersInfo (SPI_GETNONCLIENTMETRICS,nm.cbSize,&nm,0));
 m_lf =  nm.lfMenuFont;
 m_fontMenu.CreateFontIndirect (&m_lf);

错误原因:

这段代码本来没错,错在平台上.

nm.cbSize = sizeof (NONCLIENTMETRICS) - sizeof(nm.iPaddedBorderWidth);这个是高版本.通常所说的VISTA版本
nm.cbSize = sizeof (NONCLIENTMETRICS) ; 这个是低版本,通常所说的0x0501

看似是系统问题.其实这2个代码都能在XP上运行,也都能在VS2008/VS2010上编译,

低于这里所说的系统与VS编译器2个版本的,就会出问题.了解即可.

解决办法:

(一)如果你想用nm.cbSize = sizeof (NONCLIENTMETRICS) ; 这个是低版本,通常所说的0x0501,就在stdafx.h中

#pragma once

#ifndef _SECURE_ATL
#define _SECURE_ATL 1
#endif

#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN            // 从 Windows 头中排除极少使用的资料
#endif

这个语句的后面加上以下2句

#ifndef WINVER    // 允许使用特定于 Windows XP 或更高版本的功能。
#define WINVER 0x0501  // 将此值更改为相应的值,以适用于 Windows 的其他版本。
#endif

#ifndef _WIN32_WINNT  // 允许使用特定于 Windows XP 或更高版本的功能。
#define _WIN32_WINNT 0x0501 // 将此值更改为相应的值,以适用于 Windows 的其他版本。
#endif  

(2)如果你想用nm.cbSize = sizeof (NONCLIENTMETRICS) - sizeof(nm.iPaddedBorderWidth);这个是高版本.通常所说的VISTA版本
就什么也不加,如果你的stdaftx.h里有加的话,就把删除掉就好了.采用默认的(系统与VS编译器版本上面已提过了)

以下是随便写的一个获取当前系统版本的接口,可以创建一个MFC应用程序,直接拷贝过去就能用

/
   // TODO: 在此添加控件通知处理程序代码
 //CPoint point;
 //GetCursorPos(&point);
 /注意系统版本检测函数,专门写成一个接口
 OSVERSIONINFO osvi;
 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

 //获取系统的版本信息。
 ::GetVersionEx(&osvi);
 bool bIsWindowsXPorLater = (osvi.dwMajorVersion > 5) ||
  ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) );

 CString str;
 str.Format("%d=%d=%d=%d=%d",osvi.dwBuildNumber,osvi.dwMajorVersion,osvi.dwMinorVersion,osvi.dwOSVersionInfoSize,osvi.dwOSVersionInfoSize,osvi.dwPlatformId);
    AfxMessageBox(str);
 //显示当前的版本。
 if (bIsWindowsXPorLater)
 {
  OutputDebugString(_T("Windows XP或更新版本!/r/n"));
 }
 else
 {
  OutputDebugString(_T("Windows XP以前版本!/r/n"));              
 }    

备注1:另外期间找了一些很不错的资料,也顺便拷贝过来供参考了

SystemParametersInfo ( SPI_GETNONCLIENTMETRICS...)在VC 2008 里不能正常工作

 // create fonts
 NONCLIENTMETRICS info; 
 info.cbSize = sizeof(info);
 ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0);

 if (!_fontHorzMenu.CreateFontIndirect(&info.lfMenuFont))
  return FALSE;

DWORD dw = GetLastError();

上面这段代码在VC 6.0 中工作正常,但是在VC 2008 里 SystemParametersInfo 返回 0,而 GetLastError 返回的却是 0简单订正如下:info.cbSize = sizeof(info) - sizeof(info.iPaddedBorderWidth);  实际是由于从 Vista 和 Windows Server 2008 开始 NONCLIENTMETRICS 在最后增加了iPaddedBorderWidth字段,如果你的程序打算同时支持 vista 或 XP ,Windows 2000, Windows Server 2003,那么应该先调用 GetVersionEx 检测Windows版本,然后决定是否需要减去 sizeof (ncms.iPaddedBorderWidth)参考:  http://msdn.microsoft.com/en-us/library/ms724506(VS.85).aspx转自: http://blog.csdn.net/royer/archive/2008/12/23/3591046.aspx备注2:另外对为什么一个没加,而另一个加了#define WINVER 0x0501就好了.这里也顺便说下原因:以下是从网上摘抄来的.VC中使用高版本API的方法——undeclared identifier引发的血案2010-10-09 20:32

在VC6中使用GetListBoxInfo这个API函数的时候编译提示: 'GetListBoxInfo' : undeclared identifier

他已经将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

对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。

时光荏苒,日月如梭,现在已经是2010年了,主流的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版本呢?详见这篇文章,我就不详细讲了:

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

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

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


 

你可能感兴趣的:(软件)