delphicommand-line-argumentsdelphi-xe2delphi-ide

Why the quantity of command-line arguments changes when the application is started by the IDE vs an launcher?


Considering the following command-line arguments

"alfa" "beta" "4"

When I specify the Run>Parameters... for an project I'm working the application shows on Process Explorer as command-line:

"c:\myapp\myapp.exe" "alfa" "beta" "4"

And ParamCount shows 4 parameters. But when I start the same executable from an launcher application (which does access control), Process Explorer shows:

"alfa" "beta" "4"

ParamCount show 3 paramers. The command-line was extracted from the launcher application. In theory it would work, since when started from launcher the application work flawlessly. When started from IDE it tries to do StrToInt on the "4" above, but retrieves just the "beta" parameter instead.

Sample code from launcher application:

var
  StartupInfo: TSTARTUPINFO;
  ProcessInfo: PROCESS_INFORMATION;
  CurrentDirPath: String;
begin
  Result := 0;
  ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
  StartupInfo.cb := SizeOf(StartupInfo);
  DirCorrente := ExtractFilePath(sExe);

  if CreateProcess(PChar(sExe), PChar(sParam), nil, nil, true,
    NORMAL_PRIORITY_CLASS, nil, PChar(CurrentDirPath),
    StartupInfo, ProcessInfo) then

The content of sParam is the command-line arguments above and sExe is the executable path. Why this happens?

NOTE:I already devised how to change the command-line arguments interpretation to be robust for this edge case - the point here is WHY this happens.


Solution

  • Your launcher program isn't calling CreateProcess properly. Consider this excerpt from the documentation (emphasis added):

    If both lpApplicationName and lpCommandLine are non-NULL, the null-terminated string pointed to by lpApplicationName specifies the module to execute, and the null-terminated string pointed to by lpCommandLine specifies the command line. The new process can use GetCommandLine to retrieve the entire command line. Console processes written in C can use the argc and argv arguments to parse the command line. Because argv[0] is the module name, C programmers generally repeat the module name as the first token in the command line.

    Ignore the bit about "C programmers"; it applies to everyone writing programs for Windows, regardless of the language.

    Your launcher is providing values for both the lpApplicationName and lpCommandLine parameters, but it is not following the convention of repeating the program file name as the first parameter in the command line. Delphi's ParamStr and ParamCount functions know to follow the convention, so they skip the first token on the command line. If the caller didn't follow the convention, then the receiver ends up thinking the intended second parameter is really the first, the third is really the second, and so on.