17

CreateProcessのドキュメントには次のように記載されています (太字で強調しています)。

lpEnvironment [入力、オプション]

[...] lpEnvironment が指す環境ブロックに Unicode 文字が含まれている場合は、dwCreationFlags に CREATE_UNICODE_ENVIRONMENT が含まれていることを確認してください。このパラメーターが NULL で、親プロセスの環境ブロックに Unicode 文字が含まれている場合、dwCreationFlags に CREATE_UNICODE_ENVIRONMENT が含まれていることも確認する必要があります。

MSDN は間違っていて、フラグの意味を誇張していますか、それとも実際の要件ですか?

フラグを設定せず、機能しているように見えるコードを見たことがありますが、私の妄想的な部分は、MSDN の言うことを 100% 遵守したいと考えています。そうは言っても、極端に行かずに MSDN の規則に本当に従うことができるかどうかはわかりません。

lpEnvironment が NULL のときに CREATE_UNICODE_ENVIRONMENT を設定する (または設定しない) 必要があるのはばかげていると思います。

  1. 環境ブロックを渡さない場合、CreateProcess はブロック自体を取得する必要があります。その場合、ブロックのタイプを知ることは私よりも良い立場にあります。

  2. ブロックに実際に Unicode 文字が含まれているかどうかを確認するにはどうすればよいですか?

    ブロックを取得して、現在のコード ページ外の文字を検査する必要がありますか? (ここでは、MSDN が "Unicode 文字" と呼ぶのは、それを意味していると思います。)

    env-block を取得する必要がある場合は、NULL ではなく lpEnvionment で渡すこともできますが、なぜ NULL を許可するのでしょうか?

    環境ブロックを取得して検査する必要があるのは、CreateProcess のすべての呼び出し元にとって非常識な要件のようです。それは確かに API 自体が処理する必要があるものです。

  3. 「親プロセス」と言うとき、それは私のプロセスを意味するのでしょうか、それは新しい親になろうとしていますか、それとも私のプロセスの親を意味しますか? MSDN を最初に読んだとき、プロセスを開始した CreateProcess 呼び出しがANSI または Unicode 環境ブロックを渡されたかどうかを何らかの方法で判断する必要があると思いましたが、そうではありません。

    NT ベースの OS では、プロセスの作成時に必要に応じて ANSI から変換された Unicode 環境ブロックがすべてのプロセスにあり、CreateProcess にそのまま渡されたデータのブロックにプロセスが固執しないと想定しています。

    (もしかしたら、この全体は、OS 自体が Unicode ではなかった Win9x 時代の名残なのだろうか? それでも、アプリケーション コードが OS 自体よりも優れた決定を下す方法や、そうすべき理由がわかりません。することが期待されます。)

  4. フラグを設定しないコードだけでなく、コンパイル時に UNICODE が定義されている場合は常にフラグを設定するコードを見てきました。要件が実行時の env ブロックにある場合、およびコードが外部プロセスにロードされた DLL にある可能性がある場合、それは意味がありません。

    env ブロックはプロセス全体であるため、コンパイル時に定義されている UNICODE は無関係に見えます。

  5. CreateProcessA または CreateProcessW のどちらを呼び出すかだけの問題である場合、ブロックが NULL の場合、フラグは暗黙的でなければならないため、それも意味がありません。

私自身のコードでは、この質問を避け、常に環境ブロックの Unicode コピーを (GetEnvironmentStringsW 経由で) 取得し、常にそれを CreateProcess に渡し、常に CREATE_UNICODE_ENVIRONMENT を設定することにしました。これが、MSDN の記述に基づいて 100% 正しいと判断できる唯一の方法です。

確かに私がやっていることは冗長ですが。CreateProcess はそれほど愚かではありませんね。

一方、これは私たちが話している CreateProcess です。これは最適に設計された API ではなく、他にも多くの落とし穴があります (頭の中で):

  1. 引数文字列が const の場合、インプレースで変更されるため失敗します。
  2. 最初の引数をオプションにすることで、2 番目の引数で exe-path を引用するのを忘れないようにします。
  3. 最初の引数で明示的に指定されている場合でも、2 番目の引数で適切に引用された exe-path を要求します。

したがって、それがインテリジェントに動作する、または呼び出し元の雑用を処理する可能性が高いと想定するのはおそらく正しくありません...

自分のコードからパラノイアのがらくたを削除するか、他のすべてのコードに追加するかはわかりません。ああ。:-)

2010 年 11 月 18 日追加:

少なくとも Windows 2000 から Windows 7 まで、env-block が NULL の場合、フラグは無関係であるように思われます。以下の私のテストを参照してください。

明らかに、これは、フラグが将来のすべての OS で常に無関係であるという決定的なものではありませんが、それ以外の場合はどうなるかわかりません。

子を作成しようとしている親を作成した祖父母がいるとします。

  • OS が常に親の環境ブロックを Unicode として格納する場合 (祖父母が ANSI ブロックを渡した場合に親の作成中に ANSI から変換した場合)、親が NULL ブロックを渡すときにフラグに注意を払うと、CreateProcess はエラーになります。 . CreateProcess は、Child が継承するブロックが常に Unicode であることを認識している必要があります。

  • あるいは、OS は、親の環境ブロックを祖父母からのものとまったく同じように保存する場合があります。(これはありそうにないようですが、可能です。) その場合、親は、祖父母が通過したブロックのタイプを検出する方法がありません。ここでも、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-block が NULL のときにフラグが問題になる可能性があるシナリオを考えられる人はいますか? そして、そのシナリオでは、OS 自体よりもアプリの方がフラグの正しい値をどのように知っているのでしょうか?

4

1 に答える 1

8

CreateProcess関数の宣言では、lpEnvironmentパラメーターが is と宣言されていることに注意してくださいLPVOID

これは何を意味するのでしょうか?これは、CreateProcess関数の Ansi/Unicode バージョンを使用し、Ansi/Unicode バージョンの環境ブロックを任意の組み合わせで渡すことができることを意味します。特に、 の Unicode バージョンを使用CreateProcessして Ansi 環境ブロックに渡したり、その逆を行ったりすることができます。

そのため、実際にユニコード環境ブロックを使用する場合は、設定CREATE_UNICODE_ENVIRONMENT必要です。これは、OS がユニコードであると認識する可能性がある他の従来の方法 (いくつかの見苦しいヒューリスティックを除く) がないためです。

今あなたの質問について:

  1. 環境ブロックを明示的に渡さない場合、新しく作成されたプロセスは、最初はその作成者と同じ環境変数を持ちます。新しく作成されたプロセスに追加の構成を行う必要がない限り、これ以上は必要ありません。

  2. 新しく作成されたプロセスに環境ブロックを渡す場合は、手動で構築するか、どこかから取得する必要があります。いずれにせよ、それがユニコードかどうかを知る必要があります。

  3. 新しいプロセスの親は、その作成者です。あなたの特定のケースでは - あなたのプロセス。

  4. これは、環境ブロックの作成方法のみに依存します。呼び出して取得したものを常に渡す場合、定義済みGetEnvironmentStringsでコンパイルしている場合、それはユニコードになります。UNICODE次にCREATE_UNICODE_ENVIRONMENT、Unicode でコンパイルする場合に設定する必要があります。一方、手動で構築する場合は、Unicode でコンパイルしなくても、Unicode で構築できます。したがってCREATE_UNICODE_ENVIRONMENT、コンパイル定義ではなく、環境ブロックの構築方法に従って設定する必要があります。

  5. 既に述べたように、 と の両方CreateProcessACreateProcessWAnsi または Unicode 環境ブロックで動作する可能性があります。これが、まさにこのフラグが必要な理由です。

于 2010-11-17T17:14:59.657 に答える