Getting Dos output through memory

Hi,
How do I get DOS output when typing a command?
Please don't tell me to do it through a file, only through memory. One of the reasons is that when I type some command, I can get an Error message as an output. I must know what this error is, but the problem is that it cannot be written to a file.
Probably this can be done by Pipes, but I don't know how to do it properly.
Points will be given for source code that can "catch" every Dos output, including errors.
You can post a source code or a web address of freeware source/component.
Thanks,
Ronit
LVL 5
ronit051397Asked:
Who is Participating?
 
interConnect With a Mentor Commented:
Thanks, if you need anything;
inter@kosgeb.tekmer.gov.tr
regards, igor
0
 
rwilson032697Commented:
Ronit,

In DOS you can use >& to catch stdout (normal output) and stderr output (where errors are usually wriiten) eg:

MyProgram.err >& MyOutput.txt

In a delphi prog you could run the program in the usual fashion using >& then inspect the resulting file.

Cheers,

Raymond.

0
 
ronit051397Author Commented:
No Files, Thank you.
0
Upgrade your Question Security!

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

 
jecksomCommented:
Hi Ronit !

You want catch every dos output from every dos window  that currently created in the system? or just specific window?

Jecksom

0
 
ronit051397Author Commented:
A specific one. One that I create by using CreateProcess with some command.
0
 
ronit051397Author Commented:
Adjusted points to 400
0
 
jecksomCommented:
HI ronit!


I've coded little app that storing all HEAP space of process ('c:\command.com in my
example) onto hard disk 'c:\out' , there no exceptions checking so execute it in Debug step mode.

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  tlhelp32;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);
var si:Tstartupinfo;pi:TPROCESSINFORMATION;
he32:THeapEntry32;
hProcess:hwnd;
heaplist:array [0..65535] of integer;
buf:pointer;
heaplistc:word;nd:word;
readbytes:longint;
MBI:TMEMORYBASICINFORMATION;
f:file;
begin
he32.dwSize:=sizeof(THeapEntry32);
heaplistc:=0;
FillChar(SI, SizeOf(SI), 0);SI.cb := SizeOf(SI);SI.wShowWindow :=0;
CreateProcess(Nil, PChar('c:\command.com'), Nil, Nil, False,
Normal_Priority_Class, Nil, Nil, SI, PI);
hProcess := OpenProcess (PROCESS_ALL_ACCESS, TRUE,
             pi.dwProcessId);
assignfile(f,'c:\out');rewrite(f,1);
he32.th32HeapID:=0;
if Heap32First(he32,pi.dwProcessid,he32.th32HeapID) then
  repeat
  getmem(buf,he32.dwBlockSize);
  virtualqueryex(hProcess,pointer(he32.dwaddress),
  MBI,sizeof(TMEMORYBASICINFORMATION));
  Toolhelp32ReadProcessMemory(pi.dwProcessid,
  pointer(mbi.BaseAddress),buf^,he32.dwBlockSize,readbytes);
  blockwrite(f,buf^,readbytes);
  freemem(buf);
  until not (Heap32Next (he32));
  closefile(f);
  closehandle(hProcess);
end;

end.


I hope it will help ya , since you always could anlyze code without saving HEAP onto HDD.
Need to go home badly (it's 19:33) , so reject it if it's not that you've asked for!

Regards,
           Jecksom.

0
 
ronit051397Author Commented:
I get access violation when I activate the application. Do you get also?

0
 
interCommented:
Hi there,
the following code executes a command and returns all the output and also error that program returns:

ask anything, we can also make this procedure interractive(i.e. before the process terminates...

TO TEST:  
drop a memo and button on your form link button with the following buttonclick code and proceed...

//***************CODE STARTS*********************
//
// this procedure executes a console application
// waits its completion and returns all the output
// of the execution in strings (OutPut)
//

procedure ExecWithPipe(FName : PChar; OutPut : TStrings);
var
   StartupInfo:TStartupInfo;
   ProcessInfo:TProcessInformation;
   Buffer : array[0..255] of char;
   bRead  : integer;
   hRead,hWrite  : THandle;
   saAttr : TSECURITYATTRIBUTES;
   OutSt : TMemoryStream;  //this is where we transfer all the output at the end
begin
   // Set the bInheritHandle flag so pipe handles are inherited.
   saAttr.nLength := sizeof(TSECURITYATTRIBUTES);
   saAttr.bInheritHandle := true;
   saAttr.lpSecurityDescriptor := nil;
   if not CreatePipe(hRead, hWrite,@saAttr,0) then
   begin
     ShowMessage('Can not create the pipe!');
     Exit;
   end;
   if not AllocConsole then
   begin
     ShowMessage('Can not allocate console for the child!');
     Exit;
   end;
   try
   FillChar(StartupInfo,Sizeof(StartupInfo),#0);
   StartupInfo.cb := Sizeof(StartupInfo);
   StartupInfo.dwFlags := STARTF_USESTDHANDLES;
   StartupInfo.wShowWindow := SW_SHOWDEFAULT;
   //Associate our handles with our child process
   StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
   StartupInfo.hStdOutput:= hWrite; //we catch output
   StartupInfo.hStdError := hWrite; //and also error
   if not CreateProcess(nil,
     FName,
     nil, nil,
     true,                         //!!!!!!!we should inherit handles
     NORMAL_PRIORITY_CLASS,        //the child should use our CONSOLE
     nil, nil, StartupInfo, ProcessInfo) then
      ShowMessage('Can not create process')
   else
   begin
     //loop until terminated
     while WaitforSingleObject(ProcessInfo.hProcess,0)
           <> WAIT_OBJECT_0 do ;
     //now read all the output of the child and put it to a memo
     OutSt := TMemoryStream.Create;
     repeat
        if ReadFile(hRead, Buffer, 80,bRead,nil) then
          OutSt.WriteBuffer(Buffer, bRead)
        else break;
     until bRead <> 80;
     OutSt.Seek(0,0); //seek to begining
     //read memo from stream
     OutPut.LoadFromStream(OutSt);
     OutSt.Free;
   end;
   finally
     //close read and write handles of our pipe
     CloseHandle(hRead); CloseHandle(hWrite);
     if not FreeConsole then MessageBeep(0);
   end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  ExecWithPipe('ping localhost',Memo1.Lines);
end;
//***************CODE ENDS*********************

0
 
jecksomCommented:
Hi ronit!

There could be problem with NT , you have to be Administrator or user with debug priviligues,
If you crashing under Win95 , just tell me line of code there it is  and i'll give you correct one.

Btw , inter's example looks good for you main question , but if you also interesting not
only in Screen dump (which also should be contained in on of heap) , you could learn something from my metod.

inter:
Hm , i have strange thing with your source when "AllocConsole " happend, it's not crashing, just hanging and there not chance to close even Delphi (i've tried with Delphi3 &4).

Jecksom

0
 
MadshiCommented:
jecksom,

toolhelp functions only work with win95/98 and winNT5, NOT with NT4. So I guess Ronit gets an Access Violation, because of working with NT4, where the toolhelp functions are not assigned...

Regards, Madshi.
0
 
jecksomCommented:
Hi Madshi !

Yep , you right, but MS promised to make first look in Service Pack 4 , i didn't test it personaly
yet.

Jecksom

0
 
MadshiCommented:
Hi jecksom,

really? That would be great!!! Unfortunately the german version of Service Pack 4 seems to be not ready yet. At least I didn't find it...    :-(

Regards, Madshi.
0
 
interCommented:
Hi friend,
in my code if your command does not require a console input then
delete AllocConsole and FreeConsole and change
   StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
to
   StartupInfo.hStdInput := 0;
this should work, please if you have time test it(I have NT4 and test it with the following commands:
  cmd /k dir
  ping localhost
  netstat)
regardes, igor
0
 
ronit051397Author Commented:
Hi all,
I work on NT4 as you guassed.
JeckSom, I get AV when the application starts, not on a perticular line of code.
Inter, I have changed the code according to what you suggested, and it's not working.
Both, Try to run your code with some command that doesn't exists. I need to get the output which is an error.
for example if You run the command: 'c:\aaa'
Then you should get this error as an output:
"The name specified is not recognized as an
internal or external command, operable program or batch file."
Please send me your example after testing it on NT4 to:  ronith@cmr.co.il
I will give the points to the one who's code is working properly.

Thanks a lot
Ronit
0
 
MadshiCommented:
Ronit,

have you installed ServicePack 4? Perhaps you should...

Regards, Madshi.
0
 
ronit051397Author Commented:
Acually, there is ne need to test with 'c:\aaa' because the process will not be created. But if it is created, then it should "catch" the errors.
For example, if I type "ping.exe aaa" then I should get the Error: "Bad IP address aaa."

Thanks,
0
 
ronit051397Author Commented:
Madshi, no I haven't. But I think this should work also without.
Jrcsom, Inter: Try to run it with the 'route' command. The CreateProcess is stucked.
0
 
ronit051397Author Commented:
Jecksom, sorry, I have to reject your answer.
Inter Your code almost works, also on NT4 without even upgrading to Service Pack 3.
There is only one problem:
Before creating the process I set:
StartupInfo.dwFlags:=STARTF_USESHOWWINDOW;
StartupInfo.wShowWindow:=SW_HIDE;
But I still see the Dos Window.
How can I fix it?


0
 
interCommented:
Hi,
just arrived to work on lovely sunday afternoon...Could you please list me few examples that my code stucks. I try to hide the window by that time...
regards, igor
0
 
ronit051397Author Commented:
Hi Igor. I have found some source that works with hidden Windows:
"...
var
  tsi: TStartupInfo;
  tpi: TProcessInformation;
  dwRead, dwAvail: DWORD;
  hInputRead, hInputWrite, hDupInputWrite: THandle;
  s: string;
begin
  { sParameter is the parameter you want to pass to net.exe view }  
  s := 'ping.exe -n 1' + sParameter;
  if CreatePipe(hInputRead, hInputWrite, nil, 1024) and
     DuplicateHandle(GetCurrentProcess(), hInputWrite, GetCurrentProcess(),
@hDupInputWrite, 0, True,
                     DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS) then
  begin
    FillChar(tsi, SizeOf(TStartupInfo), 0);
    tsi.cb := SizeOf(TStartupInfo);
    tsi.wShowWindow := SW_HIDE;
    tsi.hStdOutput := hDupInputWrite;
    tsi.hStdError := hDupInputWrite;
    tsi.hStdInput := 0; { You don't need input }
    tsi.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    if CreateProcess(nil, PChar(s), nil, nil, True, 0, nil, nil, tsi, tpi) then
    begin
      CloseHandle(hDupInputWrite);
      WaitForSingleObject(tpi.hProcess, INFINITE);
      if PeekNamedPipe(hInputRead, nil, 0, nil, @dwAvail, nil) then
      begin
        SetLength(s, dwAvail);
        ReadFile(hInputRead, Pointer(s)^, dwAvail, dwRead, nil);
        {$IFOPT D+} Assert(dwAvail = dwRead); {$ENDIF}
        { s not contains all the text ping.exe spits on the DOS box }
        { so you can check it whether it was successful or not }
      end;
      CloseHandle(tpi.hProcess);
      CloseHandle(tpi.hThread);
    end;
    CloseHandle(hInputRead);
  end;
end;
."

I think I'll use it, instead can you tell me how do I pass a command line to a process that I have Created before.I'll explain:
I create a DOS process and it is open. Later I would like to send commands to it, knowing the process handle, how do I do it?
I must send commands to that perticular process not create a new one.

Thanks,
Ronit

0
 
interCommented:
Hi there,
i have sent you corrected code...but this is actually similiar. I do not know why they need to Duplicate handle...anyway..
Returning back to your point, what do you mean by sending commands? Do we want to open command prompt and execute series of commands on it? The solution all depends the processing you require any may not be possible...
here for comments,
igor
0
 
ronit051397Author Commented:
I have checked your code. I works.
What I mean that: When creating my application I create a single Dos process which stays in the background as long as my application is running. I destroy this process when I close my application.
Later, I need to send new commands to that process.
I don't want to create a new process for each new command, that's why the DOS process stays resident in memory all the time.
How do I send commands to that process?
I'll raise the points for this answer.

Thanks,

0
 
ronit051397Author Commented:
Probabely I need to use WriteFile.
0
 
interCommented:
Hi,
I start working on it, inform you in several minutes...
0
 
interCommented:
Yehuuuuuuuu,
I make it out, wait for few minutes more I fine tune it(I use another pipe to write on command prompt);
0
 
interCommented:
Hi the only thing thats left is that I cannot synchronize the output of the sub command if it takes so much,
do you want to see the code as is,
waiting for your sugesstions while working on it...
igor
0
 
ronit051397Author Commented:
Thanks, I would like to see it. To synchronize maybe you need to use  WaitForInputIdle.
0
 
interCommented:
I post it to you(cant make it with WaitForInputIdle but working on...)
0
 
interCommented:
Could you get it?
0
 
ronit051397Author Commented:
I got the code, But I always get the results of the command before the last command.
Try to type this first command:'ping 2.3'  and then 'ping aaa'
0
 
interCommented:
replace this
  ExecFunc(Edit1.Text);
  Sleep(1000);
  ReadOutPuts(Memo1.Lines);
and you see partial result...I am still trying(by the way in formdestroy call
 Button4.Click;
sorry, igor

0
 
interCommented:
I mean delete the Waitforinput idle stuff. The problem is we should detect WHEN the command ends. I have a clever idea if you could help me. I notice that when there is a command running on cmd the caption of the window is changed to that command. So the algorithm for detecting the end of sub process is
1 - Read the title of the cmd upon starting
2 - Issue a command and wait until title is same as original
3 - then read the output
The problem is that I can not be able get the title of the console I have created(there is an api call for a process to obtain itselfs console title...?)
if we could do this we solve the problem...
working with you is a pleasure,
igor
0
 
ronit051397Author Commented:
I don't know, but did you try using WaitNamedPipe? Maybe this is where hooking comes into action...
0
 
interCommented:
We have anonymous pipe, and I do not think we can use named pipe for stdinput output. At least I send you the best I can(I find the window with FindWindow so we should find where cmd.exe resides-normally c:\winnt\system32\cmd.exe'). You can play the code so we solve the problem quicker...
igor

0
 
interCommented:
I think its ok,
change the code in startup console to (in my last email) to
.
  begin
     WaitForInputIdle(ProcessInfo.hProcess, INFINITE);
     repeat
       hConsoleWindow := FindWindow(nil,'c:\winnt\system32\cmd.exe');
     until hConsoleWindow <> 0;
     Result := true;
  end;
.
eagerly waiting...
0
 
ronit051397Author Commented:
This is working, but I would like to say that comparing the caption can be a bit dangerous, because several Dos Applications can run on the computer with the same caption.
Before CreateProcess we should set our own title, something like:
StartupInfo.lpTitle:=pChar('igor');
I am going home now. Tomorrow I let you know about the final results, and grade your answer.

Many thanks for your efforts.
Ronit, Israel

0
 
ronit051397Author Commented:
Hi, I am back.
My original purpose was to increase performance, and this can be done by creating one single Dos application and "talk" to it all the time.
The proposed code work slower then creating a new process each time because of the sleep(..) procedure. Each time I execute a command I have to wait for a while. This will not work faster also if I upgrade the Hardware, again, because of the sleep(..) procedure.
So, the Bottom line is that I go back to my previos method, which is creating a new process for each command. I guess this problem can be solved by Hooking, but that's another issue.

Thanks anyway. If you post an answer I'll grade it.
Ronit,
0
 
interCommented:
Thanks friend,
but I think you do not want to spend this much points on a solution that you do not use. So if you want delete the question and ask it with less points and I supply my first solution...
regards, igor
0
 
ronit051397Author Commented:
I use the solution of the first question. The solution of the second question don't work as I wanted but I can learn something from it and how pipes work. So you can post some answer and I'll grade it.

Ronit,

0
 
ronit051397Author Commented:
If you come up with something new in the future, please let me know. My Email is in the user information.

Thanks,
Ronit
0
 
interCommented:
Yesterday,
I dig into what called GINA. Now I can be able to change the windows logon, logoff, shutdown and locking properties...Since you are also a NT guy, may be you are interested...
regards, igor
0
 
ronit051397Author Commented:
Sure, why not. :-)     ronith@cmr.co.il
0
 
interCommented:
I have sent it thanks,..
0
 
ronit051397Author Commented:
Hi Igor,
I could not open your zip file. Somehow it got corrupted. Please resend it to gal-b@cmr.co.il
Thanks,
Ronit
0
 
jecksomCommented:
Hi ronit , inter !

I've lost my inet connection to ex-ex for 2 days , maybe one of you could send me final
source , since this subject really interesting for me , but i've lost some fragments.

Best Regards,
                          Jecksom.

0
 
jecksomCommented:
PS : sorry forgot email: ivan@elkatel.ru :)

0
 
MadshiCommented:
Igor, please look at "www.experts-exchange.com/Q.10094994"...

Thanx... Madshi.
0
 
interCommented:
Guys, I have moved(all my department). So I have no valid inet address for last few weeks. Now I could enter inet from some friends connection 600 km away from home. This is very sad, I want to tear my hairs if I couldn't soon acquire an inet account. I try to send the GINA source to all as soon as I can. Take care of yourselves and Exp-Exc, I am sure you could fill my emptiness in domain...
regards, I miss this community...
igor
0
 
ronit051397Author Commented:
I hope to hear from you soon, you helped us a lot.
Ronit,
0
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.

All Courses

From novice to tech pro — start learning today.