上一節咱們瞭解了進程、入口函數和進程實例句柄等內容,在進入進程的命令行學習前,有一個全局變量初始化問題須要測試一波。本節的學習目標以下:
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的系統下運行此程序,運行結果以下,很明顯個人主機系統符合應用程序的要求的系統版本: