Consider the following files:
a.bat
:
@echo Hello from bat %1
and c.cpp
:
#define UNICODE
#include <windows.h>
#include <stdio.h>
void check(TCHAR *cmd, TCHAR *args) {
STARTUPINFO sinf;
PROCESS_INFORMATION pinf;
memset(&sinf, 0, sizeof sinf);
sinf.cb = sizeof(sinf);
CreateProcess(cmd, args, NULL, NULL, FALSE, 0, NULL, NULL, &sinf, &pinf);
WaitForSingleObject(pinf.hProcess, INFINITE);
}
int main() {
TCHAR cmd1[] = L"a";
TCHAR cmd2[] = L"a.bat";
TCHAR cmdargs1[] = L"a argument";
TCHAR cmdargs2[] = L"a.bat argument";
TCHAR args[] = L"argument";
#define run_check(a, b) printf(#a " + " #b "\n"); fflush(stdout); check(a, b)
run_check(cmd1, cmdargs1);
run_check(cmd1, cmdargs2);
run_check(cmd1, args);
run_check(cmd2, cmdargs1);
run_check(cmd2, cmdargs2);
run_check(cmd2, args);
run_check(NULL, cmdargs1);
run_check(NULL, cmdargs2);
printf("Done\n");
return 0;
}
Note that I haven't specified cmd.exe
in any of calls to CreateProcess
, while MSDN says that I have to do it:
To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file.
However, I get the following output:
cmd1 + cmdargs1
cmd1 + cmdargs2
cmd1 + args
cmd2 + cmdargs1
Hello from bat argument
cmd2 + cmdargs2
Hello from bat argument
cmd2 + args
"argument" не является внутренней или внешней
командой, исполняемой программой или пакетным файлом.
NULL + cmdargs1
NULL + cmdargs2
Hello from bat argument
Done
This means that whenever .bat
extension is explicitly specified either in lpApplicationName
or lpCommandLine
, batch file is successfully started. This works wit .cmd
too, but not with .vbs
. Does anyone know reason behind such behavior and difference between practice and documentation? Is such behavior 'an accident' or it persists among different Windows versions? (I use Windows 7 HP) Is there any point in documentation from which one can infer such behavior?
As a software developer, you shouldn’t rely on undocumented behavior. Even if something works fine when you test it, if the documentation says “you must do it another way”, you generally have to forget your tests, and do as instructed. Otherwise your software can silently break down with the next windows update, including even the minor update.
Now on the batch/vbs files.
To run the CMD/BAT, call
GetEnvironmentVariable("ComSpec")
to obtain path to cmd.exe, then call CreateProcess specifying command=cmd.exe
, arguments/C path_to_CMD_file arg1 arg2
. If the path or arguments contain space, you must include them in quotes. If the path or arguments contain spaces contain"
, replace with^"
.To run VBS, call
ExpandEnvironmentStrings("%windir%\\System32\\cscript.exe")
or wscript.exe, then pass VBS path in the first argument of the .exe file. If the path or an argument contain spaces or quotes, same rules apply to escaping.