5 марта 2014 г.

Когда CreateProcess завершается с ошибкой ERROR_SUCCESS

Не в первый раз с этим сталкиваюсь и всё время чешу голову. Решил сделать заметку, чтобы не забыть.

CreateProcess возвращает True, если она успешно запустила программу, и False в случае ошибки. Когда CreateProcess возвращает False, вы можете вызвать GetLastError, чтобы узнать причину неудачи.

Типичный код может выглядеть так:
Win32Check(CreateProcess(...));

Проблема, с которой я столкнулся, заключалась в том, что CreateProcess возвращала False, но GetLastError возвращала 0 (ERROR_SUCCESS). В результате программа выкидывала исключение с сообщением "Операция успешно завершена" ("The operation completed successfully").

Обычно, когда происходит такое, я в первую очередь ищу подводные камни, которые могли бы испортить код ошибки, например. Во вторую очередь я анализирую функцию: быть может я неверно обрабатываю код ошибки, не все функции сообщают коды через LastError. Тем не менее, в нашем случае код никто не портит и CreateProcess его использует.

Как оказалось, причина такого поведения CreateProcess - неверный аргумент. А именно: текущий каталог для программы (lpCurrentDirectory). Если вы указываете этот параметр, но при этом он не содержит допустимый (корректный) путь, то CreateProcess завершится с ошибкой, но не установит причину ошибки. Исправить это можно так:
SetLastError(ERROR_INVALID_PARAMETER);
Win32Check(CreateProcess(...));

P.S. Другие не очевидные моменты с CreateProcess:
1. Если вы указываете первый параметр (имя программы), то программа должна указываться дважды: в первом и во втором параметрах. Иными словами, второй параметр - это полная командная строка, передаваемая в программу "как есть", а не "параметры программы", как ошибочно трактует его 80% людей.
2. Второй параметр не может быть константой.
3. Правильная расстановка кавычек - ещё один больной вопрос.
4. CreateProcess пытается исправлять неверные параметры, что означает что ваш (неверный) код может работать "почти всегда".

P.P.S. Добавьте кто-нибудь описание этого поведения в MSDN, мне не даёт писать, выкидывает ошибку "Error occurred while saving your data".

Читать ещё по CreateProcess:

14 комментариев :

  1. С этой проблемой я столкнулся, когда перешел с D6 на D2010, и достаточно быстро решил ее аналогичным способом. На D6 сей проблемный эффект у меня не наблюдался.

    ОтветитьУдалить
  2. подскажите пожалуйста, почему не работает такой вариант:
    CreateProcess(nil, 'ping -a 11.111.12.38 > D:\file.txt', 0, 0, false, CREATE_NO_WINDOW, 0, 0, SI, PI), выдает ошибку: неверный параметр >. А вариант CreateProcess(nil, 'ping -a 11.111.12.38', 0, 0, false, CREATE_NO_WINDOW, 0, 0, SI, PI) работает

    ОтветитьУдалить
  3. Потому что операторы перенаправления вывода - это функция командного интерпретатора.

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

    Способ первый описан по ссылке (использовать операторы перенаправления) - не самый красивый способ, т.к. включает в себя "лишний" файл. Но зато очень простой.

    Способ второй - использовать перенаправление вывода, предоставляемое самой функцией CreateProcess (см. поля hStdInput и hStdOutput). Это сложнее, но более правильно: вывод попадает к вам напрямую, минуя промежуточный файл. Не сложно найти в интернете и пример - по ключевым словам "delphi перенаправление вывода".

    ОтветитьУдалить
  4. А что случилось с EurekaLog ? Проект живой или нет ?

    ОтветитьУдалить
  5. Блог умер. Пичаль...

    ОтветитьУдалить
  6. "Блог умер. Пичаль... "

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

    ОтветитьУдалить
  7. Автор не совсем прав https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx

    ОтветитьУдалить
  8. Прошу прощения за некропостинг, но возникла вот такая проблема, не могу понять, почему код не работает.
    http://www.cyberforum.ru/delphi-winapi/thread1872690.html

    ОтветитьУдалить
    Ответы
    1. Цитирую: "Второй параметр не может быть константой".

      UniqueString своему CmdLine сделайте.

      Удалить
    2. Эээ... я полагал, что передаю переменную, а не константу. UniqueString сделал, все завелось, большое спасибо! =)) Но в чем ошибка была я так и не понял.

      Удалить
    3. Ну так в том коде по ссылке в переменной записана константа: строковый литерал 'что-то-там'. Это область памяти read only. Когда CreateProcess в неё попробует записать - получит отлуп в виде Access Violation.

      Удалить
  9. Windows 7 с почти последними обновами + Delphi XE2.
    "SetLastError(ERROR_INVALID_PARAMETER);" не ставил.
    И получаю такой код ошибки:
    0x0000010B - Неверно задано имя папки

    ОтветитьУдалить
    Ответы
    1. Да, видимо они подправили.
      Delphi 7 на Windows 10 x64 - при указании несуществующего каталога в lpCurrentDirectory ошибка ERROR_DIRECTORY (The directory name is invalid, == 267).
      Может в "Простая обёртка к CreateProcess, функция-обёртка WinExec" следует поменять SetLastError(ERROR_INVALID_PARAMETER) на SetLastError(ERROR_DIRECTORY), для соответствия?
      Хотя зачем там вообще что-либо - там ведь ни в каком виде не предусмотрено указание lpCurrentDirectory, оно прописано строго как "nil".

      Удалить
    2. Я сейчас уже не вспомню, в каком окружении это было.

      > Может в "Простая обёртка к CreateProcess, функция-обёртка WinExec" следует поменять SetLastError(ERROR_INVALID_PARAMETER) на SetLastError(ERROR_DIRECTORY), для соответствия?

      Нет, ведь это сделано для "всех прочих, непредусмотренных ситуаций". Т.е. не факт, что именно для этой ситуации. Она - просто одна из возможных (да, она единственная известная мне, но сути это не меняет).

      Удалить

Можно использовать некоторые HTML-теги, например:

<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>

Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и (опционально) ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.

Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.

Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.

Примечание. Отправлять комментарии могут только участники этого блога.