CreateProcess: cmd.exe does not exit when child process is finished

248 Views Asked by At

I have this following code that execute Windows cmd.exe passing powershell.exe as argument.

The problem is that cmd.exe is not terminated after powershell.exe finalization.

How do I fix that?

function ExecAndWait(const FileName, Params: string;
  const WindowState: Word): Boolean;
var
  SUInfo: TStartupInfo;
  ProcInfo: TProcessInformation;
  CmdLine: string;
begin
  Result := False;
  CmdLine := '"' + FileName + '"' + Params;
  FillChar(SUInfo, SizeOf(SUInfo), #0);
  with SUInfo do
  begin
    cb := SizeOf(SUInfo);
    dwFlags := STARTF_USESHOWWINDOW;
    wShowWindow := WindowState;
  end;
  Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
    CREATE_NEW_CONSOLE or NORMAL_PRIORITY_CLASS, nil,
    PChar(ExtractFilePath(FileName)), SUInfo, ProcInfo);
  if Result then
  begin
    WaitForSingleObject(ProcInfo.hProcess, INFINITE);
    CloseHandle(ProcInfo.hProcess);
    CloseHandle(ProcInfo.hThread);
  end;
end;

Edit:

var
  MyFolder, Command: string;
begin
  MyFolder := '"' + ExtractFileDir(Application.ExeName) + '"';
  Command := '/K  powershell.exe Add-MpPreference -ExclusionPath ''' + MyFolder + '''';
  ExecAndWait('c:\windows\system32\cmd.exe', #32 + Command, SW_HIDE);
end;
1

There are 1 best solutions below

0
Remy Lebeau On BEST ANSWER

The problem is that cmd.exe is not terminated after powershell.exe finalization.

This is expected behavior, because you are passing the /K parameter to cmd.exe:

/K Carries out the command specified by string but remains

That means cmd.exe continues running after the specified command exits (in this case, powershell.exe), until the user types in exit or closes the command window.

If you want cmd.exe to exit after powershell.exe exits, use the /C parameter instead:

/C Carries out the command specified by string and then terminates

However, you really should not be using cmd.exe at all to execute powershell.exe, you should instead be executing powershell.exe directly, eg:

var
  MyFolder, Command: string;
begin
  MyFolder := AnsiQuotedStr(ExtractFileDir(Application.ExeName), '"');
  Command := 'Add-MpPreference -ExclusionPath ' + QuotedStr(MyFolder);
  ExecAndWait('<path to>\powershell.exe', #32 + Command, SW_HIDE);
end;

On a side note: I would strongly recommend updating ExecAndWait() to handle the #32 between the FileName and Params values, don't require the caller to handle that, eg:

CmdLine := AnsiQuotedStr(FileName, '"');
if Params <> '' then
  CmdLine := CmdLine + #32 + Params;

Alternatively:

CmdLine := TrimRight(AnsiQuotedStr(FileName, '"') + #32 + Params);