Discussion:
[Mingw-users] CommandLineToArgvW is not working
Vincent Torri
2007-05-24 17:52:44 UTC
Permalink
Hey,

I've tried to use CommandLineToArgvW in a simple program :

int argc = 0;
LPWSTR *argv;

argv = CommandLineToArgvW ((LPCWSTR )lpCmdLine, &argc);
printf ("%d **%s**\n", argc, (char *)argv[0]);

With the following command:

./ddraw.exe --toto tata

i get that result:

1 **--toto tata **

I think that it is not the behavior described here:

http://msdn2.microsoft.com/en-us/library/ms647232.aspx

Vincent Torri
Tor Lillqvist
2007-05-24 23:11:40 UTC
Permalink
Post by Vincent Torri
int argc = 0;
LPWSTR *argv;
argv = CommandLineToArgvW ((LPCWSTR )lpCmdLine, &argc);
printf ("%d **%s**\n", argc, (char *)argv[0]);
That is not a complete program. If you want us to ponder what might be
your problem, please provide a *complete* sample program, including
the command line you use to compile it.

But anyway, I don't understand what you think you will achieve by
casting argv[0], which is a pointer to wide characters (wchar_t *), to
a plain character pointer (char *)?

--tml
Vincent Torri
2007-05-25 06:06:03 UTC
Permalink
Post by Tor Lillqvist
Post by Vincent Torri
int argc = 0;
LPWSTR *argv;
argv = CommandLineToArgvW ((LPCWSTR )lpCmdLine, &argc);
printf ("%d **%s**\n", argc, (char *)argv[0]);
That is not a complete program. If you want us to ponder what might be
your problem, please provide a *complete* sample program,
Here it is:

#include <stdio.h>

#include <windows.h>

int APIENTRY
WinMain (HINSTANCE hinstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int argc = 0;
LPWSTR *argv;

argv = CommandLineToArgvW ((LPCWSTR )lpCmdLine, &argc);
printf ("%d **%s**\n", argc, (char *)argv[0]);

return 0;
}
Post by Tor Lillqvist
including
the command line you use to compile it.
gcc -g -Wall -o test3 test3.c -lshell32

Is the problem being the fact that I use the lpCmdLine parameter which is
not a LPCWSTR ?

The fact that argc is not 2 (it seems that in that case, the name of the
prog is not included in argv, see:
http://msdn2.microsoft.com/en-us/library/ms683156.aspx)

Actually, I would use the standard C main function, which would solve all
my problems, but I create a window and I would need an HINSTANCE for it. I
don't know how to create such variable.

Vincent Torri
Julien Lecomte
2007-05-25 14:17:33 UTC
Permalink
Post by Vincent Torri
#include <stdio.h>
#include <windows.h>
int APIENTRY
WinMain (HINSTANCE hinstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int argc = 0;
LPWSTR *argv;
argv = CommandLineToArgvW ((LPCWSTR )lpCmdLine, &argc);
printf ("%d **%s**\n", argc, (char *)argv[0]);
return 0;
}
Post by Tor Lillqvist
including
the command line you use to compile it.
gcc -g -Wall -o test3 test3.c -lshell32
Using -Wall to have warnings enabled, and then hushing them up by using
casts is a source of errors and undefined behavior.

Without the casts :
gcc -g -Wall -o test3 test3.c -lshell32
test3.c: In function `WinMain':
test3.c:14: warning: passing arg 1 of `CommandLineToArgvW' from
incompatible pointer type
test3.c:15: warning: char format, different type arg (arg 3)
Post by Vincent Torri
Is the problem being the fact that I use the lpCmdLine parameter which is
not a LPCWSTR ?
Yes. A wide string and a c string are not the same, and nor are pointers
to them.
Why not use GetCommandLineW() as MSDN recommends ?

==BOF test1.c==
#include <stdio.h>
#include <windows.h>

int APIENTRY
WinMain (HINSTANCE hinstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
int argc, i;
LPWSTR *argv;
// NB: Use 'W' versions and %ls in printf
if ((argv = CommandLineToArgvW(GetCommandLineW(), &argc)))
{
// success...
for (i = 0; i < argc; i++)
printf("%d=%ls\n", i, argv[i]);
// MSDN says to LocalFree the memory
LocalFree(argv);
}
return 0;
}
==EOF test1.c==
$ gcc -o test1.exe test1.c && ./test1.exe --toto --tata
Post by Vincent Torri
Actually, I would use the standard C main function, which would solve all
my problems, but I create a window and I would need an HINSTANCE for it. I
don't know how to create such variable.
Actually HINSTANCE is the same as HMODULE so you can use
GetModuleHandle(0) to get HINSTANCE of your process.

Julien
Vincent Torri
2007-05-25 15:53:14 UTC
Permalink
Using -Wall to have warnings enabled, and then hushing them up by using casts
is a source of errors and undefined behavior.
I am completely aware of the warnings and such things. But the API of
windows is crappy and you sometimes have to cast to remove warnings. Like
the directdraw 7 interface, for instance.
Post by Vincent Torri
Is the problem being the fact that I use the lpCmdLine parameter which is
not a LPCWSTR ?
Yes. A wide string and a c string are not the same, and nor are pointers to
them.
Why not use GetCommandLineW() as MSDN recommends ?
Luke Dunstan
2007-05-25 16:04:14 UTC
Permalink
----- Original Message -----
From: "Vincent Torri" <***@univ-evry.fr>
To: "Julien Lecomte" <***@famille-lecomte.net>
Cc: "MinGW Users List" <mingw-***@lists.sourceforge.net>
Sent: Friday, May 25, 2007 11:53 PM
Subject: Re: [Mingw-users] CommandLineToArgvW is not working
Post by Vincent Torri
Using -Wall to have warnings enabled, and then hushing them up by using casts
is a source of errors and undefined behavior.
I am completely aware of the warnings and such things. But the API of
windows is crappy and you sometimes have to cast to remove warnings. Like
the directdraw 7 interface, for instance.
Post by Vincent Torri
Is the problem being the fact that I use the lpCmdLine parameter which is
not a LPCWSTR ?
Yes. A wide string and a c string are not the same, and nor are pointers to
them.
Why not use GetCommandLineW() as MSDN recommends ?
Vincent Torri
2007-05-25 16:08:51 UTC
Permalink
Yes, and if you only want the ANSI string then you should be calling
CommandLineToArgvA() instead.
already tried that wihtout success. More precisely:

grep -r CommandLineToArgvA /mingw/include

returns nothing. Only the wide char version exists (mingw 5.1.3
installer, candidate version).

Vincent Torri
Julien Lecomte
2007-05-29 09:14:59 UTC
Permalink
(NB: I'm replying to a post in another thread, because for some reason,
even if I did receive a post ack, when I create a new thread my post
mysteriously never gets through. I'm sorry.)


Vincent Torri's mail on WinMain, CommandLineToArgvW and
GetCommandLineA/W made me notice the following "bug" which can be tested
with the following code:

== BOF test1.c ==
#include <stdio.h>
#include <windows.h>

int APIENTRY
WinMain (HINSTANCE hinstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
printf("lpCmdLine (%d) = **%s**\n", strlen(lpCmdLine), lpCmdLine);
// strip right trailing spaces
CHAR* cstrip = lpCmdLine + strlen(lpCmdLine) - 1;
while (*cstrip == ' ')
*cstrip-- = 0;
printf("stripped (%d) = **%s**\n\n", strlen(lpCmdLine), lpCmdLine);
return 0;
}
== EOF test1.c ==

I've noticed that the mingw-runtime will:
- often add at least a trailing space when using several commands in a row.
- convert/remove quotes in some cases.

With the folowing commands (please note that spaces can be important)
$ gcc test1.c -o test1.exe
$ ./test1.exe "toto1"
$ ./test1.exe 'toto2'
$ ./test1.exe toto3&& ./test1.exe toto4
$ ./test1.exe toto5 && ./test1.exe toto6

I get the selected following results:

With console MSYS shell
(TERM=cygwin; MSYSTEM=MINGW32)

***@PUMILIO ~/procrast
$ ./test1.exe "toto1"
lpCmdLine (6) = **toto1 **
stripped (5) = **toto1**

$ ./test1.exe 'toto2'
lpCmdLine (6) = **toto2 **
stripped (5) = **toto2**

$ ./test1.exe toto3&& ./test1.exe toto4
lpCmdLine (6) = **toto3 **
stripped (5) = **toto3**

lpCmdLine (6) = **toto4 **
stripped (5) = **toto4**

$ ./test1.exe toto5 && ./test1.exe toto6
lpCmdLine (6) = **toto5 **
stripped (5) = **toto5**

lpCmdLine (6) = **toto6 **
stripped (5) = **toto6**


With console cmd.exe:

M:\home>test1.exe "toto1"
lpCmdLine (7) = **"toto1"**
stripped (7) = **"toto1"**

M:\home>test1.exe 'toto2'
lpCmdLine (7) = **'toto2'**
stripped (7) = **'toto2'**

M:\home>test1.exe toto3&& test1.exe toto4
lpCmdLine (5) = **toto3**
stripped (5) = **toto3**

lpCmdLine (5) = **toto4**
stripped (5) = **toto4**

M:\home>test1.exe toto5 && test1.exe toto6
lpCmdLine (12) = **toto5 **
stripped (5) = **toto5**

lpCmdLine (5) = **toto6**
stripped (5) = **toto6**


I might be able to understand why quotes are converted (toto1 and toto2
for example) depending on console box as this might be an effect of the
shell before creating the process.

On the other hand, I can't understand why behavior isn't the same in
toto5 test case since I tend to believe that any command line will
(should?) always be right space trimmed.

I believe that mingw-runtime should trim the spaces on the right before
calling WinMain and that results should be more consistent under cmd.exe.
Should we or not modify mingw-runtime to trim those spaces?
Keith MARSHALL
2007-05-29 11:13:46 UTC
Permalink
Post by Julien Lecomte
I believe that mingw-runtime should trim the spaces on the right
before calling WinMain and that results should be more consistent
under cmd.exe. Should we or not modify mingw-runtime to trim
those spaces?
I believe this is more likely to be an issue with the way in which
MSYS' sh.exe reconstructs the command line it passes to the child
process, rather than a problem in mingw-runtime.

Any native Woe32 process receives its arguments as a single string
of characters. Whatever you type on the cmd.exe command line just
gets passed verbatim to the child; this includes any leading or
trailing spaces which happen to be present, after the command name
has been carved off, and up to any following command separator. The
only exception to this is that redirection operators are actioned,
and filtered out.

MSYS' sh.exe, OTOH, behaves like any UNIX shell. It parses each
command line, expanding one level of quoting, and constructs an
argument vector; this is then passed to the child through an `exec'
function call, (after forking a process in which to run the child).
To make this work in the Woe32 world, MSYS' `exec' function has to
reconstruct a single command line argument string, from the passed
argument vector; it is this reconstructed string which is then
passed to the child, via the system's own `CreateProcess' function.

I don't think it would be practical to make sh.exe reproduce the
behaviour of cmd.exe, wrt redundant white space; here are just a
few of the reasons which spring to mind:

1) While constructing the argument vector for `exec', one level of
quoting is expanded; the quoting characters are discarded.

2) In this process, all unquoted white space is discarded.

3) Attempting to modify this behaviour would, in all likelihood,
destroy sh.exe's variable expansion and command substitution
features.

4) sh.exe supports a much richer set of quoting capabilities; it
is not practical to simply pass all quoting directly to a Woe32
process, which is unlikely to understand the syntax.

One thing which does seem odd, is the single trailing space left on
the *unquoted* argument in your example; I would not have expected
the MSYS `exec' function to add that, so it could be considered a
bug, which we may wish to investigate. Please raise a ticket on
the bug tracker:
https://sourceforge.net/tracker/?func=add&group_id=2435&atid=102435

FWIW, you should also note that Woe32's own `exec' function, as
provided by MSVCRT, is critically broken wrt the quoting of grouped
arguments with included white space, which are present in the passed
argument vector. The disfunctional `spawn' functions, which MSVCRT
provides instead of the `fork()...exec()' API is identically broken.
For an explanation, and GPLed workaround, see:
https://sourceforge.net/project/showfiles.php?group_id=2435&package_id=82725&release_id=366538

Regards,
Keith.

Loading...