Solved

Creating a front end for a dos program...

Posted on 2002-03-12
13
220 Views
Last Modified: 2010-08-05
I have a dos program that when run will sit there and give you status reports in the command window you opened it in.

I want to create a front end for this that will remove that command window..

Basically, instead of having a command window sitting there scrolling what its doing at you, I have an edit box or maybe just one line with what it's currently doing.

How do I go about calling the program without it opening up a command window and then displaying its output in my front end?

I assume I will be simply calling the program using ShellExecute and using sw_Hide as the ShowCmd..

But how do I get the constant stream it outputs as its status?

Thanks
0
Comment
Question by:Palamedes
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 4
  • 3
  • 3
  • +3
13 Comments
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6860032
Hi Palamedes, here's some code in my code repository... haven't really tested it personally, but it should work

{----------------------------CreateDOSProcessRedirected---------------------------
 Description    : executes a (DOS!) app defined in the CommandLine parameter redirected
                  to take input from InputFile and give output to OutputFile
 Result         : True on success
 Parameters     :
                  CommandLine : the command line for the app, including its full path
                  InputFile   : the ascii file where from the app takes input
                  OutputFile  : the ascii file to which the app's output is redirected
                  ErrMsg      : additional error message string. Can be empty
 Error checking : YES
 Target         : Delphi 2, 3, 4
 Author         : Theodoros Bebekis, email bebekis@otenet.gr
 Notes          :
 Example call   : CreateDOSProcessRedirected('C:\MyDOSApp.exe',
                                             'C:\InputPut.txt',
                                             'C:\OutPut.txt',
                                             'Please, record this message')
-----------------------------------------------------------------------------------}
function CreateDOSProcessRedirected(const CommandLine, InputFile, OutputFile, ErrMsg :string):boolean;
const
  ROUTINE_ID = '[function: CreateDOSProcessRedirected ]';
var
  OldCursor     : TCursor;
  pCommandLine  : array[0..MAX_PATH] of char;
  pInputFile,
  pOutPutFile   : array[0..MAX_PATH] of char;
  StartupInfo   : TStartupInfo;
  ProcessInfo   : TProcessInformation;
  SecAtrrs      : TSecurityAttributes;
  hAppProcess,
  hAppThread,
  hInputFile,
  hOutputFile   : THandle;
begin

  Result := False;

  { check for InputFile existence }
  if not FileExists(InputFile)
  then
    raise Exception.CreateFmt(ROUTINE_ID          + #10 +  #10 +
                              'Input file * %s *' + #10 +
                              'does not exist'    + #10 +  #10 +
                              ErrMsg, [InputFile]);

  { save the cursor }
  OldCursor     := Screen.Cursor;
  Screen.Cursor := crHourglass;

  { copy the parameter Pascal strings to null terminated strings }
  StrPCopy(pCommandLine, CommandLine);
  StrPCopy(pInputFile, InputFile);
  StrPCopy(pOutPutFile, OutputFile);

  TRY

    { prepare SecAtrrs structure for the CreateFile calls
      This SecAttrs structure is needed in this case because
      we want the returned handle can be inherited by child process
      This is true when running under WinNT.
      As for Win95 the documentation is quite ambiguous }
    FillChar(SecAtrrs, SizeOf(SecAtrrs), #0);
    SecAtrrs.nLength              := SizeOf(SecAtrrs);
    SecAtrrs.lpSecurityDescriptor := nil;
    SecAtrrs.bInheritHandle       := True;

    { create the appropriate handle for the input file }
    hInputFile := CreateFile(
                             pInputFile,                            { pointer to name of the file }
                             GENERIC_READ or GENERIC_WRITE,         { access (read-write) mode }
                             FILE_SHARE_READ or FILE_SHARE_WRITE,   { share mode }
                             @SecAtrrs,                             { pointer to security attributes }
                             OPEN_ALWAYS,                           { how to create }
                             FILE_ATTRIBUTE_TEMPORARY,              { file attributes }
                             0 );                                   { handle to file with attributes to copy }


    { is hInputFile a valid handle? }
    if hInputFile = INVALID_HANDLE_VALUE
    then
      raise Exception.CreateFmt(ROUTINE_ID                                                     + #10 +  #10 +
                                'WinApi function CreateFile returned an invalid handle value'  + #10 +
                                'for the input file * %s *'                                    + #10 + #10 +
                                ErrMsg, [InputFile]);

    { create the appropriate handle for the output file }
    hOutputFile := CreateFile(
                              pOutPutFile,                           { pointer to name of the file }
                              GENERIC_READ or GENERIC_WRITE,         { access (read-write) mode }
                              FILE_SHARE_READ or FILE_SHARE_WRITE,   { share mode }
                              @SecAtrrs,                             { pointer to security attributes }
                              CREATE_ALWAYS,                         { how to create }
                              FILE_ATTRIBUTE_TEMPORARY,              { file attributes }
                              0 );                                   { handle to file with attributes to copy }

    { is hOutputFile a valid handle? }
    if hOutputFile = INVALID_HANDLE_VALUE
    then
      raise Exception.CreateFmt(ROUTINE_ID                                                     + #10 +  #10 +
                                'WinApi function CreateFile returned an invalid handle value'  + #10 +
                                'for the output file * %s *'                                   + #10 + #10 +
                                ErrMsg, [OutputFile]);

    { prepare StartupInfo structure }
    FillChar(StartupInfo, SizeOf(StartupInfo), #0);
    StartupInfo.cb          := SizeOf(StartupInfo);
    StartupInfo.dwFlags     := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    StartupInfo.wShowWindow := SW_HIDE;
    StartupInfo.hStdOutput  := hOutputFile;
    StartupInfo.hStdInput   := hInputFile;

    { create the app }
    Result := CreateProcess(nil,                           { pointer to name of executable module }
                            pCommandLine,                  { pointer to command line string }
                            nil,                           { pointer to process security attributes }
                            nil,                           { pointer to thread security attributes }
                            True,                          { handle inheritance flag }
                            CREATE_NEW_CONSOLE or
                            REALTIME_PRIORITY_CLASS,       { creation flags }
                            nil,                           { pointer to new environment block }
                            nil,                           { pointer to current directory name }
                            StartupInfo,                   { pointer to STARTUPINFO }
                            ProcessInfo);                  { pointer to PROCESS_INF }

    { wait for the app to finish its job and take the handles to free them later }
    if Result
    then
      begin
        WaitforSingleObject(ProcessInfo.hProcess, INFINITE);
        hAppProcess  := ProcessInfo.hProcess;
        hAppThread   := ProcessInfo.hThread;
      end
    else
      raise Exception.Create(ROUTINE_ID          + #10 +  #10 +
                             'Function failure'  + #10 +  #10 +
                             ErrMsg);

  FINALLY
    { close the handles
      Kernel objects, like the process and the files we created in this case,
      are maintained by a usage count.
      So, for cleaning up purposes we have to close the handles
      to inform the system that we don't need the objects anymore }
    if hOutputFile <> 0 then CloseHandle(hOutputFile);
    if hInputFile <> 0 then CloseHandle(hInputFile);
    if hAppThread <> 0 then CloseHandle(hAppThread);
    if hAppProcess <> 0 then CloseHandle(hAppProcess);
    { restore the old cursor }
    Screen.Cursor:= OldCursor;
  END;

end;
0
 

Expert Comment

by:Rabster
ID: 6860257
Hi,
this is an alternative way to do it. It requires a TMemo, TButton and TEdit on a form. NOTE: Type the full path name to the executable.

procedure RunDosInMemo(DosApp:String;AMemo:TMemo);
const
  ReadBuffer = 2400;
var
  Security            : TSecurityAttributes;
  ReadPipe,WritePipe  : THandle;
  start               : TStartUpInfo;
  ProcessInfo         : TProcessInformation;
  Buffer              : Pchar;
  BytesRead           : DWord;
  Apprunning          : DWord;
begin
  With Security do
  begin
   nlength              := SizeOf(TSecurityAttributes);
   binherithandle       := true;
   lpsecuritydescriptor := nil;
  end;
  if Createpipe (ReadPipe, WritePipe,@Security, 0) then
  begin
    Buffer  := AllocMem(ReadBuffer + 1);
    FillChar(Start,Sizeof(Start),#0);
    start.cb          := SizeOf(start);
    start.hStdOutput  := WritePipe;
    start.hStdInput   := ReadPipe;
    start.dwFlags     := STARTF_USESTDHANDLES +
                        STARTF_USESHOWWINDOW;
    start.wShowWindow := SW_HIDE;
    if CreateProcess(nil,PChar(DosApp),@Security,
    @Security,true,NORMAL_PRIORITY_CLASS,nil,nil,
    start,ProcessInfo) then
    begin
      repeat
        Apprunning := WaitForSingleObject
        (ProcessInfo.hProcess,100);
       Application.ProcessMessages;
      until (Apprunning <> WAIT_TIMEOUT);
      repeat
        BytesRead := 0;
        ReadFile(ReadPipe,Buffer[0],
        ReadBuffer,BytesRead,nil);
        Buffer[BytesRead]:= #0;
        OemToAnsi(Buffer,Buffer);
        AMemo.Text := AMemo.text + String(Buffer);
      until (BytesRead < ReadBuffer);
    end;
  end;
  FreeMem(Buffer);
  CloseHandle(ProcessInfo.hProcess);
  CloseHandle(ProcessInfo.hThread);
  CloseHandle(ReadPipe);
  CloseHandle(WritePipe);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  Memo1.Clear;
  RunDosInMemo(Edit1.Text,Memo1);
end;
0
 
LVL 27

Expert Comment

by:kretzschmar
ID: 6860399
listening . . .
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 5

Expert Comment

by:Gwena
ID: 6861482
I'm listening too :-)
0
 
LVL 4

Author Comment

by:Palamedes
ID: 6862917
Okay.. I dinkered with both methods..

DragonSlayer your method works for me with one exception.. The front end completely freezes while the program is outputing to the output file.  This is bad.  I need it to still retain control.. As it currently stands.. if I wanted to exit the frontend (and thus terminate the dos program) I cant. =(  

What this also means is that there is no way to now use that output.txt file in real time.  As the dos program is outputing to it, my front end isnt updating.. (again because its frozen)

Good call .. thanks..  Close.. heheh

Rabster,  Your method is more along the lines of what I am trying to do, but sufferes the same problem.

When, for example, I enter "c:/command.com /c tracert www.yahoo.com" as in the text box it doesnt update every line as it comes back, but rather waits until the dos application is finsihed before updating the memo.

My dos program that I am trying to create a front end for DOESNT END.. heheh.. It will sit there and scroll it's status at you forever if you let it.. (Even if that status is "Finished.. you can terminate me now..")  (I know poor coding for this dos app.. but hey, I didnt write it..)


You guys are awesome!  Thanks for getting me what ya got me so far.. but I need this to go one step further..  (Name yer point cost! hehe)  

So.. to sum up..  How do I get it to update the TMemo in real time?

Thanks!  

0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6863226
Perhaps you could put all these in another thread?

That way, your main programme will continue working, and as for the thread that spawns this console app, it will only output something after getting a respond. To get more info on how to update the main UI from the secondary thread, check out the Synchronize function of TThread.

All the best!
0
 
LVL 4

Author Comment

by:Palamedes
ID: 6863656
Can you give me a hint as to how to go about doing that?  I'm not savvy on the threading thing..
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6863799
Don't have time to do that for you right now... I'll be away for about 3 days, in the meantime, I'll see what I can come up with... just hang in there, okay?
0
 

Expert Comment

by:Rabster
ID: 6864160
I will look into it a bit more. All the examples I have seen wait for the DOS app to finish but I'll investigate a little further.
0
 

Accepted Solution

by:
Rabster earned 150 total points
ID: 6864290
Hoorah, Maybe

Found a component which does exactly what you need. No point in me re-inventing the wheel because it would probably be square anyway:
Here is the link. It is freeware and the source is included.

http://uk.torry.net/vcl/system/tasks/doscommand.zip
0
 

Expert Comment

by:DelFreak
ID: 6871380
Listening...
0
 
LVL 14

Expert Comment

by:DragonSlayer
ID: 6875602
well, guess Rabster has found it for you :)
0
 
LVL 4

Author Comment

by:Palamedes
ID: 6876357
Yep thats the ticket.. Thanks Rab..
0

Featured Post

Enroll in June's Course of the Month

June’s Course of the Month is now available! Experts Exchange’s Premium Members, Team Accounts, and Qualified Experts have access to a complimentary course each month as part of their membership—an extra way to sharpen your skills and increase training.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
If you're a developer or IT admin, you’re probably tasked with managing multiple websites, servers, applications, and levels of security on a daily basis. While this can be extremely time consuming, it can also be frustrating when systems aren't wor…
In this video, viewers are given an introduction to using the Windows 10 Snipping Tool, how to quickly locate it when it's needed and also how make it always available with a single click of a mouse button, by pinning it to the Desktop Task Bar. Int…

688 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question