Link to home
Start Free TrialLog in
Avatar of sandman_br
sandman_br

asked on

Running DOS programs from a Delphi app

Can I run a DOS program from inside my Delphi app in such a way that my program waits for the DOS program to end (something like a "modal" DOS program)?
ASKER CERTIFIED SOLUTION
Avatar of inthe
inthe

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I've used a simple approach with success.  Use FileExecute() found in the FMXUtils.pas that ships with Delphi.
Here's an example:
ExecuteFile ('gencri.exe',,SW_SHOWMINIMIZED);

You can put command line parameters for your DOS app between the two commas.

REFERENCE: From Delphi 3 Help under FileExecute -
"Many times an application needs to execute another application, either to perform a specific task or just to have that application run concurrently. The Windows API provides a function, ShellExecute, that executes an application, but it requires a number of parameters superfluous to Delphi applications. The FMXUtils unit provides a more useful alternative, called ExecuteFile.
ExecuteFile operates in two different ways. If passed the name of an executable file, ExecuteFile runs that application. If passed the name of a document with an associated application, ExecuteFile runs the application, automatically opening that document at startup."

Hope this helps.

Avatar of inthe
inthe

martin_q
 the whole point was to execute and wait. try running your way and putting close statement after execute file,sure it opens the file but it dont wait.(or at least i cant get it to)
executefile is calling shellexecute as you say but shellexecute is calling createprocess so why not go straight to createprocess
and tweak a bit to get dos prog to wait like in code above.
please correct me if im wrong  :-)
Regards Barry
Hi Barry,

your sources look nice, but not enough (I'm a perfectionist). The problem with your sources is that your application doesn't process messages while waiting on the dos program. That means that the main form isn't repainted anymore and all that stuff.
So here comes my solution (thread based) that solves this problem, too.

sandman_br, my sources copies the "autoexec.bat" file, and waits until this is ready. Then you'll find the output text in 'c:\output.txt'. Furthermore the dos window is hidden. If you don't like that, just remove the line:
  si.dwFlags:=STARTF_USESHOWWINDOW; si.wShowWindow:=SW_HIDE;

Regards, Madshi.

unit runThread_;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, tools, ddraw;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen}
  public
    { Public-Deklarationen}
  end;

var Form1 : TForm1 = nil;

implementation

{$R *.DFM}

const unitName = 'runThread_.';

type TRunThread = class(TThread)
  private
    processHandle : cardinal;
    processReady  : boolean;
    waitingThread : cardinal;
    procedure Execute; override;
  end;

procedure TRunThread.Execute;
begin
  WaitForSingleObject(processHandle,INFINITE);   // This call does not return, unless copy is stopped
  processReady:=true;                            // Set "processReady" flag for main thread
  PostThreadMessage(waitingThread,WM_NULL,0,0);  // Wake up main thread
                                                 // If you call Application.HandleMessage (see below) in the
                                                 // main thread, the main thread is sleeping the most time in
                                                 // winAPI "waitMessage". So we send a "dummy" message in order
                                                 // to let the main thread return from Application.HandleMessage
end;

procedure TForm1.Button1Click(Sender: TObject);
var si : TStartupInfo;
    pi : TProcessInformation;
begin
  caption:='start copy...';
  ZeroMemory(@si,sizeOf(si)); si.cb:=sizeOf(si);
  si.dwFlags:=STARTF_USESHOWWINDOW; si.wShowWindow:=SW_HIDE;
  if CreateProcess(nil,'c:\command.com /c copy c:\autoexec.bat c:\test.bat >c:\output.txt',nil,nil,false,0,nil,nil,si,pi) then begin
    caption:='copy started...';
    with TRunThread.Create(true) do         // create the thread object, but do not start it now...
      try
        processHandle:=pi.hProcess;         // tell the thread what process it has to watch
        processReady:=false;                // flag for the loop (see below)
        waitingThread:=GetCurrentThreadID;  // the current threadID for the wakeup message (see above)
        caption:='wait for copy...';
        Resume;                             // now all information is prepared; so let's start the thread
        repeat
          Application.HandleMessage;        // message loop
        until Application.Terminated or processReady;  // continue with normal program when either the
                                                       // started process has stopped or our program is closed
        caption:='copy stopped...';
      finally Free end;
    CloseHandle(pi.hThread); CloseHandle(pi.hProcess); // Never forget to close handles...
    caption:='ready...';
  end else caption:='could not start copy...';
end;

end.
Avatar of sandman_br

ASKER

Hi guys,
Barry's code worked great. My app's main window won't get repainted until the DOS program ends, as Madshi pointed, but I can live with that :) Madshi, I tried your code too, but the app was still accessible to the user while the DOS prog was running... but thanks for your efforts.
Thanks everyone!

Best regards,
Roger
Hmmm. If you want your main window to be disabled, you just need to call "form1.enabled:=false" before starting the dos program and "form1.enabled:=true" after coming back...
Hi Madshi,
Thanks for the last comment, that finishing touch makes your solution perfect. I'm just a beginner in Delphi and had not realized I could use that... thanks a lot!
No problem...   :-)
yes thanks for your input madshi the more ways to know how to do something the better then we can use all the best bits and have perfection eh :-)
Regards Barry
:-)  perfection... Yeah!!   :-)
hi, inthe
I used your code in Delphi 3, it work Ok. But In Delphi 4, "GetExitCodeProcess(ProcessInfo.hProcess,Result)" could not be compiled. The Error Messsage: Types of actual and formal var parameters must be identical My Delphi version is Version 4.0(Build 5.104) Update Pack 2. why? :-(

Regards,
SupWang
SupWang,

change the result type of the function "WinExecAndWait32" to cardinal.
In D3, integer and cardinal were almost identical. In D4, they are incompatible.

Regards, Madshi.
hi all
thanks madshi ,just got in and seen comment
we have to be more careful now with all different versions of delphi being slightly different eh.
Regards Barry
Hi, Madshi
Thanks :-)
Regards,SupWang

No prob...  :-)

I really like it that D4 has no a "real" cardinal type, but unfortunately that makes some problems, too...

Regards, Madshi.