2010-11-17 304 views
9

CreateProcess文檔狀態(我的粗體強調):Win32 CreateProcess:何時需要CREATE_UNICODE_ENVIRONMENT *?

lpEnvironment [中,可選]

[...]如果環境塊指向lpEnvironment包含Unicode字符,請確保dwCreationFlags包含CREATE_UNICODE_ENVIRONMENT。 如果此參數爲NULL並且父進程的環境塊包含Unicode字符,則還必須確保dwCreationFlags包含CREATE_UNICODE_ENVIRONMENT。

MSDN是錯誤的,並誇大了旗幟的含義或是真正的要求?

我見過的代碼從來沒有設置標誌,似乎工作,但我偏執狂的一部分想要遵守MSDN所說的100%。說,我不知道你真的可以遵循MSDN的規則,而不會走到極端。

不必設置(或不設置)CREATE_UNICODE_ENVIRONMENT時lpEnvironment爲NULL令我可笑:

  1. 如果我沒有通過環境塊,然後CreateProcess的,必須取得該塊本身。在那種情況下,它比我更瞭解該塊的類型。

  2. 如何知道該塊是否實際上包含Unicode字符?

    我是否希望獲得該塊並檢查其當前代碼頁之外的字符? (我認爲這就是MSDN在這裏所說的「Unicode字符」。)

    如果我確實需要獲取env-block,那麼我不妨將它傳遞給lpEnvionment而不是NULL,那麼爲什麼要允許NULL?

    必須獲取並檢查env塊對於CreateProcess的每個調用者來說似乎都是一個瘋狂的要求;它肯定是API本身應該處理的東西。

  3. 當它說「父進程」是否意味着我的進程,這即將成爲一個新的父母,或這是否意味着我的進程的父母?我對MSDN的初步閱讀讓我覺得我不得不告訴我的進程開始的CreateProcess調用是否已經通過了ANSI或Unicode環境塊,但事實並非如此。

    我假設,在基於NT的操作系統上,所有進程都有一個Unicode env塊,如果需要在進程創建時從ANSI轉換而來,並且該進程不會掛在任何數據塊傳遞給CreateProcess原樣。 (也許這整個事情是從操作系統本身不是Unicode的Win9x時代剩下的?儘管如此,即使如此,我看不到應用程序代碼如何使決定比操作系統本身更好,也不是爲什麼它應該被期望。)

  4. 以及永遠不會設置標誌的代碼,我看到如果UNICODE是在編譯時定義的代碼,它總是設置它。當需求是在運行時的env塊中的內容以及代碼可能在DLL加載到外部進程中時,這是沒有意義的。

    env塊是進程範圍的,因此UNICODE在編譯時定義似乎不相關。

  5. 如果這只是我調用CreateProcessA或CreateProcessW的問題,那麼標記在塊爲NULL時應該是隱式的,因此也沒有任何意義。

在我自己的代碼,我決定來避免這個問題,總是獲得環境塊(通過GetEnvironmentStringsW)的Unicode的副本,總是把它傳遞給CreateProcess,總是設置CREATE_UNICODE_ENVIRONMENT。根據MSDN的說法,這是我看到100%正確的唯一方法。

雖然我在做什麼是多餘的。 CreateProcess不能那麼笨,可以嗎?

另一方面,這是我們正在討論的CreateProcess。它不是最好設計的API,並有一堆其他陷阱(我的頭頂上):

  1. 失敗,如果參數字符串是常量,因爲它在原地修改它。
  2. 使第一個參數可選,從而請人們忘記引用第二個參數中的exe路徑。
  3. 即使在第一個參數中明確給出了第二個參數,也需要正確引用的exe路徑。

也許這是不正確的假設它智能行爲,或可能照顧家務呼叫者...

我不知道是不是從我自己刪除偏執垃圾代碼或將其添加到我看到的所有其他代碼。哎呀。 :-)

添加18 /十一月/ 2010:

這當然好像標誌是無關緊要的,當ENV塊通過到Windows 7.請參閱下面我的測試是NULL,至少在Windows 2000中。

很明顯,這並不能說明這個標誌在所有未來的操作系統中都是不相關的,但我真的不知道它會如何。

說我們有祖父母這創造家長是要創建子:

  • 如果操作系統總是存儲母公司爲Unicode的ENV塊 - 家長的創作,如果祖父母在從ANSI已經將它轉換傳遞一個ANSI塊 - 然後CreateProcess會錯誤地注意到當Parent傳遞一個NULL塊時該標誌。 CreateProcess必須知道Child將繼承的塊總是Unicode。

  • 或者,操作系統可能會存儲父母的env-block,就像它來自祖父母一樣。 (這看起來不太可能,但有可能。)在這種情況下,Parent無法檢測到祖父母傳遞了哪​​種類型的塊。同樣,CreateProcess必須知道塊的類型並忽略該標誌。

這是一個測試我今天早上寫的啓動方式不同的子過程,使子進程報告的ENV-VAR(只是「OS」爲了簡潔變量):

wchar_t *szApp = L"C:\\Windows\\system32\\cmd.exe"; 
wchar_t *szArgs = L"\"C:\\Windows\\system32\\cmd.exe\" /C set OS"; 
STARTUPINFOW si = {0}; 
si.cb = sizeof(si); 
PROCESS_INFORMATION pi = {0}; 

// For brevity, this leaks the env-blocks and thread/process handles and doesn't check for errors. 
// Must compile as non-Unicode project, else GetEnvironmentStringsA is hidden by WinBase.h 
for(int i = 0; i < 3; ++i) 
{ 
    const char *t = (i==0) ? "no env" : (i==1) ? "unicode env" : "ansi env"; 
    void *env = (i==0) ? NULL : (i==1) ? (void*)GetEnvironmentStringsW() : (void*)GetEnvironmentStringsA(); 
    printf("--- %s/unicode flag ---\n", t, i); 
    ::CreateProcessW(szApp, szArgs, NULL, NULL, FALSE, CREATE_UNICODE_ENVIRONMENT, env, NULL, &si, &pi); 
    ::WaitForSingleObject(pi.hProcess, INFINITE); 
    printf("\n--- %s/ansi flag ---\n", t, i); 
    ::CreateProcessW(szApp, szArgs, NULL, NULL, FALSE, 0, env, NULL, &si, &pi); 
    ::WaitForSingleObject(pi.hProcess, INFINITE); 
    printf("\n"); 
} 

此輸出:

--- no env/unicode flag --- 
OS=Windows_NT 

--- no env/ansi flag --- 
OS=Windows_NT 

--- unicode env/unicode flag --- 
OS=Windows_NT 

--- unicode env/ansi flag --- 

--- ansi env/unicode flag --- 

--- ansi env/ansi flag --- 
OS=Windows_NT 

當env-block爲NULL時,標誌在那裏並不重要。

當它不是NULL時,標誌確實很重要,因爲CreateProcess需要被告知void *的後面(但這很明顯,問題純粹是關於NULL的情況)。

任何人都可以想到任何情況下,當env塊爲NULL時標誌可能會出現問題嗎?在這種情況下,應用程序如何可能比操作系統本身更好地瞭解標誌的正確值?

回答

4

請注意,在聲明CreateProcess函數lpEnvironment參數聲明爲LPVOID

這是什麼意思?這意味着您可以使用Ansi/Unicode版本的CreateProcess函數,並以任意組合傳遞Ansi/Unicode版本環境塊。特別是您可以使用Unicode版本的CreateProcess並將其傳遞給Ansi環境塊,反之亦然。

,使得在設定CREATE_UNICODE_ENVIRONMENT需要當且僅當你實際使用unicode環境塊,因爲沒有其他常規方式(除了一些醜陋的啓發)操作系統可以實現它的統一。

現在關於你的問題:

  1. 如果不明確地傳遞環境塊 - 新創建的進程最初有相同的環境變量,它的創造者。除非您需要對新創建的流程進行一些額外配置,否則無需重新配置。

  2. 如果您將環境塊傳遞到新創建的進程 - 您必須手動構建它或從某處獲取它。無論如何,你需要必須知道它是否是unicode。

  3. 新過程的父母是它的創造者。在你的特定情況下 - 你的過程。

  4. 這完全取決於環境塊的創建方式。如果你總是通過調用GetEnvironmentStrings來得到你所得到的結果 - 那麼它就是unicode,如果你用UNICODE定義編譯。那麼你應該設置CREATE_UNICODE_ENVIRONMENT如果你用unicode編譯。另一方面,如果你手動構造它 - 即使你不用unicode編譯,你也可以用unicode構造它。因此 - 您應該根據您如何構建環境塊來設置CREATE_UNICODE_ENVIRONMENT,而不是根據編譯定義。

  5. 如前所述,CreateProcessACreateProcessW都可以用於Ansi或Unicode環境塊。這是正是該標誌是必需的原因。

+1

顯然,如果lpEnvironment爲非NULL,那麼標誌是必需的,否則API將不知道該塊中的內容。但是,如果lpEnvironment爲NULL,何時以及爲什麼需要標誌? – 2010-11-17 17:26:43

+2

當lpEnvironemnt爲NULL時,標誌不是必需的。 – John 2011-02-11 05:00:02