• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 225
  • Last Modified:

Creating a front end for a dos program...

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
Palamedes
Asked:
Palamedes
  • 4
  • 3
  • 3
  • +3
1 Solution
 
DragonSlayerCommented:
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
 
RabsterCommented:
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
 
kretzschmarCommented:
listening . . .
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
GwenaCommented:
I'm listening too :-)
0
 
PalamedesAuthor Commented:
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
 
DragonSlayerCommented:
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
 
PalamedesAuthor Commented:
Can you give me a hint as to how to go about doing that?  I'm not savvy on the threading thing..
0
 
DragonSlayerCommented:
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
 
RabsterCommented:
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
 
RabsterCommented:
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
 
DelFreakCommented:
Listening...
0
 
DragonSlayerCommented:
well, guess Rabster has found it for you :)
0
 
PalamedesAuthor Commented:
Yep thats the ticket.. Thanks Rab..
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

  • 4
  • 3
  • 3
  • +3
Tackle projects and never again get stuck behind a technical roadblock.
Join Now