Search code examples
c++cmdparametersshellexecuteshellexecuteex

Passing multiple cmd.exe parameters/arguments to ShellExecute(Ex)?


I've been trying to get cmd.exe /c /v:on executed using ShellExecute and ShellExecuteEx. However, both methods only seem to accept one parameter, because when it encounters the /v:on, a The filename, directory name, or volume label syntax is incorrect. is shown under Windows 7.

This is the code i've tried and am currently messing with (without luck):

#include <windows.h>

int main()

{

    SHELLEXECUTEINFO info = {0};

    info.cbSize = sizeof(SHELLEXECUTEINFO);
    info.fMask = SEE_MASK_NOCLOSEPROCESS;
    info.hwnd = NULL;
    info.lpVerb = NULL;
    info.lpFile = "cmd.exe";
    info.lpParameters = "/c /v:on SET example=stackoverflow & ECHO '!example! & pause'";
    info.lpDirectory = NULL;
    info.nShow = SW_SHOW;
    info.hInstApp = NULL;

    ShellExecuteEx(&info);

//  wait for process to terminate
//  WaitForSingleObject(info.hProcess, INFINITE);

    return 0;

}

Solution

  • Since cmd.exe is an executable file, you should be using CreateProcess() instead of ShellExecuteEx() (which is just going to call CreateProcess() internally anyway, so get rid of the middle man).

    In any case, this is not a ShellExecute() problem. If you open a cmd.exe window and enter your complete command line:

    cmd /c /v:on SET example=stackoverflow & ECHO '!example! & pause'`
    

    You get the exact same error, and more:

    cmd /c /v:on SET example=stackoverflow & ECHO '!example! & pause'
    
    The filename, directory name, or volume label syntax is incorrect.
    '!example!
    'pause'' is not recognized as an internal or external command, operable program or batch file.
    

    The reason for the first error is because /v is a parameter of cmd.exe itself, not a separate command that /C can execute. Everything that follows /C (or /K) is a new command line, so it must be the last parameter specified when calling cmd.exe. This is stated in the documentation for /C (and /K):

    If /C or /K is specified, then the remainder of the command line after the switch is processed as a command line

    As such, /v:on is being interpreted as the first parameter of a new command line, and so it gets treated as a filename which obviously doesn't exist.

    Swap the /V and /C parameters, and the first error goes away:

    cmd /v:on /c SET example=stackoverflow & ECHO '!example! & pause'
    
    '!example!
    'pause'' is not recognized as an internal or external command, operable program or batch file.
    

    Now, you will notice that !example! is not being expanded as expected. That is because you are not quoting the command-line, as the documentation for /C (and /K) says:

    Note that multiple commands separated by the command separator '&&' are accepted for string if surrounded by quotes. Also, for compatibility reasons, /X is the same as /E:ON, /Y is the same as /E:OFF and /R is the same as /C. Any other switches are ignored.

    So, wrap the command-line in quotes, and then !example! gets expanded:

    cmd /v:on /c "SET example=stackoverflow & ECHO '!example! & pause'"
    
    'stackoverflow
    'pause'' is not recognized as an internal or external command, operable program or batch file.
    

    And lastly, pause is not being interpreted correctly because you put it inside of the single quotes of the ECHO instead of outside as a separate command:

    cmd /v:on /c "SET example=stackoverflow & ECHO '!example!' & pause"
    
    'stackoverflow '
    Press any key to continue . . .
    

    And then you can drop the single quotes from ECHO:

    C:\Users\Ryan>cmd /v:on /c "SET example=stackoverflow & ECHO !example! & pause"
    
    stackoverflow
    Press any key to continue . . .