Search code examples
delphishellexecute

TShellExecuteInfo lpParameters and ">" symbol


This code calls Sqlite3.exe to do a database backup, but its not working because of the ">" symbol in the parameters. Can you tell me how to fix it?

procedure TfAdmin.DoDBBackup(ADBBackupFile: String);
var
  b, p, q: String;
  ps: TShellExecuteInfo;
begin

  b := ExtractFilePath(ParamStr(0)) + 'PPDB.bak';
  p := ExtractFilePath(ParamStr(0)) + 'sqlite3.exe';
  q := ExtractFilePath(ParamStr(0)) + 'PPDB.db .dump > ' + b;  //here lies the problem

  ShowMessage(p + ' ' + q);
  fMain.UniConnection1.Close;
  try
    // Execute process and wait for it to terminate
    FillChar(ps, SizeOf(ps), 0);
    ps.cbSize := SizeOf(ps);
    ps.Wnd := Handle;
    ps.lpVerb := Nil;
    ps.fMask := SEE_MASK_FLAG_NO_UI or SEE_MASK_NOCLOSEPROCESS;
    ps.lpFile := PChar(p);
    ps.lpParameters := PChar(q);
    ps.nShow := SW_SHOWNORMAL;

    if not ShellExecuteEx(@ps) then
      RaiseLastOSError;

    if ps.hProcess <> 0 then
    begin
      while WaitForSingleObject(ps.hProcess, 50) = WAIT_TIMEOUT do
        application.ProcessMessages;
      CloseHandle(ps.hProcess);
    end;
  finally
    fMain.UniConnection1.Open;
  end;
end;

Solution

  • The > symbol instructs the command interpreter (cmd.exe) to redirect the output from the executable to a file. That works when the command interpretor is running the show. But here, there is no command interpretor.

    There are a couple of options for you. A quite simple approach would be to ask the command interpretor to do the work. Use cmd.exe as your executable, pass the /C argument, and then the rest of the command line. If you want to be a good citizen, then use the value of the COMSPEC environment variable rather than hard coding cmd.exe.

    A more grown up solution is to abandon the shell. Instead call CreateProcess directly. This is a little more involved. You have to create a file handle by calling CreateFile. Pass that handle to CreateProcess as the standard output file handle for the new process. You need to make sure that handles are inherited when you call CreateProcess.

    One final point to make is that I really don't like your wait loop. You'd be far better off using MsgWaitForMultipleObjects to block until queued messages arrive.