Solved

Getting Dos output through memory

Posted on 1998-10-29
50
386 Views
Last Modified: 2010-04-06
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
0
Comment
Question by:ronit051397
  • 21
  • 18
  • 6
  • +2
50 Comments
 
LVL 12

Expert Comment

by:rwilson032697
ID: 1344899
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344900
No Files, Thank you.
0
 
LVL 1

Expert Comment

by:jecksom
ID: 1344901
Hi Ronit !

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

Jecksom

0
 
LVL 5

Author Comment

by:ronit051397
ID: 1344902
A specific one. One that I create by using CreateProcess with some command.
0
 
LVL 5

Author Comment

by:ronit051397
ID: 1344903
Adjusted points to 400
0
 
LVL 1

Expert Comment

by:jecksom
ID: 1344904
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344905
I get access violation when I activate the application. Do you get also?

0
 
LVL 5

Expert Comment

by:inter
ID: 1344906
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
 
LVL 1

Expert Comment

by:jecksom
ID: 1344907
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1344908
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
 
LVL 1

Expert Comment

by:jecksom
ID: 1344909
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1344910
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
 
LVL 5

Expert Comment

by:inter
ID: 1344911
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344912
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
 
LVL 20

Expert Comment

by:Madshi
ID: 1344913
Ronit,

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

Regards, Madshi.
0
 
LVL 5

Author Comment

by:ronit051397
ID: 1344914
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344915
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344916
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
 
LVL 5

Expert Comment

by:inter
ID: 1344917
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344918
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
 
LVL 5

Expert Comment

by:inter
ID: 1344919
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344920
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344921
Probabely I need to use WriteFile.
0
 
LVL 5

Expert Comment

by:inter
ID: 1344922
Hi,
I start working on it, inform you in several minutes...
0
 
LVL 5

Expert Comment

by:inter
ID: 1344923
Yehuuuuuuuu,
I make it out, wait for few minutes more I fine tune it(I use another pipe to write on command prompt);
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 5

Expert Comment

by:inter
ID: 1344924
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344925
Thanks, I would like to see it. To synchronize maybe you need to use  WaitForInputIdle.
0
 
LVL 5

Expert Comment

by:inter
ID: 1344926
I post it to you(cant make it with WaitForInputIdle but working on...)
0
 
LVL 5

Expert Comment

by:inter
ID: 1344927
Could you get it?
0
 
LVL 5

Author Comment

by:ronit051397
ID: 1344928
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
 
LVL 5

Expert Comment

by:inter
ID: 1344929
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
 
LVL 5

Expert Comment

by:inter
ID: 1344930
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344931
I don't know, but did you try using WaitNamedPipe? Maybe this is where hooking comes into action...
0
 
LVL 5

Expert Comment

by:inter
ID: 1344932
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
 
LVL 5

Expert Comment

by:inter
ID: 1344933
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344934
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344935
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
 
LVL 5

Expert Comment

by:inter
ID: 1344936
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344937
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
 
LVL 5

Accepted Solution

by:
inter earned 450 total points
ID: 1344938
Thanks, if you need anything;
inter@kosgeb.tekmer.gov.tr
regards, igor
0
 
LVL 5

Author Comment

by:ronit051397
ID: 1344939
If you come up with something new in the future, please let me know. My Email is in the user information.

Thanks,
Ronit
0
 
LVL 5

Expert Comment

by:inter
ID: 1344940
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344941
Sure, why not. :-)     ronith@cmr.co.il
0
 
LVL 5

Expert Comment

by:inter
ID: 1344942
I have sent it thanks,..
0
 
LVL 5

Author Comment

by:ronit051397
ID: 1344943
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
 
LVL 1

Expert Comment

by:jecksom
ID: 1344944
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
 
LVL 1

Expert Comment

by:jecksom
ID: 1344945
PS : sorry forgot email: ivan@elkatel.ru :)

0
 
LVL 20

Expert Comment

by:Madshi
ID: 1344946
Igor, please look at "www.experts-exchange.com/Q.10094994"...

Thanx... Madshi.
0
 
LVL 5

Expert Comment

by:inter
ID: 1344947
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
 
LVL 5

Author Comment

by:ronit051397
ID: 1344948
I hope to hear from you soon, you helped us a lot.
Ronit,
0

Featured Post

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Suggested Solutions

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
This video discusses moving either the default database or any database to a new volume.
This video shows how to remove a single email address from the Outlook 2010 Auto Suggestion memory. NOTE: For Outlook 2016 and 2013 perform the exact same steps. Open a new email: Click the New email button in Outlook. Start typing the address: …

757 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

23 Experts available now in Live!

Get 1:1 Help Now