2

I want my console application to start another console application, display everything this another application wants to display then do something after this another application finishes and exit. Basically:

Writeln('Started');
ShellExecute(0, 'open', 'another.exe', nil, nil, SW_SHOWNORMAL);
Writeln('Finished');

So how can I show all the output from another console app in my console app? I don't want to capture output from another app. I just want another app to execute in the same command line window.

Tom
  • 2,962
  • 3
  • 39
  • 69
  • I hope you can find something about console output reading so as waiting for its exit. – Victoria Aug 23 '18 at 20:10
  • @Victoria Can't I just redirect processing to another exe? Like I would in a batch/bash script? – Tom Aug 23 '18 at 20:12
  • Possible duplicate of [Execute DOS program and get output dynamically](https://stackoverflow.com/questions/25723807/execute-dos-program-and-get-output-dynamically) – Graymatter Aug 24 '18 at 01:36
  • @Graymatter It's not a duplicate. Your link is to a different problem. – Tom Aug 24 '18 at 15:44
  • The accepted answer from David Heffernan does exactly what you are needing. It launches a ping, waits for it to finish and then writes out the results from the ping. – Graymatter Aug 24 '18 at 20:06
  • @Graymatter I don't want to capture output from another exe to my exe. I just want to run my exe, pause it, run another exe, close another exe, resume to my exe. And everything needs to happen in the same command line window. – Tom Aug 24 '18 at 22:15

1 Answers1

6

You might want to try something like this:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Windows, SysUtils;

var
  cl: string;
  SI: TStartupInfo;
  PI: TProcessInformation;

begin

  cl := 'C:\WINDOWS\System32\ping.exe 127.0.0.1';
  UniqueString(cl);

  try
    try
      writeln('begin');
      FillChar(SI, sizeof(SI), 0);
      FillChar(PI, sizeof(PI), 0);
      SI.cb := sizeof(SI);

      if not CreateProcess(nil, PChar(cl), nil, nil, true, 0, nil, nil, SI, PI) then
        RaiseLastOSError;

      WaitForSingleObject(PI.hProcess, INFINITE);

      CloseHandle(PI.hProcess);
      CloseHandle(PI.hThread);

      writeln('end');
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    Writeln('Complete');
    Readln;
  end;


end.
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • Thank you. It's exactly that- runs an app, lets it take the stdout and stdin, waits for it and quits. – Tom Aug 23 '18 at 20:28
  • @Tom: I just hope I am using the handles correctly. I don't do this on a daily basis, so I am a bit rusty... – Andreas Rejbrand Aug 23 '18 at 20:30
  • @Tom: I realised I complicated things unnecessarily. I simplified the code. – Andreas Rejbrand Aug 23 '18 at 21:09
  • 1
    Leaks two handles and passes a non modifiable string to second arg. That's an AV on Unicode Delphi. – David Heffernan Aug 23 '18 at 22:13
  • 1
    @David: Thank you very much for your input. The handle leaks were very embarrassing; I should have read the documentation (which is very clear on this point) more carefully before posting an A on a topic I am so rusty on. Regarding the second argument, I do know it must be writeable, and that's why I didn't use a string-literal constant. But maybe my approach is still not enough. Perhaps I do need something like `UniqueString`? (The code doesn't AV on any of the Unicode D. versions I tried it on, but I do trust you. Of course, "it works on my machine" very much doesn't prove code correctness.) – Andreas Rejbrand Aug 24 '18 at 05:47
  • It is a literal though, just referenced by the variable. Use UniqueString to make it writeable. You don't need the finally around WFSO. You could close the thread handle before calling WFSO but that would be major nitpicking. – David Heffernan Aug 24 '18 at 06:58
  • 1
    @DavidHeffernan: I know I don't need the `try..finally` just for `WaitForSingleObject`, but I chose to add it because a future developer might add some non-trivial Delphi code between the retrieval of the handles and the calls to `CloseHandle`, and `try..finally` blocks are free. I'll add `UniqueString`. Thanks! – Andreas Rejbrand Aug 24 '18 at 07:02
  • try finally blocks aren't free. They do incur a runtime cost. Not that it is significant. However, they also incur a cost of extra verbiage. – David Heffernan Aug 24 '18 at 07:07
  • @DavidHeffernan: In know they aren't completely without cost, but in this case, I think the cost is completely negligible in practice. Personally, I also like the visual grouping you get by `try..finally` blocks, but maybe that's just me. – Andreas Rejbrand Aug 24 '18 at 07:09
  • 1
    From a practical consideration here including them as you originally did is likely to tell the reader that WFSO can raise an exception. Which it can't. So I think didactically it is better not to risk suggesting that. – David Heffernan Aug 24 '18 at 07:21
  • @DavidHeffernan: I understand your point, but I am not sure I agree on the conclusion. To me `try..finally` is a basic pattern that I always use, even if the current version doesn't need it. The block tells me that I am protecting some kind of resource, and indicates visually where the resource is created, where it is 'freed', and how long it lives. And future additions to the code might make the block necessary in a true technical sense. But of course I do understand your point, which is very valid as well. – Andreas Rejbrand Aug 24 '18 at 07:32
  • @Andreas Rejbrand Thanks for your update. It is much easier now and works exactly like I needed. – Tom Aug 24 '18 at 22:16