problems calling a DOS app from Delphi and capturing output

This really is a dos app .. not a console app. It is Wordnet and Quickview says that it is a dos executable.

I am using the code below, which works just fine for console apps.

It works OK for a simple dos test app and some of the time it works OK for Wordnet .. I am passing through stuff on the commandline.

Under W98 it attempts to access the floppy when I call a DOS app, and I can see no explanation for this. I AM using short pathnames. A: is not on the path. This happens irrespective of which dos app I am calling (even command/c dir *.* on W98).

So, maybe the attempted access to floppy on W98 is a clue (by tracking through the code it happens at the CreateProcess call). It does not do this on XP. Rebooting on W98 does not help.

The problem with calling Wordnet from Delphi is that it sometimes returns empty stuff. I can send it valid command line stuff, stuff that works when run from DOS and sometimes is OK when called in the Delphi call, and I get this non output.

I figured it might be something to do with handles or timing, but I can construct up a very heavy stress test and run it under DOS (ie a bunch of direct calls to Wordnet in a DOS batch file )and it works OK. So I don't think Wordnet itself is the culprit.

btw, I tried calling a batch file (from my Delphi app) that then invoked Wordnet . Same result as calling the app direct.

So, is there something wrong with my code ?

----------------------------------------------------------------------
procedure RunConsoleApp2Strings(const DosOrConsoleApp:String;
                                 const Args:String;
                                 const outStrings:Tstrings);

 const
    ReadBuffer = 2400;
 var
  Security            : TSecurityAttributes;
  ReadPipe,WritePipe  : THandle;
  start               : TStartUpInfo;
  ProcessInfo         : TProcessInformation;
  Buffer              : Pchar;
  BytesRead           : DWord;
  Apprunning          : DWord;
var
  app : string;
 begin


  app := sysutils.trim(DosOrConsoleApp);

   if args <> '' then
   app := app+' ' + Args;

  With Security do begin
   nlength              := SizeOf(TSecurityAttributes);
   binherithandle       := true;
   lpsecuritydescriptor := nil;
  end;
   // create pipes for stdin and stdout
  if Createpipe (ReadPipe, WritePipe,     //  1 pipe, 2 handles
                 @Security, 0) then begin
   Buffer  := AllocMem(ReadBuffer + 1);
   FillChar(Start,Sizeof(Start),#0);
   start.cb          := SizeOf(start);
   // setup pipes for stdin and stdout
   start.hStdOutput  := WritePipe;
   start.hStdInput   := ReadPipe;

   start.dwFlags     := STARTF_USESTDHANDLES
                          +
                        STARTF_USESHOWWINDOW

                        ;
   start.wShowWindow := SW_HIDE;

   if CreateProcess     (nil,
          PChar(app),  // should normally be a FULL command line
          @Security,
          @Security,
          true,           // new process inherits handles from the calling process
          CREATE_NEW_CONSOLE+       // JA based on examination of CodeCentral code
          NORMAL_PRIORITY_CLASS,   // creation flags and process priority
          nil,   // environment (if different from calling process)
          nil,   // current drive and directory for the new process
          start,       // Startup info
          ProcessInfo  // ProcessInfor
          )
   then
   begin

    repeat
     Apprunning := WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
     application.processmessages;
     until (Apprunning <> WAIT_TIMEOUT);


     Repeat
       BytesRead := 0;
       ReadFile(ReadPipe,Buffer[0],
                  ReadBuffer,BytesRead,nil);
       Buffer[BytesRead]:= #0;
       OemToAnsi(Buffer,Buffer);
       outStrings.Text := outStrings.text + String(Buffer);
     until (BytesRead < ReadBuffer);
  end;
  FreeMem(Buffer);

  // free handles for the spawned process and the thread
  CloseHandle(ProcessInfo.hProcess);
  CloseHandle(ProcessInfo.hThread);

  // the pipe handles need to be freed

  CloseHandle(ReadPipe);
  CloseHandle(WritePipe);
  end;
 end;

Mutley2003Asked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Wim ten BrinkSelf-employed developerCommented:
What if you just call your DOS application from outside Delphi? Just from the command prompt. It could be that WordNet is just always trying to access the A: drive.

http://www.arts.cuhk.edu.hk/humftp/Linguistics/wordnet/readme.pc Is this the WordNet that you are talking about? Can't you use a newer version instead?
0
Mutley2003Author Commented:
hi Alex

wordnet 1.6 is the latest one there is.

the problems with hunting the A drive occur with other dos apps if I call them from my code

I can call Wordnet from the command line and redirect output to a file, but that is a lot of work since I want to "follow up" .. one word suggests another etc.

can you see anyhting wrong with my code?

regards

0
BobcsiCommented:
Try this:


procedure GetDosOutput(const Exe,Param:string;Amemo:TMemo);
var
  SA: TSecurityAttributes;
  SI: TStartupInfo;
  PI: TProcessInformation;
  StdOutPipeRead, StdOutPipeWrite: THandle;
  WasOK: Boolean;
  Buffer: array[0..255] of Char;
  BytesRead: Cardinal;
  WorkDir, Line: String;
begin
  Amemo.Lines.Clear;
  Application.ProcessMessages;
  with SA do
  begin
    nLength := SizeOf(SA);
    bInheritHandle := True;
    lpSecurityDescriptor := nil;
  end;
  // create pipe for standard output redirection
  CreatePipe(StdOutPipeRead,  // read handle
             StdOutPipeWrite, // write handle
             @SA,             // security attributes
             0                // number of bytes reserved for pipe - 0 default
             );
  try
    // Make child process use StdOutPipeWrite as standard out,
    // and make sure it does not show on screen.
    with SI do
    begin
      FillChar(SI, SizeOf(SI), 0);
      cb := SizeOf(SI);
      dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      wShowWindow := SW_HIDE;
      hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdinput
      hStdOutput := StdOutPipeWrite;
      hStdError := StdOutPipeWrite;
    end;

    // launch the command line compiler
    WorkDir := ExtractFilePath(application.exename);
    WasOK := CreateProcess(pchar(Exe), PChar(Param), nil, nil, True, 0, nil,
PChar(WorkDir), SI, PI);

    // Now that the handle has been inherited, close write to be safe.
    // We don't want to read or write to it accidentally.
    CloseHandle(StdOutPipeWrite);
    // if process could be created then handle its output
    if not WasOK then
      raise Exception.Create('Could not execute command line!')
    else
      try
        // get all output until dos app finishes
        Line := '';
        repeat
          // read block of characters (might contain carriage returns and line feeds)
          WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);

          // has anything been read?
          if BytesRead > 0 then
          begin
            // finish buffer to PChar
            Buffer[BytesRead] := #0;
            // combine the buffer with the rest of the last run
            Line := Line + Buffer;
            Amemo.Lines.Text := Amemo.Lines.Text+Buffer;
//            amemo.SelStart:=length(amemo.lines.text);
//            Sendmessage(amemo.handle, WM_KEYDOWN, VK_NEXT, 0);
//            Sendmessage(amemo.handle, WM_KEYUP, VK_NEXT, 0);
//            Application.ProcessMessages;

          end;
        until not WasOK or (BytesRead = 0);
        // wait for console app to finish (should be already at this point)
        WaitForSingleObject(PI.hProcess, INFINITE);
      finally
        // Close all remaining handles
        CloseHandle(PI.hThread);
        CloseHandle(PI.hProcess);
      end;
  finally
      CloseHandle(StdOutPipeRead);
  end;
end;

Bobcsi
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

SaLzCommented:
ok, if you want to run a dos app and show the results in your app and put them into a memo1.text then the below code should help, I use this code to convert .rc to .res files using brcc32.exe file and display the dos results.

uses shellapi;

function GetShortFilename(const FileName: TFileName): TFileName;
var
  buffer: array[0..MAX_PATH-1] of char;
begin
  SetString(Result, buffer, GetShortPathName(
    pchar(FileName), buffer, MAX_PATH-1));
end;

procedure TMain1.Button1Click(Sender: TObject);
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;
  FreeMem(Buffer);
  CloseHandle(ProcessInfo.hProcess);
  CloseHandle(ProcessInfo.hThread);
  CloseHandle(ReadPipe);
  CloseHandle(WritePipe);
  end;
 end;

begin {button 1 code}
//u can take off the +GetShortFilename(copen), its the last bits of the command from my app.
   RunDosInMemo('C:\Program Files\Borland\Delphi7\Bin\brcc32.exe '+GetShortFilename(copen),Memo1);

   ShowMessage(ExtractFileName(GetShortFilename(copen))+' Created');
end;

Sal.
0
SaLzCommented:
change procedure TMain1.Button1Click(Sender: TObject); to procedure TForm1.Button1Click(Sender: TObject);
0
Mutley2003Author Commented:
hello people

the answers I have been given here are more or less refactorings of the code I started with, and I appreciate the help but it does not fix my problem.

I think the issue comes down to flushing buffers. But anyway I have had success with porting
http://www.codeproject.com/threads/redir.asp
- note the use of threads
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.