3

I want to achieve the following with a CreateProcess() - call:

  1. Change to an svn working-copy
  2. Execute svn commands
  3. Pipe the output to a file

I try this with the following function

procedure TQPortMainForm.CmdMigrationClick(Sender: TObject);
var
  StartInfo: TStartupInfo;
  ProcInfo: TProcessInformation;
  CreateOk: boolean;
  input: String;
begin
  { fill with known state }
  FillChar(StartInfo, SizeOf(TStartupInfo), #0);
  FillChar(ProcInfo, SizeOf(TProcessInformation), #0);
  StartInfo.cb := SizeOf(TStartupInfo);

  //debug
  input := 'D: && cd D:\Qport\trunk\Qport\ && ' + SVN_PATH + ' log > C:\users\PhilippKober\UNIQUE_NAME_BLUB.txt';

  CreateOk := CreateProcess(nil, PChar(input), nil, nil, false, CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil,
    nil , StartInfo, ProcInfo);
  { check to see if successful }
  if CreateOk then
    // may or may not be needed. Usually wait for child processes
    WaitForSingleObject(ProcInfo.hProcess, INFINITE);
end;

Simply nothing happens. Has anybody got an idea how to achieve this?

Thanks,

Philipp

EDIT 1: I am using Delphi XE - Build 7601: Service Pack 1

EDIT 2: Here is the solution:

var
  StartInfo: TStartupInfo;
  ProcInfo: TProcessInformation;
  CreateOk: boolean;
  input: String;
  path : String;
  cmd : String;
begin
  { fill with known state }
  FillChar(StartInfo, SizeOf(TStartupInfo), #0);
  FillChar(ProcInfo, SizeOf(TProcessInformation), #0);
  StartInfo.cb := SizeOf(TStartupInfo);

  path := 'D:\Qport\trunk\Qport\';
  cmd := 'C:\Windows\System32\cmd.exe';
  //debug
  input := '/C' + SVN_PATH + ' help > C:\users\PhilippKober\UNIQUE_NAME_BLUB.txt';

  CreateOk := CreateProcess(PChar(cmd), PChar(input), nil, nil, false, CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil,
     Pchar(path), StartInfo, ProcInfo);
  { check to see if successful }
  if CreateOk then
    // may or may not be needed. Usually wait for child processes
    WaitForSingleObject(ProcInfo.hProcess, INFINITE);
end;
skylla
  • 464
  • 3
  • 7
  • 17

1 Answers1

4

You need to supply an executable file when you call CreateProcess. I guess you are used to calling ShellExecute which is more lax.

You are clearly looking to call cmd.exe so you should add that to the command line. Rather than changing the working directory after cmd.exe has started, use the lpCurrentDirectory parameter of CreateProcess to do that. You will also need to pass the /C option to cmd.exe to make it close once the command has completed.

So you need to change input to be this:

input := GetEnvironmentVariable('COMSPEC') + ' /C ' + SVN_PATH + 
  ' log > C:\users\PhilippKober\UNIQUE_NAME_BLUB.txt';

I use GetEnvironmentVariable('COMSPEC') as a means to obtain the path to the command interpretor.

And then call CreateProcess like this:

CreateProcess(
  nil, 
  PChar(input), 
  nil, 
  nil, 
  False, 
  CREATE_NEW_PROCESS_GROUP or NORMAL_PRIORITY_CLASS, 
  nil,
  'D:\Qport\trunk\Qport', 
  StartInfo, 
  ProcInfo
);

It is semantically cleaner to use or to combine flags than +, although it has the same effect for these flags.

One thing to watch out for is that the second parameter must point to writeable memory. That's because CreateProcess may modify that parameter. As it happens, your setting of input will meet that requirement. In any case, a call to UniqueString would be recommended in my view to make it explicit that you are meeting that requirement.

The other thing I see that is missing is code to close the handles that are returned by CreateProcess. Close those handles by doing this in the end:

//WaitForSingleObject(ProcInfo.hProcess, INFINITE); //in case you want to wait for Process to terminate
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
dialex
  • 2,706
  • 8
  • 44
  • 74
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • @David: It is a bad idea to use the first parameter of CreateProcess! You should put all into the second parameter in order to have a "valid" command line argument for the started process. See documentation of `CreateProcess`... – Jochen Kalmbach Aug 02 '13 at 11:36
  • @JochenKalmbach: The special case of batch execution trough cmd.exe, the documentation suggest to use the first parameter with cmd.exe. "To run a batch file, you must start the command interpreter; set lpApplicationName to cmd.exe and set lpCommandLine to the following arguments: /c plus the name of the batch file." (http://goo.gl/9y0gw) – yucer Jul 06 '14 at 12:06