3

Passing command-line arguments to an application in Linux just works fine with the exec* commands where you clearly pass each argument on its own. Doing so on Windows using the same functions is not an option if one wants to control the standard pipes. As those functions are based on CreateProcess() there are some clear rules on how to escape special characters like double quotes.

Sadly, this only works correctly as long as the called application retrieves its command-line arguments via main(), wmain() or CommandLineToArgvW(). However, if the called application gets those arguments via WinMain(), wWinMain(), GetCommandLineA() or GetCommandLineW() it is up to the application how to parse the command-line arguments as it gets the whole command-line rather than argument by argument.

That means a simple application named test using main() as entry point gets "abc" if called as test.exe \"abc\". Calling cmd.exe as cmd.exe /c "echo \"abc\"" will not output "abc" as expected but \"abc\".

This leads to my question:
How it possible to pass command-line arguments to Windows applications in a generic way despite these quirks?

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
user2525536
  • 366
  • 1
  • 4
  • 14
  • As long as applications are free to parse the command line any way they like, there's by definition no generic way to send the arguments. Do you have any specific problem you are trying to solve? What do you need the generic solution for? – Karel Petranek Aug 21 '15 at 22:38
  • Any application (including those with a `main` entry point) can parse the command line any way they like. Any application (including those with a `WinMain` entry point) can use [__argv](https://msdn.microsoft.com/en-us/library/dn727674.aspx) to get individual arguments parsed using common rules. What's more, the real entry point for all Windows applications doesn't have any parameters. The command line is extracted as a **single** string from the PEB on startup. – IInspectable Aug 21 '15 at 22:47
  • The rule reference you link to is a description of how Microsoft's runtime library parses arguments. I think `CommandLineToArgvW` differs somewhat. Anyway you don't have those rules at the API level. There is no common convention for quoting in Windows, except basic double-quote characters. When you drag files onto an executable, the common GUI shell just quotes them (and you can experiment with that to see how to pass them yourself). – Cheers and hth. - Alf Aug 21 '15 at 23:07
  • I am trying to write an application which e.g. runs commands in user defined shells. So it is up to the user to execute echo "abc" in cmd.exe or in sh.exe (like from Cygwin or MSys). I am reading the output of the executed application for further processing. – user2525536 Aug 21 '15 at 23:10
  • You seem to be confusing the command line interpreter's parsing with that done by the application. It appears as though you attribute the command line interpreter's parsing to the application. Unless you provide a concise specification of what you really want, and why it doesn't exhibit the expected behavior, this question is off-topic, and subject to closing. – IInspectable Aug 21 '15 at 23:43
  • That means I cannot pass for example the argument `abc\"xyz` to any application while retaining all characters exactly as given there if I do not know exactly how the command-line parser of that application works so that I can quote special characters? – user2525536 Aug 22 '15 at 00:14

2 Answers2

0

It is the C language that makes you need to use a backslash before a double quote in C code. There is no such rule for shell processing. So if you writing code to call CreateProcess and passing the literal string "abc" then you need to use backslashes because you are writing in C. But if are writing a shell script to pass invoke your app to pass "abc", e.g. the Echo example, then you don't use backslashes because there is no C code involved.

David Ching
  • 1,903
  • 17
  • 21
0

In Windows, you need to think about the command as a whole, not as a list of individual arguments. Applications are not obliged to parse the command into arguments in any particular way, or indeed at all; consider the example of the echo command, which treats the command line as a single string.

This can be a problem for runtime library developers, because it means there is no reliable way to implement a POSIX-like exec function. Some library developers take the straightforward approach, and require the programmer to provide quote marks as necessary, and some attempt to quote the arguments automatically. In the latter case it is essential to provide some method for the programmer to specify the command line as a whole, disabling any automatic quotation, even if that means a Windows-specific extension.

However, in your scenario (as described in the comments) there shouldn't be a problem. All you have to do is make sure you ask the user for a command, not for a list of arguments. Your program simply doesn't need to know how the command will be split up into arguments, if at all; understanding the command's syntax is the user's job. [NB: if you don't think this is true, you need to explain your scenario much more clearly. Provide an example of what the user might enter and how you think your application would need to process it.]

PS: since you mentioned the C library _exec functions, note that they don't work as you might be expecting. The arguments are not passed individually to the child, since that's impossible; in the Microsoft C runtime, if I remember correctly, the arguments are simply combined together into a single string, with a single space as the delimiter, so ("hello there") will be indistinguishable from ("hello", "there").

PPS: note that calling cmd.exe to parse the command introduces an additional (and much more complicated) layer of processing. Generally speaking taking that into account would still be the user's job, but you may want to be aware of it. The escape character for cmd.exe processing is the caret.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • I feared as much, but I accept this answer as it confirms what I thought: There is no generic way to retain all characters as it and still pass argument by argument to a different application in Windows [like in Linux](http://stackoverflow.com/questions/14042586/getcommandline-linux-true-equivalent) because Windows works fundamentally different on that part. I will leave it up to the user to provide a correct quoting scheme. I just also need the user to provide the path to the executable as it is impossible to retrieve that automatically for CreateProcess() if I want to support cmd.exe and co. – user2525536 Aug 22 '15 at 07:32