1. 程式人生 > >Windows核心編程之核心總結(第四章 進程(二))(2018.6.17)

Windows核心編程之核心總結(第四章 進程(二))(2018.6.17)

函數的參數 設置 函數詳解 可執行文件 一次 HA AC 關聯 原型

學習目標

上一節我們了解了進程、入口函數和進程實例句柄等內容,在進入進程的命令行學習前,有一個全局變量初始化問題需要測試一波。本節的學習目標如下:
1.測試C/C++運行庫啟動函數初始化哪些全局變量
2.進程的命令行
3.進程的環境變量
4.進程的當前驅動器和目錄
5.判斷系統版本
6.創建進程(CreateProcess函數詳解)

測試啟動函數初始化哪些全局變量

我們知道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函數。

進程的命令行

(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函數的使用

#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

我們要註意的是等號左邊的VarName1、VarName2等都是環境變量的名稱,而等號右邊的VarValue1、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核心編程之核心總結(第四章 進程(二))(2018.6.17)