system() implementation on Windows (again)by Anonymous Monk
|on Aug 18, 2011 at 12:05 UTC||Need Help??|
Anonymous Monk has asked for the
wisdom of the Perl Monks concerning the following question:
I noticed a strange behaviour of the system() function on windows, and I would like to understand how _exactly_ is system() implemented on Win OS'es...
According to perl's documentation, system() can be used in two ways: either passing it a string as a _single_ argument, which contains the external program to execute plus all needed arguments. E.g.:
Or you can pass it an array, of which the first element is then taken as the program name to execute, and the remaining elements are passed as separate arguments to the program.
According to docs, the difference between the first usage and the second usage is that when passing a single string, perl first checks if the string contains any shell metacharacters, and if it does - system() calls the shell and passes it the whole string as command. In the second usage, system() never calls any OS shell, but directly executes the program and passes the array elements as separate arguments, EXACTLY as they are. Regardless if the array elements contain spaces or special characters or anything, each of them is passed to the external program as one and exactly one argument.
Now... This is true, and works perfectly well on Unix. But not on Windows. As an example, let's use a simple executable, that (I think) is available on every windows machine: msg.exe. It can take some options and arguments, but basically it presents a message box with some text to the user. In the simplest form, you can manually execute this on the command line:
where "username" is your windows ser name, and it will display a message box with the text "some text". (Btw. the command works even without the double-quotes around "some text".) But now to the problem with perl's system() function. My goal is to execute an external program, and be able to pass it my arguments EXACTLY as they are. no interpretation whatsoever. Since I know that the first usage of system() - i.e. giving it the full command with arguments as one string - might involve the local OS shell, I decide to not use it. But use the array form instead. So first check this sample code:
This works OK. The whole string in $txt is diplayed in the message box, and it looks like system() is working correctly. But this code:
does not work anymore! The only difference is the /w at the beginning of the message text. But it shows that something strange is happening behind the scenes, when system() runs. /w is an option for msg.exe which tells it to wait until user clicks OK. And in fact when I execute the perl script, I can see two things: First, the message box does not display the whole text that is in $txt, it only starts with the word "sample". And second: the command line prompt does not return immediately, it only comes back when I click on OK. (This is because the script waits for system() to complete, system() waits for msg.exe to complete, and msg.exe waits for my click.
Sooo... Reading perl docs, I would expect that system() executes my program (msg.exe), and passes it the "username" as first argument, and the complete $txt content (whatever characters it might contain!) as a second argument. Instead, it looks like system() still builds one long command first, using the array elements provided, and then somehow executes all of it: (although I do not see any additional shell i.e. cmd.exe running in the task manager)
This is in my opinion incorrect! And the big question is: What does system() actually do behind the scenes, because what I ultimately need, is the possibility to pass _arbitrary_ arguments to my external program, even with special characters etc.BTW. I tried ActivePerl and Strawberry perl, and they both have the same issue. If there are any monks out there, who know how this stuff is exactly implemented on Windows, I would greatly appreciate an answer.