ShellExecuteEx 7z Delphi

4.6k views Asked by At

So I'm trying to do a archive using delphi and ShellExecuteEx my code is :

 Result := False;
  DecodeDate(now,y,m,d);
  NumeFisier := dir+'\Export_'+IntToStr(y)+'.'+IntToStr(m)+'.'+IntToStr(d)+'.zip';
  FillChar(exInfo, SizeOf(exInfo), 0);
  with exInfo do
   begin
    cbSize := SizeOf(exInfo);
    fMask := SEE_MASK_NOCLOSEPROCESS or SEE_MASK_FLAG_DDEWAIT;
    Wnd := GetActiveWindow();
    exInfo.lpVerb := nil;
    exInfo.lpFile  := PAnsiChar('C:\Windows\System32\cmd.exe');
   exInfo.lpParameters := PAnsiChar('C:\Program Files\7-Zip\7z.exe ' +'a ' + NumeFisier + ' ' + dir);
    nShow := SW_SHOWNORMAL;
   end;
   if ShellExecuteEx(@exInfo) then
    Ph := exInfo.hProcess
    else
     begin
     ShowMessage(SysErrorMessage(GetLastError));
     Result := true;
     exit;
    end;
   while WaitForSingleObject(exInfo.hProcess, 50) <> WAIT_OBJECT_0 do
     Application.ProcessMessages;
   CloseHandle(Ph);

  Result := true;

For some reason this only opens the Command Prompt and doesn't execute the archiving. How can I make it execute the 7z.exe file.

I tried with ShellExecute and it works great, but I have to check then the process is finished, so I'm stuck with ShellExecuteEx

1

There are 1 answers

9
David Heffernan On BEST ANSWER

There's no need to involve cmd.exe. That's the command interpreter. You want to execute a different executable so do that directly.

You don't want to use ShellExecuteEx since that has far more generality than you need. All that ShellExecuteEx is doing here is calling CreateProcess. You should do that directly and avoid the middle man. What's more, calling CreateProcess allows you to hide the console window easily. Pass CREATE_NO_WINDOW to achieve that.

Finally, there are better ways to wait than your code. Using MsgWaitForMultipleObjects allows you to avoid polling. And putting this code into a thread would allow you to avoid calls to Application.ProcessMessages.

procedure WaitUntilSignaled(Handle: THandle; ProcessMessages: Boolean);
var
  retval: DWORD;
begin
  if ProcessMessages then begin
    Application.ProcessMessages;//in case there are messages already in the queue
    while True do begin
      retval := MsgWaitForMultipleObjects(1, Handle, False, INFINITE, QS_ALLEVENTS);
      case retval of
      WAIT_OBJECT_0,WAIT_ABANDONED_0:
        break;
      WAIT_OBJECT_0+1:
        Application.ProcessMessages;
      WAIT_FAILED:
        RaiseLastOSError;
      end;
    end;
  end else begin
    Win32Check(WaitForSingleObject(Handle, INFINITE)<>WAIT_FAILED);
  end;
end;

procedure ExecuteProcess(
  const ExecutablePath: string;
  const Arguments: string;
  const CurrentDirectory: string;
  const Wait: Boolean;
  const CreationFlags: DWORD
);
var
  si: TStartupInfo;
  pi: TProcessInformation;
  MyCurrentDirectory: PChar;
begin
  ZeroMemory(@si, SizeOf(si));
  si.cb := SizeOf(si);

  if CurrentDirectory <> '' then begin
    MyCurrentDirectory := PChar(CurrentDirectory);
  end else begin
    MyCurrentDirectory := nil;
  end;

  Win32Check(CreateProcess(
    nil,
    PChar('"' + ExecutablePath + '" ' + Arguments),
    nil,
    nil,
    False,
    CreationFlags,
    nil,
    MyCurrentDirectory,
    si,
    pi
  ));
  try
    if Wait then begin
      WaitUntilSignaled(pi.hProcess, True);
    end;
  finally
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
  end;
end;