Recent

Author Topic: Getting stdout from ipconfig /all into a TMemo  (Read 9220 times)

fatmonk

  • Sr. Member
  • ****
  • Posts: 252
Getting stdout from ipconfig /all into a TMemo
« on: March 04, 2015, 01:29:30 pm »
I'm trying to put together a very, very simple little UI to grab network info on a Windows PC.

The Form has a button and a TMemo (mOutput) to display results.

The OnClick procedure for the button is:

Code: [Select]
procedure TForm1.bGetInfoClick(Sender: TObject);
var
  AProcess: TProcess;
  AStringList: TStringList;
begin
  try
    AProcess := TProcess.Create(nil);
    AStringList:= TSTringList.Create;

    AProcess.Executable:='ipconfig';
    if (rbAllAdapters.Checked = true) then AProcess.Parameters.Add('/all');
    AProcess.Options:=AProcess.Options + [poWaitOnExit, poUsePipes, poNoConsole];

    AProcess.Execute;

    AStringList.LoadFromStream(AProcess.Output);
    mOutput.Lines.Clear;
    mOutput.Lines.AddStrings(AStringList);

    AProcess.Free;
    AStringList.Free;

  except
        showMessage('Error');
  end;
end;

I've tried various combinations of Options for the TProcess object but I'm still having a number of issues...

  • The output from the command isn't the same as when ipconfig /all is run from cmd.
  • If I do not use poWaitOnExit I only get one line of output passed to the TMemo and I need to close the console window before I see anything at all in the TMemo (this is with poNoConsole omitted, obviously).
  • I cannot suppress the console window effectively. If I use poNoConsole and poWaitOnExit I get nothing in the TMemo - presumably because AProcess is 'waiting on exit' before passing its output to the
Code: [Select]
TMemo
    .

I'm starting to get the feeling that I'm going about this the wrong way.. and it seemed such a simple project. :-\

-FM

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #1 on: March 04, 2015, 01:40:37 pm »
-1 Search for API code to enumerate network adapters and connections, rather than creating homemade solutions. There must be delphi code somewhere that can be converted. (iirc via wnetcfg API calls. Maybe the jwa libs have the headers.).
0. you don't use the "large output" model for tprocess. When there is trouble, always fix that first.
1. Logical, since that is a combination of stdout+stderr and possibly non pipable console behaviour. It could even be that ipconfig changes behaviour depending on whether it detects a pipe or not.
2. First fix issue (0), then retest.
3. Maybe the ipconfig needs a handle to the console to function. Not necessarily TProcess' problem.

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #2 on: March 04, 2015, 02:54:53 pm »
1. The output is exactly the same however it is truncated.
As MarcoV suggested use the "large output" model.
The good news is, the demo runs with only a minor modification : http://wiki.lazarus.freepascal.org/Executing_External_Programs#Reading_large_output.
Just change the program name and parameter and you should have the required output.
(Tested on Win8.1)

<<edit>>
Or start from the demo GUI application and copy/paste the bits you need:
https://svn.code.sf.net/p/lazarus-ccr/svn/examples/process/
« Last Edit: March 04, 2015, 03:03:21 pm by eny »
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

paweld

  • Hero Member
  • *****
  • Posts: 970
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #3 on: March 04, 2015, 04:13:42 pm »
Save result ipconfig to file:
Code: [Select]
ipconfig /all > c:\ipconfig.txt
and load file to memo.
Best regards / Pozdrawiam
paweld

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #4 on: March 04, 2015, 04:16:23 pm »
Or for lazy people:

Code: [Select]

uses process;

var s : ansistring;
begin
  runcommand('ipconfig',['/all'],s);
  writeln(s);
end.

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #5 on: March 04, 2015, 04:22:29 pm »
Or for lazy people:
Good point.
Since this question gets asked so many times, maybe the Wiki needs to be reshuffled: first paragraph mentions the RunCommand procedure.
And if someone really needs more control, he/she can read on for all the nooks and crannies.
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #6 on: March 04, 2015, 04:26:32 pm »
Well, in this case the long term best solution I think is not by  parsing ipconfig at all, but using the windows api. I just don't have ready made code lying around.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #7 on: March 04, 2015, 05:01:50 pm »
To get sb started:

Code: [Select]
program ListIPs;

[$mode delphi}
{$APPTYPE CONSOLE}

uses
Windows,
SysUtils,
Winsock,
JwaIpHlpAPI,
JwaIpRtrMib;

procedure ListIPAddresses;
var
Ret: DWord;
Table: PMib_IPAddrTable;
TableSize: ULong;
i: Integer;
Row: PMib_IPAddrRow;
begin
// We begin by assuming there's just one entry, so we allocate space
// for that one
TableSize := SizeOf(Table^);
GetMem(Table, TableSize);
try
// Request a list of IP addresses, unsorted
Ret := GetIpAddrTable(Table, TableSize, False);
case Ret of
No_Error: ; // No error. Continue at the end of the case statement
Error_Insufficient_Buffer: begin
// Oops. Space for just one entry wasn't enough. Allocate more.
ReallocMem(Table, TableSize);
Ret := GetIpAddrTable(Table, TableSize, False);
if Ret <> No_Error then begin
// Function expects signed value, but Ret is unsigned. Type
// cast to avoid range-check error, however unlikely.
RaiseLastOSError(Integer(Ret));
end;
end;
else RaiseLastOSError(Integer(Ret));
end;
writeln(Table.dwNumEntries, ' entries:');
if Table.dwNumEntries > 0 then begin
Row := @Table.table[0];
for i := 0 to Pred(Table.dwNumEntries) do begin
writeln(inet_ntoa(in_addr(Row.dwAddr)));
Inc(Row);
end;
end;
finally
FreeMem(Table);
end;
end;

begin
try
ListIPAddresses;
except
on E: Exception do
writeln(E.ClassName, ': ', E.Message);
end;
readln;
end.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11383
  • FPC developer.
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #8 on: March 04, 2015, 10:49:28 pm »
Since this question gets asked so many times, maybe the Wiki needs to be reshuffled: first paragraph mentions the RunCommand procedure.
And if someone really needs more control, he/she can read on for all the nooks and crannies.

I think the simplified code should be removed. Too many people seem to start with it, and not look further

eny

  • Hero Member
  • *****
  • Posts: 1634
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #9 on: March 04, 2015, 10:58:08 pm »
I think the simplified code should be removed. Too many people seem to start with it, and not look further
Simplified code that does not work, is incomplete or has no practical purpose should probably be removed; like the intermediate examples that lead up to the final version of the 'large output' code.

However if 'simple code' gets the job done there is no need to look further.
Hence my suggestion to move the runcommand to the top.

Come to think of it: maybe a separate list of 'practical examples' would be handy (trying hard to avoid the term 'best practices'  ;) ).
A list of working programs, classes or code fragments that have a specific purpose and actually work in a documented way and can be used out of the box (more or less).
« Last Edit: March 04, 2015, 11:03:18 pm by eny »
All posts based on: Win10 (Win64); Lazarus 2.0.10 'stable' (x64) unless specified otherwise...

fatmonk

  • Sr. Member
  • ****
  • Posts: 252
Re: Getting stdout from ipconfig /all into a TMemo
« Reply #10 on: March 05, 2015, 10:35:25 am »
Wow! I didn't expect this to generate so much traffic...

@marcov: The intention isn't to parse the result - I just want to output the result into a TMemo for viewing or clipboard copying. However, your 'large output model' comment is what set me on the right track.

@paweld: You've got the gist of what I'm trying to do - but I'm trying to avoid people having to drop to cmd to do it - and sadly some people struggle with anything on the command line never mind redirecting output to files.

My idea here was to have a simple GUI based tool that replicates ipconfig /all > c:\ipconfig.txt but ouputs to the clipboard for pasting into an email or text document or whatever. Just to save people having to drop to the command line.

Starting with the large output model demo code I came up with the following:

Code: [Select]
procedure TForm1.bGetInfoClick(Sender: TObject);
const
  READ_BYTES = 2048;
var
  OutputLines: TStringList;
  MemStream: TMemoryStream;
  OurProcess: TProcess;
  NumBytes: LongInt;
  BytesRead: LongInt;
begin
  // A temp Memorystream is used to buffer the output
  MemStream := TMemoryStream.Create;
  BytesRead := 0;

  OurProcess := TProcess.Create(nil);

  OurProcess.Executable := 'ipconfig';
  OurProcess.Parameters.Add('/all');

  OurProcess.Options := [poUsePipes, poNoConsole];
  OurProcess.Execute;

  while True do
  begin
    // make sure we have room
    MemStream.SetSize(BytesRead + READ_BYTES);

    // try reading it
    NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
    if NumBytes > 0 // All read() calls will block, except the final one.
    then begin
      Inc(BytesRead, NumBytes);
    end else
      BREAK // Program has finished execution.
  end;
  //if BytesRead > 0 then WriteLn;
  MemStream.SetSize(BytesRead);

  OutputLines := TStringList.Create;
  OutputLines.LoadFromStream(MemStream);

  mOutput.Lines.AddStrings(OutputLines);
  OutputLines.Free;
  OurProcess.Free;
  MemStream.Free;

end;

which works a treat.

-FM

 

TinyPortal © 2005-2018