Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Execute background program and continue execution of code?

Tags:

delphi

I want to run a background application from my Delphi code. The application that opens is a DOS based EXE which outputs stuff to the DOS window. The program will be open indefinitely until it is closed from task manager. The current code I use to open the application is;

procedure CaptureConsoleOutput(const ACommand, AParameters: String; CallBack: TArg<PAnsiChar>);
const
    CReadBuffer = 2400;
var
    saSecurity: TSecurityAttributes;
    hRead: THandle;
    hWrite: THandle;
    suiStartup: TStartupInfo;
    piProcess: TProcessInformation;
    pBuffer: array [0 .. CReadBuffer] of AnsiChar;
    dBuffer: array [0 .. CReadBuffer] of AnsiChar;
    dRead: DWord;
    dRunning: DWord;
begin
    saSecurity.nLength := SizeOf(TSecurityAttributes);
    saSecurity.bInheritHandle := True;
    saSecurity.lpSecurityDescriptor := nil;

    if CreatePipe(hRead, hWrite, @saSecurity, 0) then
    begin
        FillChar(suiStartup, SizeOf(TStartupInfo), #0);
        suiStartup.cb := SizeOf(TStartupInfo);
        suiStartup.hStdInput := hRead;
        suiStartup.hStdOutput := hWrite;
        suiStartup.hStdError := hWrite;
        suiStartup.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
        suiStartup.wShowWindow := SW_HIDE;

        if CreateProcess(nil, pChar(ACommand + ' ' + AParameters), @saSecurity, @saSecurity, True, NORMAL_PRIORITY_CLASS, nil, nil, suiStartup, piProcess) then
        begin
            repeat
                dRunning := WaitForSingleObject(piProcess.hProcess, 100);
                //Application.ProcessMessages();
                repeat
                    dRead := 0;
                    ReadFile(hRead, pBuffer[0], CReadBuffer, dRead, nil);
                    if(dRead > 0) then
                    begin
                      pBuffer[dRead] := #0;
                      //ShowMessage(pBuffer);
                      //OemToAnsi(pBuffer, pBuffer);
                      //Unicode support by Lars Fosdal
                      OemToCharA(pBuffer, dBuffer);
                      CallBack(dBuffer);
                    end;
                until (dRead < CReadBuffer);
            until (dRunning <> WAIT_TIMEOUT);
            CloseHandle(piProcess.hProcess);
            CloseHandle(piProcess.hThread);
        end;
        CloseHandle(hRead);
        CloseHandle(hWrite);
    end;
end;

This is good, however because the program doesn't 'quit' and stays open forever, my application hangs, and the code never moves along.

Any help would be appreciated

like image 808
Jake Evans Avatar asked Aug 08 '14 21:08

Jake Evans


People also ask

How to execute program in background?

Execute in the background: To execute in the background, instead of left- clicking on the execute icon, use the menu path Program > Execute in background.

How to make program run in background unix?

You can easily send such commands to the background by hitting the Ctrl + Z keys and then using the bg command. Hitting Ctrl + Z stops the running process, and bg takes it to the background. You can view a list of all background tasks by typing jobs in the terminal. Use the fg command to get back to the running task.


2 Answers

If you don't need to do anything with the spawned process, you can simply close the handles that CreateProcess() returned and move on, the process will keep running. But since you appear to need to read continuously from the output of the spawned process, you can simply move that logic into a worker thread so your main code is not blocked anymore.

like image 200
Remy Lebeau Avatar answered Oct 01 '22 18:10

Remy Lebeau


//You have to put your conde inside a thread. For example:

//Let's suppose you want to ping Google Server. You'll have to create a thread just like //below in your interface section inside Delphi. You'll get this done by choosing File-New-//Other-Thread Object, Then name your thread as you want.

type TPing = class(TThread) private { Private declarations } protected procedure Execute; override; end;

//Copy your CaptureConsoleOutput procedure onto Thread unit.

//inside Execute procedure, call your CaptureConsoleOutput procedure. //In my case, I just capture the output directly in the Memo component.

//That worked just fine for me !

procedure TPing.Execute; begin Priority := tpLower; CaptureConsoleOutput('ping www.yahoo.com', '-t', Form1.Memo2); end;

like image 40
Android Hunter Avatar answered Oct 01 '22 17:10

Android Hunter