sfrazor 3 Posted July 8, 2023 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
FPiette 383 Posted July 8, 2023 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
aehimself 396 Posted July 8, 2023 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
sfrazor 3 Posted July 8, 2023 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
FPiette 383 Posted July 8, 2023 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
Der schöne Günther 316 Posted July 9, 2023 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