У `CreateProcess(name, args, ...)` первым аргументом должен быть путь к исполняемому файлу а вторым параметры. Но ещё можно и путь и параметры задать вторым аргументом а первым передать нулл. Ещё можно задать и путь и параметры первым аргументом. Но если раскидывать на два, то параметры должны начинаться с пробельчика иначе будет какая-то ошибка.
Ну, раз ошибка «какая-то», а не возвращаемая CreateProcess(), то подозреваю, что это вызываемая программа или какая-то стандартная библиотека в ней видит на нулевом месте в argv какую-то чепуху и сильно удивляется или пытается что-то с ней дальше делать, а пробельчик приводит к честному null.
@mugiseyebrows Это я намекаю, что первым аргументом надо повторить путь к файлу, потому что posix-программы ожидают, что при argc>0 там будет именно он, и только потом параметры, а не используют, скажем, GetModuleFileName().
@stiletto Я вчера посмотрел, это везде так: CreateProcess() в Win32, LoadModule() в Win16, exec*() в UNIX 80-х — везде можно написать что хочешь в качестве командной строки, а разбирать это будет указанная отдельным параметром вызываемая программа. (А вот DOS только голый кусок строчки между именем программы и символами перенаправления передаёт.) Busybox и все прочие именно так понимают, что за команду выполнять, да и в NT, по идее, файловые ссылки есть — загружать в память система будет реальный файл, а строчка останется. Кроме того, этим же путём можно вызвать оболочку с какими-то командами или запустить скрипт, указав при этом только строку, без имени обработчика (которое программа не хочет — или вообще не может — узнать). В общем, и такая комбинация иногда требуется, и сякая.
Передавать первым параметром имя запускаемого файла никто не обязан, но тогда и запускаемый файл не обязан работать.
@stiletto Мне кажется, пробел в начале приводит к argv[0] нулевой длины, то есть null, и поэтому оно прокатывает.
@mugiseyebrows Никаких приколов, всё нормально и аналогично описанному в POSIX man exec(3). Системе предлагается запустить конкретный файл, если она может это сделать, остальные параметры регулируют какие-то детали процесса создания процесса. Одним из них является командная строка, то есть произвольная строчка, привязанная к прочим системным данным о процессе, и доступная ему. Есть она или нет, и что там будет написано — для запуска не важно. Может показаться, что система должна как-то разбирать её, склеивать одно с другим в соответствии с конкретной практикой, но это не так.
Во-первых, не все программы написаны на C или поверх C. Между собой через командную строку они могут общаться как угодно. Даже на C наполнение и значение параметров argc и argv определяется практикой, а не стандартом. Легко представить систему, в которой не существует никаких «путей» и «файлов программ» для подстановки в нулевой элемент, под которую пишут на C. С другой стороны, не труднее представить систему, в которой никакой «командной строки» в памяти не существует, а параметры main() конструируются libc по результатам вызова каких-то системных функций. Можно сказать, что программа, делающая что-то с argv[0], неявно требует задавать его в инструкции по использованию, только сегодня эта часть инструкции может лежать где-то в глубинах документации к компилятору или фреймворку.
Во-вторых, популярные операционные системы исторически поддерживают запуск процессов разнообразных подсистем. Легко представить поддержку не только привычных DOS, Win16, Win32, Linux и так далее, но и какой-нибудь экзотики, в которой командные строки выглядят как "abc$def/ghi". Добавлять в систему конструктор или конвертер командных строк для каждой подсистемы, который будет догадываться, что хотели сказать при вызове, — не лучшая идея. Кроме того, в разных системах модели соответствия между человекочитаемой строчкой и программным представлением могут отличаться. Как уже сказано, в DOS приложение получало не строку, а отдельно имя и параметры после разбора. Более того, для совместимости при указании первым или вторым параметром имён файлов их дескрипторы сразу записывались в специальные поля, чтобы программа ничего не разбирала, а сразу брала и читала-писала. Это, конечно, из времён килобайтов памяти и одной корневой директории на диске(те) пришло, и более сложно устроенными программами не использовалось.
В-третьих, одна и та же программа может по-разному работать, если её вызывать под разными именами, либо через манипуляцию с командной строкой, либо с помощью файловых ссылок. Функция, которая всегда использует имя файла, не позволит сделать такие вызовы.
Если же имя файла не задано, то системе предлагается просто обработать некий текстовый ввод. Можно было бы сделать как в system()/ShellExecute(), чтобы принималась и программа с параметрами, и команды оболочки, и какой-то скрипт, для которого она будет искать и запускать интерпретатор, и всё это с поиском имён файлов по директориям в PATH, но и имеющийся вариант поиска только приложений по началу строчки достаточно сложен. В MSDN всё это и описывается.
@mugiseyebrows Заполняешь строчку ты, по своему разумению, разбирает строчку программа, по своему разумению, а система только перекладывает байтики и ничего по этому поводу не думает.