上一节咱们了解了进程、入口函数和进程实例句柄等内容,在进入进程的命令行学习前,有一个全局变量初始化问题须要测试一波。本节的学习目标以下:
1.测试C/C++运行库启动函数初始化哪些全局变量
2.进程的命令行
3.进程的环境变量
4.进程的当前驱动器和目录
5.判断系统版本ios
咱们知道C/C++运行库的启动函数会作一些过后再调用咱们的入口函数,而入口函数的参数都是在调用前就初始化好了的。那么我就产生了一个疑问,全局变量随入口函数的不一样(四种入口函数,分别是main、wmain、wWinMain、WinMain)都分别初始化了哪些全局变量?我作出了下面的测试:
(1)在CUI程序下测试Unicode字符集和多字节字符集两种状况的全局变量的初始化:编程
#include<windows.h> #include<tchar.h> #include<iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { //测试两次:第一次是在Unicode环境下,第二次是在多字节字符集环境下,注意输出的不一样。 //测试_environ有无被初始化成有用的值 char** p1 = _environ; if (p1 != NULL) { while (*p1) { cout << *p1 << endl; p1++; } cout << "---------------------------上面的是_environ输出的值---------------------------------" << endl; } //测试_wenviron有无被初始化成有用的值 wchar_t** p2 = _wenviron; if (p2 != NULL) { while (*p2) { wcout << *p2 << endl; p2++; } cout << "--------------------------上面的是_wenviron输出的值--------------------------" << endl; } //测试__argv有无被初始化成有用的值 char** p3= __argv; if (p3 != NULL) { while (*p3) { cout << *p3 << endl; p3++; } cout << "-------------------------上面的是__argv输出的值----------------------------" << endl; } //测试__wargv有无被初始化成有用的值 wchar_t** p4 = __wargv; if (p4 != NULL) { while (*p4) { wcout << *p4 << endl; p4++; } cout << "-------------------------上面的是__wargv输出的值----------------------------" << endl; } system("pause"); return 0; }
测试结果:输出结果太长很差截图,这里只给出总结,运行结果能够本身运行查看。若是你写的主函数是_tmain,那么其中_environ和_wenviron全局变量,在Unicode环境下,_environ和_wenviron全局变量都被初始化成有用的值了。而在多字节字符集下,_environ全局变量被初始化成有用的值,_wenviron全局变量才被置NULL。明显,和书中P69页表格的描述有差别
(2)在GUI程序下测试Unicode字符集和多字节字符集两种状况的全局变量的初始化:
1.很明显,下面的测试结果和在wmain函数测试状况相同。
2.能够看出,下面的测试结果和在main函数测试状况也相同。
(3)大总结:
对于书中P69页的全局变量初始化表的描述我产生了质疑。若是你写的主函数是_tmain,那么其中_environ和_wenviron全局变量,在Unicode环境下,_environ和_wenviron全局变量都被初始化成有用的值了。而在多字节字符集下,_environ全局变量被初始化成有用的值,_wenviron全局变量才被置NULL。简单来讲就是不管_UNICODE有无被定义,_environ都会被初始化成有用的值,而_wenviron就受字符集影响,跟书产生了歧义。而wargv和argv就是符合书本的状况定义。若是你写的主函数是_tWinMain,那么其中_environ和_wenviron全局变量,在Unicode环境下,_environ和_wenviron全局变量都被初始化成有用的值了。而在多字节字符集下,_environ全局变量被初始化成有用的值,_wenviron全局变量才被置NULL。而wargv和argv就是符合书本的状况定义。注意,若是Windows编程,不使用_tmain和_tWinMain函数,而是使用main或wmain,那么上述的总结不必定成立,但因为兼顾两种字符集,建议之后写的入口函数就写_tmain和_tWinMain函数。windows
(1)若是是运行CUI应用程序,在C/C++运行库启动函数执行时,就已经初始化好全局变量(包括命令行参数argc、argv或wargv。若是在Unicode字符集下,初始化了argc、argv;若是在多字符集下,初始化了argc、__wargv。)而后调用入口点函数_tmain,将参数argc、argv或wargv传入_tmain函数。
如今对_tmain函数的参数进行测试:数组
#include<windows.h> #include<tchar.h> #include<iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { /* 有两种方式能够输入命令行参数: 1.属性->配置属性->调试->命令参数:例如:wo ai ni 2.在可执行文件目录下打开命令行窗口(cmd),输入文件名+命令行参数:例如:ConsoleApplication9 wo ai ni 但有一点须要注意,就是字符集问题,当项目字符集是Unicode字符集,那么在C++利用wcout输出命令行。当项目字符集是多字节字符集,那么在C++利用cout输出命令行。 注意,不论经过以上两种方式输入的命令行参数都会在C/C++运行库启动函数中被初始化全局变量argc、__argv、__wargv。 因此传入_tmain函数的argv参数也是对应字符集编码的字符串。例如:若是在Unicode下,argv数组内的元素就是宽字符串,若是在多字节字符集下,argv数组内的元素就是ANSI字符串。 注意第一种方式和第二种方式在输出上的区别,第一种输出的第一个文件名字符串,这个字符串也包括路径。而第二种输出只有命令行参数,由于就算没有填写命令行参数也会输出文件名,那个文件名 只是起到运行这个程序的象征。 */ for (int i = 0; i < argc; i++) { //cout只能输出ANSI字符和字符串,要想输出宽字符可使用wcout。 wcout << argv[i] << endl; } system("pause"); return 0; }
(2)若是是运行CUI应用程序,在C/C++运行库启动函数执行时,会调用Windows函数GetCommandLine来获取进程的完整命令行(文件名+命令行参数,其中文件名也就是绝对路径)而后启动函数进行忽略可执行文件的名称,包括路径,接着将指向命令行剩余部分的一个指针传给WinMain的pszCmdLine参数。下面给出函数的签名:安全
LPTSTR WINAPI GetCommandLine(void);
下面举个例子:
能够看出cmdLine包含绝对路径的文件名称和命令行参数。而pszCmdLine参数只有命令行参数,由于在启动函数处理中已经忽略了文件名了。
(3)咱们也能够利用CommandLinetoArgvW函数将GetCommandLineW函数获取的完整命令行分解成单独的标记。
该函数原型以下:编辑器
LPWSTR* CommandLinetoArgvW(LPCWSTR,int*); 参数1是指向一个命令行字符串,一般利用GetCommandLineW获取。 参数2是获取命令行实参的个数。 返回的字符串数组所使用的内存,用LocalFree来释放!
如下是MSDN的示例代码:是在CUI程序下CommandLinetoArgvW函数的使用ide
#include<windows.h> #include<tchar.h> #include<iostream> using namespace std; int _tmain(int argc, _TCHAR* argv[]) { LPWSTR *szArglist;//用于 int nArgs; int i; /* CommandLineToArgvW函数只有Unicode版本的,因此参数1也必须使用Unicode版本的GetCommandLineW来获取完整的命令行 参数2是存储完整命令行中一共有多少个命令行参数,包括文件名参数。 CommandLineToArgvW函数返回的是一个Unicode字符串指针数组的地址。 这个函数将参数1完整命令行分解成单独的标记。 */ LPTSTR cmdLine; cmdLine = GetCommandLine(); printf("%ws\n", cmdLine);//这个是输出完整命令行 szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); if (NULL == szArglist) { wprintf(L"CommandLineToArgvW failed\n"); return 0; } else for (i = 0; i<nArgs; i++) printf("%d: %ws\n", i, szArglist[i]);//这个是输出分解后放到字符串数组中的内容 LocalFree(szArglist); system("pause"); return 0; }
在CUI程序下,入口点函数的argv函数就已经分解好了命令行参数,其实这个函数更大的用处是在GUI程序中,例以下面代码的使用:函数
#include<windows.h> #include<tchar.h> int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE, PTSTR pszCmdLine, int nCmdShow) { LPWSTR *szArglist;//用于 int nArgs; LPTSTR cmdLine; cmdLine = GetCommandLineW(); szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs); LocalFree(szArglist); return 0; }
测试结果以下:学习
在咱们熟知的Windows系统里,一直有环境变量这一说,咱们都还知道环境变量可在Windows界面里的高级系统设置里的环境变量中获取或设置。但其实,环境变量真正是存储在注册表里的,每一个Windows系统的注册表编辑器都在C:\Windows\regedit.exe,咱们知道在可视化界面下(高级系统设置中打开)有两种环境变量,分别是系统变量和用户变量。而系统变量和用户变量分别在注册表编辑器下的两个路径(系统变量路径:HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Session Manager\Environment;用户变量路径:HKEY_CURRENT_USER\Environment)。下面放个注册表编辑器的示意图:
好了,言归正传。其实每一个进程被建立后都会有一个与它关联的环境块,也就是在进程地址空间内分配的一块内存,内存块包含的字符串大概长这样:测试
=::=::\ ... VarName1=VarValue1\0 VarName2=VarValue2\0 VarName3=VarValue3\0 VarNameX=VarValueX\0 \0
咱们要注意的是等号左边的VarName一、VarName2等都是环境变量的名称,而等号右边的VarValue一、VarValue2等都是环境变量的值。还有一个更重要的一点就是每行环境变量的赋值最后都有个‘\0’,这是字符串结束符,后边GetEnvironmentStrings函数遍历完整的环境变量字符串时有用。
咱们有两种方式来获取完整的环境块,第一种方式是调用GetEnvironmentStrings函数获取完整的环境变量(还有GetEnvironmentVariable函数获取的是单个指定环境变量名的值,下面会有使用案例)获得的完整环境块的格式和前面描述的同样;第二种方式是CUI程序专用的,就是经过入口函数所接收的TCHAR *envp[]参数来实现。不一样于GetEnvironmentStrings返回的值,GetEnvironmentStrings返回的是完整的环境块,而envp是一个字符串指针数组,每一个指针都指向一个不一样的环境变量(其定义采用常规的“名称=值”的格式),在数组最后一个元素是一个NULL指针,表明这是数组的末尾,那么咱们就能够经过这个NULL指针做为遍历的终止处,咱们须要注意的是以等号开头的那些无效字符串在咱们接收到envp以前就已经被移除了,因此没必要进行处理只要获取数组元素便可。
(1)下面先讲GetEnvironmentStrings函数的使用案例:
这里先放上等会要用到的两个函数的函数签名。
1.GetEnvironmentStrings函数用于获取全部环境变量字符串:
LPTCH WINAPI GetEnvironmentStrings(void); 返回值:成功时,返回指向保存环境变量的缓冲区;失败时,返回值为NULL。
2.FreeEnvironmentStrings函数用来释放由GetEnvironmentStrings返回的内存块:
BOOL WINAPI FreeEnvironmentStrings( __in LPTCH lpszEnvironmentBlock ); 返回值:成功时,返回非零值;失败时,返回零值,可调用GetLastError()查看进一步错误消息。
#include <windows.h> #include <tchar.h> #include <stdio.h> #include<strsafe.h> int _tmain() { LPTSTR lpszVariable; LPTCH lpvEnv;//LPTCH就是WCHAR *数据类型,指向宽字符的指针变量 size_t iTarget; //调用GetEnvironmentStrings函数获取完整的环境变量内存块,并让lpvEnv指向这个内存块 lpvEnv = GetEnvironmentStrings(); //若是获取的环境块为空,则该函数调用失败,并获取错误代码 if (lpvEnv == NULL) { _tprintf(TEXT("GetEnvironmentStrings failed(%d)\n"), GetLastError()); return 0; } //lpvEnv指向的环境变量字符串是以NULL分隔的,即'\0'分隔,能够回去看前面我展现的环境字符串的大概格式。而字符串最后是以NULL结尾的 lpszVariable = (LPTSTR)lpvEnv; while (*lpszVariable) { _tprintf(TEXT("%s\n"), lpszVariable); StringCchLength(lpszVariable, 1000, &iTarget);//PATH的值太长,我设1000为最大容许字符数 lpszVariable += iTarget + 1;//移动指针,访问下一环境变量的值 } //若是GetEnvironmentStrings函数返回的内存块不用了,记得要释放掉 FreeEnvironmentStrings(lpvEnv); system("pause"); return 1; }
运行结果以下:
(2)下面是GetEnvironmentVariable函数的使用案例:
这里先放上GetEnvironmentVariable函数签名。
1.GetEnvironmentVariable函数用于获取指定的环境变量:
DWORD WINAPI GetEnvironmentVariable( __in_opt LPCTSTR lpName, //环境变量名 __out_opt LPTSTR lpBuffer, //指向保存环境变量值的缓冲区 __in DWORD nSize //缓冲区大小(字符数) ); 返回值:成功时,返回真实的环境变量值大小,不包括null结束符;若是lpBuffer大小不足,则返回值是实际所需的字符数大小,lpBuffer内容就未被赋值;失败时,返回0;若是指定的环境变量找不到,GetLastError()返回ERROR_ENVVAR_NOT_FOUND。
#include <windows.h> #include <tchar.h> #include <stdio.h> #include<strsafe.h> int _tmain() { TCHAR szBuffer[1000]; DWORD dwResult = GetEnvironmentVariable(TEXT("PATH"), szBuffer, 1000); if (dwResult != 0) { _tprintf(TEXT("PATH=%s"), szBuffer); } else { _tprintf(TEXT("function call falid!")); } system("pause"); return 1; }
运行结果以下:
(2)下面是SetEnvironmentVariable函数的使用案例:
这里先放上SetEnvironmentVariable函数签名,后面使用案例有几个注意点须要重视。
1.SetEnvironmentVariable函数用于设置指定的环境变量:
BOOL WINAPI SetEnvironmentVariable( __in LPCTSTR lpName, //环境变量名,当该值不存在且lpValue不为NULL时,将建立一个新的环境变量 __in_opt LPCTSTR lpValue //环境变量值 ); 返回值: 成功时,返回非零值; 失败时,返回零值,调用GetLastError()查看具体的错误信息。 该函数对系统环境变量以及其余进程的环境变量不起做用!
在写测试程序前,我先在个人电脑->属性->高级系统设置->环境变量->用户变量处添加一个自定义的环境变量MyPath,环境变量值为woaini。呃。。。值不是重点,大概长下面那样。
好了,准备工做作好了,如今重点要关闭VS,从新开VS再运行测试代码(先思考为何,若是不重开VS会有什么现象,后面讲注意点有解释重开VS的缘由),如今放测试代码:
#include <windows.h> #include <tchar.h> #include <stdio.h> int _tmain(int argc,TCHAR *argv[],TCHAR *envp[]) { TCHAR szBuffer[1000];//用于存储获取的环境变量的值 DWORD dwResult1 = GetEnvironmentVariable(TEXT("MyPath"), szBuffer, 1000);//先获取咱们前面已经设置好的MyPath环境变量的值,若是没错应该是woaini,但若是你测试时获取不到,该函数返回0,那么就要看看后面我讲的注意点了哦。 if (dwResult1 != 0) { _tprintf(TEXT("MyPath=%s\n"), szBuffer); } else { _tprintf(TEXT("function call falid!\n")); } SetEnvironmentVariable(TEXT("MyPath"), TEXT("I love you"));//这里为我新建的MyPath环境变量从新修改值为I love you,注意,其实这只是修改当前进程的环境块,而未影响系统或用户的环境块 DWORD dwResult2 = GetEnvironmentVariable(TEXT("MyPath"), szBuffer, 1000);//这里从新获取如下修改后的MyPath环境变量的值 if (dwResult2 != 0) { _tprintf(TEXT("MyPath=%s\n"), szBuffer); } else { _tprintf(TEXT("function call falid!\n")); } system("pause"); return 0; }
若是执行步骤没错,那么运行结果是下面这样的:
好了,注意点有如下几点:
1.为何要重开VS,GetEnvironmentVariable函数才能正确获取前面咱们新建的环境变量MyPath?这是由于咱们以前讲过每一个进程在建立时就被分配了一个环境块,而这个环境块就是Windows系统赋予的,那么咱们能够猜想,当运行VS,就已经在内部存好了咱们将要分配的环境块内容,而咱们是VS运行后再新建环境变量MyPath,那么VS保存的这块内容还没更新呢,因此函数固然获取不到,咱们只能重开VS了。这也只是个人猜想,是为了更好理解GetEnvironmentVariable函数,若有其余见解的,能够留言探究哦。
2.GetEnvironmentVariable函数对系统环境变量以及其余进程的环境变量不起做用,由于建立了一个进程,就已经为进程分配好环境块了,咱们经过GetEnvironmentVariable函数添加、修改或删除环境块内容,也只是添加、修改或删除进程的环境块,而非Windows系统或用户的环境块。
(3)下面是CUI程序入口函数TCHAR *envp[]参数的使用案例:
这里就不本身写代码了,直接放上书本P76页的示例代码(修改过)。
#include <windows.h> #include <tchar.h> #include <stdio.h> int _tmain(int argc,TCHAR *argv[],TCHAR *envp[]) { int current = 0;//用于环境变量计数 PTSTR *pElement = (PTSTR *)envp;//建立新的指针指向CUI程序的envp数组 PTSTR pCurrent = NULL;//用于遍历envp数组元素的指针 while (pElement != NULL) { //取数组的元素 pCurrent = (PTSTR)(*pElement); //前面说过数组末尾是NULL指针,因此当遍历到NULL则将pElement置NULL,接着就跳出循环了 if (pCurrent == NULL) { pElement = NULL; } else { //打印遍历到的环境变量 _tprintf(TEXT("[%u] %s\r\n"), current, pCurrent); current++;//计数+1 pElement++;//指向下一个数组元素 } } system("pause"); return 0; }
运行结果以下:
(4)下面是ExpandEnvironmentStrings函数的使用案例:
经过前面注册表的了解,咱们能够细心发现,有些环境变量的值含有两个百分号(%)之间的字符串,这种字符串叫作可替换字符串,顾名思义,咱们能够经过函数ExpandEnvironmentStrings函数替换掉可替换字符串。也能够发现,这种可替换字符串只有在注册表才能看到,而在个人电脑->属性->高级系统设置->环境变量或经过其余方式获取整个完整的环境变量都看不到可替换字符串这种形式。下面,我先放上ExpandEnvirnmentStrings函数的函数签名:
DWORD WINAPI ExpandEnvironmentStrings( _In_ LPCTSTR lpSrc, _Out_opt_ LPTSTR lpDst, _In_ DWORD nSize ); 参数1:一个包含可替换字符串的字符串地址(也叫扩展字符串),例如:TEXT("PATH=%PATH%") 参数2:用于接收扩展字符串的一个缓冲区的地址 参数3:这个缓冲区的最大大小,用字符数来表示。 返回值:保存扩展字符串所需的缓冲区的大小,用字符数表示,若参数3小于这个返回值,%%变量就不会扩展,而是被替换为空字符串,因此通常要调用两次ExpandEnvironmentStrings函数。
#include <windows.h> #include <tchar.h> #include <stdio.h> int _tmain(int argc,TCHAR *argv[],TCHAR *envp[]) { //第一次调用ExpandEnvironmentStrings是为了获取保存扩展字符串所需的缓冲区大小,因此函数参数2能够为NULL,参数3为0 DWORD chValue = ExpandEnvironmentStrings(TEXT("USERPROFILE='%USERPROFILE%'"), NULL, 0); PTSTR pszBuffer = new TCHAR[chValue];//动态建立chValue大小的缓冲区,最后记得释放掉动态建立的空间 chValue = ExpandEnvironmentStrings(TEXT("USERPROFILE='%USERPROFILE%'"), pszBuffer, chValue);//此次调用才是真正获取替换后的字符串 _tprintf(TEXT("%s\r\n%d"), pszBuffer,chValue);//打印扩展字符串的缓冲区和字符数目 delete[]pszBuffer;//释放动态建立的空间 system("pause"); return 0; }
运行结果以下:
有一些Windows函数的调用须要提供路径,例如:CreateFile函数打开一个文件(未指定完整路径名,只有一个文件名),那么该函数就会在当前驱动器(例如:C、D、E磁盘)的当前目录查找文件和目录。系统在内部跟踪记录着一个进程的当前驱动器和目录,咱们能够获取进程的当前驱动器和目录,也能够修改进程的当前驱动器和目录。
下面给出分别获取和设置当前驱动器和目录的函数签名:
1.GetCurrentDirectory函数获取进程当前目录:
DWORD WINAPI GetCurrentDirectory( _In_ DWORD nBufferLength, _Out_ LPTSTR lpBuffer ); nBufferLength:lpBuffer指针指向内存块的大小(单位TCHAR); lpBuffer:接收当前路径的内存块。
2.SetCurrentDirectory函数设置进程当前目录
BOOL WINAPI SetCurrentDirectory( _In_ LPCTSTR lpPathName ); lpPathName:须要被设置的目录路径
3.GetFullPathName函数获取指定文件的当前路径:
DWORD WINAPI GetFullPathName( __in LPCTSTR lpFileName, __in DWORD nBufferLength, __out LPTSTR lpBuffer, __out LPTSTR *lpFilePart ); lpFileName:文件名 nBufferLength:获取全路径的内存大小(TCHAR) lpBuffer:内存指针 lpFilePart:文件名最后一个元素,在lpBuffer中的位置。 注意:这个函数,只是将当前路径,粘贴到你给的文件上,其余什么也没有作。
下面,我给出使用案例来领会这些函数的使用:
#include <windows.h> #include <tchar.h> #include <stdio.h> int _tmain(int argc,TCHAR *argv[],TCHAR *envp[]) { TCHAR szPath[MAX_PATH]; GetCurrentDirectory(MAX_PATH, szPath);//获取进程当前路径 _tprintf(L"%s\n", szPath); TCHAR *str = L"D:\\360Downloads\\";//设置的当前路径 SetCurrentDirectory(str); //设置文件的当前路径,若是指定的str参数在电脑中存在这个路径,那么就设置成功,不然设置无效,仍是采用前一个有效的当前进程目录 GetCurrentDirectory(MAX_PATH, szPath); _tprintf(L"%s\n", szPath); TCHAR *str1 = L"D:\\ddsdf\\";//设置的当前路径 SetCurrentDirectory(str1); //设置文件的当前路径,若是指定的str参数在电脑中存在这个路径,那么就设置成功,不然设置无效,仍是采用前一个有效的当前进程目录 GetCurrentDirectory(MAX_PATH, szPath); _tprintf(L"%s\n", szPath);//由于"D:\\ddsdf\\"路径在我电脑里不存在,因此SetCurrentDirectory函数设置失败了 GetFullPathName(L"wxf1", MAX_PATH, szPath, NULL); //这个函数只是将进程当前路径(szPath)粘贴到你给的文件名(wxf1)上,其余什么也没有作,不作检查 _tprintf(L"%s\n", szPath); system("pause"); return 0; }
运行结果以下:
经过上面的测试,咱们能够得出如下几点:
1.GetCurrentDirectory函数是获取进程的当前目录,而函数参数1通常用MAX_PATH(宏定义为260)就很安全了,由于这是目录名称或文件名称得最大字符数了。
2.SetCurrentDirectory函数设置进程的当前目录,而若是该函数参数指定的路径在本电脑中不存在,那么就设置无效,仍是采用前一个有效的当前进程目录。
3.GetFullPathName函数只是将进程当前路径(szPath)粘贴到你给的文件名(wxf1)上,其余什么也没有作,不作检查 。
4.为了更好理解GetCurrentDirectory函数获取进程的当前目录这一功能,你能够将上面代码生成的可执行文件放到桌面,再运行,那么进程的当前目录就改变啦
在实际应用中,咱们所开发的应用程序须要判断用户所运行的Windows系统的版本。简单来讲就是在应用程序运行前先判断系统版本,若用户系统不支持咱们的应用程序则提示用户系统版本不支持,接着退出程序。Windows提供了两个判断系统版本的函数,分别是GetVersion函数和GetVersionEx函数,因为GetVersion函数在获取的版本信息,其中Windows版本号的顺序颠倒了。明知有错,那么咱们就只用GetVersionEx函数好了,并且这个函数更好使用。咱们还能够经过VerifyVersionInfo函数确认当前系统版本是否知足应用程序版本需求。
如今放上GetVersionEx函数的函数签名:
1.GetVersionEx函数获取当前系统版本信息
BOOL WINAPI GetVersionEx( __inout LPOSVERSIONINFO lpVersionInfo ); LpVersionInfo:这个参数执行OSVERSIONINFO或者OSVERSIONINFOEX这个结构体。 OSVERSIONIINFOEX结构体是OSVERSIONINFO结构体的扩展,因此这个函数能够传递这两个参数的任意一个,前提条件是,必须设置结构体的大小,让GetVersionEx函数可以知道你传递的是那个结构体。
2.OSVERSIONINFOEX结构体
typedef struct _OSVERSIONINFOEX { DWORD dwOSVersionInfoSize;//结构体大小 DWORD dwMajorVersion;//主机系统的主版本号 DWORD dwMinorVersion;//主机系统的次版本号 DWORD dwBuildNumber;//当前系统的构建版本号 DWORD dwPlatformId;//平台ID TCHAR szCSDVersion[128];//额外的文本 WORD wServicePackMajor;//ServicePack的主版本号 WORD wServicePackMinor;//ServicePack的次版本号 WORD wSuiteMask;//suite掩码 BYTE wProductType;//产品类型 BYTE wReserved;//保留字段 } OSVERSIONINFOEX, *POSVERSIONINFOEX, *LPOSVERSIONINFOEX;
3.OSVERSIONINFO结构体
typedef struct _OSVERSIONINFO { DWORD dwOSVersionInfoSize;//结构体大小 DWORD dwMajorVersion;//主机系统的主版本号 DWORD dwMinorVersion;//主机系统的次版本号 DWORD dwBuildNumber;//当前系统的构建版本号 DWORD dwPlatformId;//平台ID TCHAR szCSDVersion[128];//额外的文本 } OSVERSIONINFO;
下面对GetVersionEx和OSVERSIONINFO结构体的测试案例:
#include <windows.h> #include <stdio.h> void main() { //OSVERSIONINFOEX osvi;//若是设置的结构体是OSVERSIONINFOEX,那么下面设置结构体大小要修改成sizeof(OSVERSIONINFOEX),还有ZeroMemory的函数参数也要相应修改 OSVERSIONINFO osvi; BOOL bIsWindowsXPorLater; //ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); ZeroMemory(&osvi, sizeof(OSVERSIONINFO)); //osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); //GetVersionEx((LPOSVERSIONINFO)&osvi); GetVersionEx(&osvi); bIsWindowsXPorLater = ( (osvi.dwMajorVersion > 5) || ( (osvi.dwMajorVersion == 5) && (osvi.dwMinorVersion >= 1) )); if(bIsWindowsXPorLater) printf("The system meets the requirements.\n"); else printf("The system does not meet the requirements.\n"); system("pause"); }
至于全部系统版本的信息,咱们能够经过在MSDN上,搜索OSVERSIONINFOEX结构体下方有系统版本号总结,官网连接为:https://msdn.microsoft.com/en-us/library/windows/desktop/ms724832(v=vs.85).aspx
就像下面的那样:
须要注意的是:在windows8.1或windows10上的应用程序经过函数GetVersionEx获取的版本信息,没有对应windows8.1或windows10的版本信息,那么将返回windows8的版本值(6.2)。
4.VerifyVersionInfo函数测试主机系统的版本符不符合应用程序要求的版本
VerifyVersionInfo函数的函数签名:
BOOL WINAPI VerifyVersionInfo( __in LPOSVERSIONINFOEX lpVersionInfo, __in DWORD dwTypeMask, __in DWORDLONG dwlConditionMask ); 参数1是应用程序本身定义的版本要求 参数2是应用程序的比较测试项 参数3是测试条件,这个测试条件是DWORDLONG类型的数据,由VER_SET_CONDITION宏来为这个DWORDLONG类型的数据添加测试条件
5.VER_SET_CONDITION宏是为了设置测试条件,例如比较什么,怎么比较。每比较一个成员就要调用一次VER_SET_CONDITION宏。
ULONGLONG VER_SET_CONDITION( ULONGLONG dwlConditionMask, DWORD dwTypeBitMask, BYTE dwConditionMask ); 参数1是测试条件,DWORDLONG类型的变量,DWORDLONG类型是DWORDLONG类型的重命名。 参数2是主机系统的比较测试项 参数3是比较方式
下面对VerifyVersionInfo函数的测试案例:
#include <windows.h> #include <tchar.h> #pragma warning(disable: 4996) int _tmain(int argc, TCHAR *argv[]) { //建立等会须要比较的版本信息,本应用程序要求的版本信息 OSVERSIONINFOEX infoEx; ZeroMemory(&infoEx, sizeof(OSVERSIONINFOEX));//清零 infoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);//设置结构体大小 infoEx.dwMajorVersion = 6;//设置待会比较的主版本信息 infoEx.dwMinorVersion = 2;//设置待会比较的次版本信息 infoEx.wServicePackMajor = 0; infoEx.wServicePackMinor = 0; DWORDLONG dwlConditionMask = 0;// VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);//用等号的方式比较VER_MAJORVERSION,并放入dwlConditionMask中,下面相似 VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); DWORD dwTypeMaask = VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; if (VerifyVersionInfo(&infoEx, dwTypeMaask, dwlConditionMask)) { _tprintf(L"this system is sufficient!\n"); } else { if (ERROR_OLD_WIN_VERSION == GetLastError()) { _tprintf(L"this system is old system!\n"); } else { _tprintf(L"this function is error!\n"); } } system("pause"); return 0; }
在我Win10的系统下运行此程序,运行结果以下,很明显个人主机系统符合应用程序的要求的系统版本: