Solved

Creating a front end for a dos program...

Posted on 2002-03-12
13
206 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
  • 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
 
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
Top 6 Sources for Identifying Threat Actor TTPs

Understanding your enemy is essential. These six sources will help you identify the most popular threat actor tactics, techniques, and procedures (TTPs).

 
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

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Introduction The parallel port is a very commonly known port, it was widely used to connect a printer to the PC, if you look at the back of your computer, for those who don't have newer computers, there will be a port with 25 pins and a small print…
Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
This video explains how to create simple products associated to Magento configurable product and offers fast way of their generation with Store Manager for Magento tool.

707 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now