Jump to content
sfrazor

Createprocess won't die

Recommended Posts

Spending time on a function to ping continuously I noticed that I can't seem to kill the proccess I started.  I googled around a bit and copied this peice of code to learn with and noticed that if a command ends properly all is well.   I thought I knew how and I've tried a few things.   You'll notice the lines commented out of the various things I tried.

 

The end goal is to collect the ping  results until a time expires.  But I never got that far.  In task manager, after each of he attempts to terminate the ping, it shows to be still running.  What am I doing wrong? 

procedure GetDosOutput;
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: PAnsiChar;
  BytesRead: Cardinal;
  WorkDir: string;
  Handle: Boolean;
  CommandLine: string;
  Output: String;
  taskkill: string;
begin
CommandLine:='c:\windows\system32\cmd.exe /c ping -t 127.0.0.1';
Buffer:=AllocMem(128000);
  Output := '';
  with SA do begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0);
  try
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE);
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;

    PI:= Default(TProcessInformation);
    UniqueString(CommandLine);

    Handle := CreateProcess(nil, PWIdeChar(CommandLine),
                            nil, nil, True, 0, nil,
                            nil, SI, PI);
    CloseHandle(StdOutPipeWrite);
    if Handle then
      try
        repeat
          WasOK := ReadFile(StdOutPipeRead, Buffer, 128000, BytesRead, nil);
          if BytesRead > 0 then
          begin
            Buffer[BytesRead] := #0;
             Output := Output + Buffer;
          end;
        until not WasOK or (BytesRead = 0);
        WaitForSingleObject(PI.hProcess, 5000);
      finally
      //TerminateProcess(PI.hProcess,0);
      //CloseHandle(PI.hThread);
      //CloseHandle(PI.hProcess);
      end;
  finally
    CloseHandle(StdOutPipeRead);
  end;
//  taskkill:=  '/C taskkill /f /t  /PID = '+ INtToStr(PI.dwProcessId);
//  ShellExecute(0, nil, 'cmd.exe',pWideChar(taskkill), nil, SW_HIDE);
//  taskkill:=  '/C taskkill /f /t  /PID = '+ INtToStr(PI.hProcess);
//   ShellExecute(0, nil, 'cmd.exe',pWideChar(taskkill), nil, SW_HIDE);
   FreeMem(Buffer);
end;

 

 

Share this post


Link to post

This won't answer your question directly, but why not simply use TPing component from ICS inside your application?

ICS has several demos for the component: OverbyteIcsPingTst, OverbyteIcsPingTst64 and OverbyteIcsConPing.

You can download open source ICS from https://wiki.overbyte.be and also directly from Delphi IDE (GetIT menu).

If you need help, there is a dedicated forum on this server: https://en.delphipraxis.net/forum/37-ics-internet-component-suite/

Share this post


Link to post

PING -t will continue to ping until you manually terminate it (usually Ctrl-C in your window). The code above does what it is told - read until the process ends; but the process will never die due to the -t switch.

As your main thread is stuck in this loop you have but a handful of options:

- Introduce a counter in the cycle. Exit the repeat-until cycle when the counter reaches a specific amount

- Start a secondary thread before the loop, passing the process handle to it. The secondary thread can then kill the process at any time, causing the loop to break

- You also can use a timer but you have to inject a message pump in your inner loop

- Move this method to a thread and spam it across with "If Self.Terminated Then Exit". Start your thread and kill it any time from your main application with mythread.Terminate

 

I'd go with option 4 as that is going to leave the UI useable during execution.

Share this post


Link to post

Thanks FPiette.  I do use the ICS and Indy components.  Ping -t is just a simple example.  The end goal would be to kill a stuck process that I started after x seconds/minutes and capture the output until that time.  There is a couple of good DOS components that are overkill, but I don't learn that way.  🙂

 

The WaitForSIngleObject is set for 5 seconds.  When it expires it falls through like I would expect and the taskkill, closehandle etc get executed as I would think.  But still the ping -t doesn't die.  

I'm using ping -t as an example but other continuous commands suffer the same.   What I don't understand is, even when the taskkill or closehandle get executed the procerss doesn't die.  I don't directly have the PID of ping so I'm missign something.  From what I can tell,  the dwProcessID is the PID of the cmd.exe that is created but when that is killed, the ping keeps going even though the parent is gone.  

 

Maybe there is a better way to careate a process, monitor it and kill it after a time, if its still running.  

 

 

 

 

Share this post


Link to post
33 minutes ago, sfrazor said:

The end goal would be

So you ask X with the intent of solving issue Y that you don't explicit.

 

Quote

Maybe there is a better way to careate a process, monitor it and kill it after a time, if its still running. 

  Instead of launching cmd.exe, just start ping.exe and kill it when you want.

 

 

Share this post


Link to post

Change your

CommandLine:= 'c:\windows\system32\cmd.exe /c ping -t 127.0.0.1';

to

CommandLine:= 'ping -t 127.0.0.1';

and your ping.exe will properly be terminated. Right now, you are killing cmd.exe which, in turn launched another ping.exe. Of course ping.exe will still be around after cmd.exe got killed.

 

Also, TerminateProcess(..) should always be a matter of last resort. Properly terminating the ping -t command is, as you've found out, sending a [ctrl] + [c] event. You can do that from your own app: GenerateConsoleCtrlEvent function - Windows Console | Microsoft Learn

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×