Win32 CreateProcess: когда CREATE_UNICODE_ENVIRONMENT * действительно * нужен? - PullRequest
14 голосов
/ 17 ноября 2010

Документация CreateProcess (мой жирный акцент):

lpEnvironment [in, необязательно]

[...] Если блок окружения, на который указывает lpEnvironment, содержит символы Юникода, убедитесь, что dwCreationFlags включает CREATE_UNICODE_ENVIRONMENT. Если этот параметр имеет значение NULL и блок среды родительского процесса содержит символы Unicode, вы также должны убедиться, что dwCreationFlags включает CREATE_UNICODE_ENVIRONMENT.

Неправильно ли указано MSDN и завышает значение флага илиэто реальное требование?

Я видел код, который никогда не устанавливает флаг и, кажется, работает, но параноидальная часть меня хочет на 100% соответствовать тому, что говорит MSDN.Сказав это, я не уверен, что вы действительно можете следовать правилам MSDN, не вдаваясь в крайности.

Необходимость устанавливать (или не устанавливать) CREATE_UNICODE_ENVIRONMENT, когда lpEnvironment равно NULL, кажется мне смешным:

  1. Если я не передам блок среды, CreateProcess должен получить сам блок.В этом случае он находится в лучшем положении, чем я, чтобы знать тип блока.

  2. Как узнать, действительно ли блок содержит символы Unicode?

    Могу ли я ожидатьполучить блок и проверить его на наличие символов вне текущей кодовой страницы?(Я предполагаю, что MSDN здесь подразумевает это под «символами Unicode».)

    Если мне нужно получить блок env, то я мог бы также передать его в lpEnvionment вместо NULL, так зачем даже разрешать NULL?

    Необходимость получения и проверки блока env кажется безумным требованием для каждого вызывающего CreateProcess;это, безусловно, то, что должен обрабатывать сам API.

  3. Когда говорится, что «родительский процесс» означает даже мой процесс, он собирается стать новым родителем или родитель моего процесса ?Мое начальное чтение MSDN заставило меня подумать, что я должен каким-то образом сказать, был ли вызов CreateProcess, который запустил мой процесс , проходить блок среды ANSI или Unicode, но это, конечно, не так.

    Я предполагаю, что в ОС на базе NT все процессы имеют блок env Unicode, преобразованный из ANSI, если это необходимо при создании процесса, и что процессы не привязываются к тому блоку данных, который был передан в CreateProcess какis.

    (Может быть, все это осталось от дней Win9x, когда сама ОС не была Unicode? Даже тогда, однако, я не вижу, как код приложения мог принять решение лучше, чемсама ОС, и почему она должна.)

  4. Как и код, который никогда не устанавливает флаг, я видел код, который всегда устанавливает его, если UNICODE был определен при компиляциивремя.Это не имеет смысла, когда требование к тому, что находится в блоке env во время выполнения, и когда код может быть в DLL, загруженной в сторонний процесс.

    Блок env является широким для процесса, поэтому UNICODE определяется во время компиляции.время кажется несущественным.

  5. Если вопрос только в том, вызову ли я CreateProcessA или CreateProcessW, тогда флаг должен быть неявным, когда блок равен NULL, так что это тоже не имеет смысла.

В своем собственном коде я решил избежать вопроса и всегда получаю Unicode-копию блока среды (через GetEnvironmentStringsW), всегда передаю ее в CreateProcess и всегда устанавливаюCREATE_UNICODE_ENVIRONMENT.Это единственный способ, который я могу видеть на 100% правильно, основываясь на том, что говорит MSDN.

Конечно, то, что я делаю, избыточно.CreateProcess не может быть настолько глупой, не так ли?

С другой стороны, это CreateProcess, о котором мы говорим.Это не самый лучший API-интерфейс, и у него есть куча других подводных камней (вне головы):

  1. Сбой, если строка аргумента является константой, потому что она изменяет ее на месте.
  2. Делать первый аргумент необязательным и, таким образом, предлагать людям забыть процитировать exe-путь во втором аргументе.
  3. Требовать правильно цитируемый exe-путь во втором аргументе, даже если он указан явнопервый.

Так что, возможно, было бы неправильно считать, что он ведет себя разумно или может позаботиться о делах для звонящего ...

Я не знаю,удалить параноидальный мусор из моего собственного кода или добавить его ко всем другим кодам, которые я вижу.Argh.: -)

ДОБАВЛЕНО 18 ноября 2010:

Очевидно, что флаг не имеет значения, когда блок env равен NULL, по крайней мере, в Windows 2000 черезв Windows 7. См. мой тест ниже.

Очевидно, это не делает окончательным, что флаг всегда будет неактуальным во всех будущих ОС, но я действительно не вижу, как это могло быть иначе.

Скажем, у нас есть дедушка, который создал Parent, который собирается создать дочерний элемент:

  • Если ОС всегда сохраняет блок env для Parent как Unicode - преобразовав его из ANSIво время создания Parent, если Grandparent передал блок ANSI - тогда CreateProcess будет с ошибкой обращать внимание на флаг, когда Parent пропускает блок NULL.CreateProcess должен ЗНАТЬ, что блок, который унаследует дочерний элемент, ВСЕГДА будет Unicode.

  • В качестве альтернативы, ОС может хранить блок env для Parent точно так же, как он поступил от деда.(Это кажется маловероятным, но это возможно.) В этом случае 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 равен NULL, флаг там не имеет значения.

Когда значение не равно NULL, флаг имеет значение, поскольку CreateProcess нужно сказать, что стоит за пустотой * (но это очевидно, и вопрос касается только случая NULL).

Может кто-нибудь придуматьлюбой сценарий вообще, где флаг может иметь значение, когда env-блок равен NULL?И в этом случае, как приложение может узнать правильное значение флага лучше, чем сама ОС?

1 Ответ

6 голосов
/ 17 ноября 2010

Обратите внимание, что в объявлении функции CreateProcess объявлен параметр lpEnvironment: LPVOID.

Что это значит?Это означает, что вы можете использовать версию CreateProcess Ansi / Unicode и передавать ей блок среды версии Ansi / Unicode в любой комбинации.В частности, вы можете использовать Unicode-версию CreateProcess и передать ей блок среды Ansi и наоборот.

Так что для установки CREATE_UNICODE_ENVIRONMENT необходимо обязательно , если вы действительно используете блок среды Unicode,потому что нет другого обычного способа (кроме некрасивой эвристики) ОС может понять, что это юникод.

Теперь по вашим вопросам:

  1. ЕслиВы явно не передаете блок окружения - у вновь созданного процесса изначально будут те же переменные окружения, что и у его создателя.Если вам не нужно вносить какую-либо дополнительную конфигурацию во вновь созданный процесс - не требуется ничего, кроме этого.

  2. Если вы передаете блок среды во вновь созданный процесс - вы должны либо вручнуюпостроить это или получить его откуда-то.В любом случае вы должны знать, является ли он юникодом.

  3. Родителем нового процесса является его создатель.В вашем конкретном случае - ваш процесс.

  4. Это зависит исключительно от того, как создается блок среды.Если вы всегда передаете то, что получаете, вызывая GetEnvironmentStrings - тогда это в юникоде, если вы компилируете с определенным UNICODE.Тогда вам следует установить CREATE_UNICODE_ENVIRONMENT, если вы компилируете в юникоде.С другой стороны, если вы создаете его вручную - вы можете создать его в Unicode, даже если вы не компилируете в Unicode.Следовательно - вы должны установить CREATE_UNICODE_ENVIRONMENT в соответствии с тем, как вы сконструировали блок окружения, а не в соответствии с определениями компиляции.

  5. Как уже говорилось, оба CreateProcessA и CreateProcessW могут работатьс блоком среды Ansi или Unicode.Это точно причина, по которой требуется этот флаг.

...