Reading MAC address of ethernet (wifi) from WinCE device lazarus / 35-Windows CE ---------------------------------------------------------------------- ertank · 2016-03-10 15:37 Hi, I have found several threads in forum.lazarus about subject. Unfortunately, none of them worked for me. Is there any one which already could get this piece of information? Thanks. ---------------------------------------------------------------------- Jurassic Pork · 2016-03-11 01:59 hello, you can try using tprocess and  tregexpr  with the command ipconfig /all. something like that ( description and mac address of each network adapter)  : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // uses process,regexpr;procedure TForm1.ReadAdapterInfos();      const READ_BYTES = 2048;      var      i : integer;      Re1,Re2: TRegExpr;      aProcess: TProcess; //TProcess is crossplatform is best way      MemStream: TMemoryStream;      NumBytes: LongInt;      BytesRead: LongInt;      Lines: TStringList;    begin     // A temp Memorystream is used to buffer the output     MemStream := TMemoryStream.Create;   Lines :=TStringList.Create;     BytesRead := 0;      try       re1 := TRegExpr.Create;       // regex for description of network adapter     re1.Expression := '.*Description.*:(.+)';       re2 :=  TRegExpr.Create;       // regex for mac address       re2.Expression := '.*Adresse physique.*:(.+)';       aProcess := TProcess.Create(nil);       aProcess.Executable := 'ipconfig.exe';     aProcess.Parameters.Add('/all');       aprocess.ShowWindow := swoHIDE;       AProcess.Options := AProcess.Options + [poUsePipes,poStderrToOutPut];       Memo1.Lines.Clear;       aProcess.Execute;       while aProcess.Running do       begin         // make sure we have room         MemStream.SetSize(BytesRead + READ_BYTES);         // try reading it         NumBytes := aProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);       if NumBytes > 0 // All read() calls will block, except the final one.            then Inc(BytesRead, NumBytes)         else            BREAK // Program has finished execution.       end;       MemStream.SetSize(BytesRead);       Lines.LoadFromStream(MemStream);       for i := 0 to Lines.Count-1 do       begin       if re1.Exec(Lines[i]) then  Memo1.lines.Add(Trim(Re1.Match[1]));       if re2.Exec(Lines[i]) then  Memo1.lines.Add(Trim(Re2.Match[1]));       end;       finally       re1.Free;       re2.Free;       aProcess.Free;       Lines.Free;       MemStream.Free;       end;    end;       1 - The expressions for the regexp are trivial -> can be optimized 2 - The expression of re2 for mac address must be changed depending of the country. In the example it is for french Ipconfig. Friendly J.P ---------------------------------------------------------------------- ertank · 2016-04-08 14:29 Hi, Thanks for the sample code. I tried to use it. However, my WinCE device insists not to run ipconfig.exe First, I get "Cannot execute empty command-line" error message. Then I also assign "ipconfig.exe" to aProcess.CommandLine (which supposed to be depreciated). Second, I started to get "Failed to execute ipconfig.exe : 87" which I have no idea what it means. I tested and confirm that my device has ipconfig.exe and it executes as expected from command shell. Are you able to support on above problems? Thanks. ---------------------------------------------------------------------- Jurassic Pork · 2016-04-08 15:44 hello, try to call the command-line interpreter before the command :  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  AProcess.Executable := 'cmd.exe'; // or with the whole path    AProcess.Parameters.Add('/c');    AProcess.Parameters.Add('ipconfig.exe');    AProcess.Parameters.Add('/all');  Friendly, J.P ---------------------------------------------------------------------- ertank · 2016-04-08 21:18 Hi, I didn't mention earlier. I am using Lazaus 1.6.0. Cross compiler for ARM is installed. Still receiving error saying "Cannot execute empty command-line" again. I suppose I need to put something in aProcess.CommandLine parameter. This parameter is depreciated as it is indicated though. Current code is as follows: I have some type casting to prevent compiler warnings. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // uses process,regexpr;function TDM.ReadAdapterInfos:String;const READ_BYTES = 2048;vari : integer;Re1,Re2: TRegExpr;aProcess: TProcess; //TProcess is crossplatform is best wayMemStream: TMemoryStream;NumBytes: LongInt;BytesRead: LongInt;Lines: TStringList;begin  Result := EmptyStr;  // A temp Memorystream is used to buffer the output  MemStream := TMemoryStream.Create;  Lines :=TStringList.Create;  BytesRead := 0;  try    re1 := TRegExpr.Create;    // regex for description of network adapter    re1.Expression := '.*Description.*:(.+)';    re2 :=  TRegExpr.Create;    // regex for mac address    re2.Expression := '.*Adresse physique.*:(.+)';    aProcess := TProcess.Create(nil);    AProcess.Executable := 'cmd.exe'; // or with the whole path    AProcess.Parameters.Add('/c');    AProcess.Parameters.Add('ipconfig.exe');    AProcess.Parameters.Add('/all');    AProcess.ShowWindow := swoHIDE;  AProcess.Options := AProcess.Options + [poUsePipes,poStderrToOutPut];    aProcess.Execute;    while aProcess.Running do begin      // make sure we have room      MemStream.SetSize(BytesRead + READ_BYTES);    // try reading it      NumBytes := aProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);    if NumBytes > 0 // All read() calls will block, except the final one.        then Inc(BytesRead, NumBytes)      else        BREAK // Program has finished execution.    end;    MemStream.SetSize(BytesRead);    Lines.LoadFromStream(MemStream);    for i := 0 to Lines.Count-1 do begin      if re1.Exec(WideString(Lines[i])) then  Result := Result + #13 + Trim(string(Re1.Match[1]));      if re2.Exec(WideString(Lines[i])) then  Result := Result + #13 + Trim(string(Re2.Match[1]));    end;  finally    re1.Free;    re2.Free;    aProcess.Free;    Lines.Free;    MemStream.Free;  end;end; ---------------------------------------------------------------------- Jurassic Pork · 2016-04-08 22:22 hello, it seems that  executable + parameters are not used for wince version of Tprocess    windows 1 - lazarus_1_6_0\fpc\3.0.0\source\packages\fcl- process\src\win\process.inc Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  if (FApplicationName<>'') then    begin    PName:=Pchar(FApplicationName);    PCommandLine:=Pchar(FCommandLine);    end  else If (FCommandLine<>'') then    PCommandLine:=Pchar(FCommandLine)  else if (Fexecutable<>'') then    begin    Cmd:=MaybeQuoteIfNotQuoted(Executable);    For I:=0 to Parameters.Count-1 do      Cmd:=Cmd+' '+MaybeQuoteIfNotQuoted(Parameters[i]);     PCommandLine:=PChar(Cmd); Wince 2 - F:\lazarus_1_6_0\fpc\3.0.0\source\packages\fcl- process\src\wince\process.inc Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  if (FApplicationName='') then    begin      If (FCommandLine='') then      Raise EProcess.Create(SNoCommandline);      PCommandLine:=PWidechar(FCommandLine)    end  else    begin      PName:=PWidechar(FApplicationName);      If (FCommandLine='') then      PCommandLine:=PWidechar(FApplicationName)      else        PCommandLine:=PWidechar(FCommandLine)    end; then try this : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i AProcess.CommandLine := 'cmd.exe  /c Ipconfig.exe /all'; Friendly, J.P ---------------------------------------------------------------------- ertank · 2016-04-08 22:44 Now we are back to that mysterious "error 87" My debug log contains below lines: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i TApplication.HandleException Failed to execute cmd.exe  /c Ipconfig.exe /all : 87TApplication.HandleException Failed to execute ipconfig.exe /all : 87TApplication.HandleException Failed to execute \windows\ipconfig.exe /all : 87  I, myself, out of ideas here. ---------------------------------------------------------------------- Jurassic Pork · 2016-04-08 23:06 error code 87 for wince : Quote87    The parameter is incorrect. ---------------------------------------------------------------------- ertank · 2016-04-08 23:09 Sure, meaning of error 87 is known. Reason is not. Everything seems in order for our command line. I have found below link related with my problem. They were not able to use TProcess in the end, too. http://forum.lazarus.freepascal.org/index.php?topic=3683.0 So, I'm stuck. ---------------------------------------------------------------------- ertank · 2016-04-09 10:56 Finally, I could read ethernet MAC address (not bluetooth though). My source link: http://forum.lazarus.freepascal.org/index.php?topic=10465.0 I needed to modify some parts of the code to be able to compile it for WinCE. Below is my complete unit code: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit Unit1; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, Menus,  StdCtrls; type   { TForm1 }   TForm1 = class(TForm)    Button2: TButton;    Memo1: TMemo;    procedure Button1Click(Sender: TObject);    procedure Button2Click(Sender: TObject);    procedure FormCreate(Sender: TObject);  private    { private declarations }    function GetWinMacAddress: String;  public    { public declarations }  end;   function GetIfTable( pIfTable : Pointer;                   VAR pdwSize  : LongInt;                       bOrder   : LongInt ): LongInt; stdcall;var  Form1: TForm1; implementation {$R *.lfm} function GetIfTable( pIfTable : Pointer;                 VAR pdwSize  : LongInt;                     bOrder   : LongInt ): LongInt; stdcall; external 'IPHLPAPI.DLL'; { TForm1 } procedure TForm1.FormCreate(Sender: TObject);begin  // Below is necessary to be able to see form on the mini device screen  Left := 0;  Top  := 25;end; procedure TForm1.Button2Click(Sender: TObject);begin  Memo1.Lines.Add(EmptyStr);  Memo1.Lines.Add(GetWinMacAddress);end; function TForm1.GetWinMacAddress: String;const  MAX_INTERFACE_NAME_LEN           = $100;  ERROR_SUCCESS                      = 0;  MAXLEN_IFDESCR                   = $100;  MAXLEN_PHYSADDR                    = 8;   MIB_IF_TYPE_ETHERNET               = 6;   _MAX_ROWS_ = 20; type    MIB_IFROW            = Record     wszName : Array[0 .. (MAX_INTERFACE_NAME_LEN * 2 - 1)] of char;     dwIndex              : LongInt;     dwType               : LongInt;     dwMtu                : LongInt;     dwSpeed              : LongInt;     dwPhysAddrLen      : LongInt;     bPhysAddr : Array[0 .. (MAXLEN_PHYSADDR-1)] of Byte;     dwAdminStatus        : LongInt;     dwOperStatus         : LongInt;     dwLastChange         : LongInt;     dwInOctets           : LongInt;     dwInUcastPkts        : LongInt;     dwInNUcastPkts     : LongInt;     dwInDiscards         : LongInt;     dwInErrors         : LongInt;     dwInUnknownProtos    : LongInt;     dwOutOctets        : LongInt;     dwOutUcastPkts       : LongInt;     dwOutNUcastPkts    : LongInt;     dwOutDiscards        : LongInt;     dwOutErrors        : LongInt;     dwOutQLen            : LongInt;     dwDescrLen         : LongInt;     bDescr     : Array[0 .. (MAXLEN_IFDESCR - 1)] of Char;     end;    _IfTable = Record                 nRows : LongInt;               ifRow : Array[1.._MAX_ROWS_] of MIB_IFROW;              end; var   pIfTable  : ^_IfTable;   TableSize : LongInt;   tmp       : String;   i,j       : Integer;   ErrCode   : LongInt;begin   pIfTable := nil;   //------------------------------------------------------------   Result := '';   try      //-------------------------------------------------------      // First: just get the buffer size.      // TableSize returns the size needed.      TableSize := 0; // Set to zero so the GetIfTabel function                    // won't try to fill the buffer yet,                  // but only return the actual size it needs.      GetIfTable(pIfTable, TableSize, 1);      if (TableSize < SizeOf(MIB_IFROW) + Sizeof(LongInt)) then      begin         Exit; // less than 1 table entry?!      end; // if-end.       // Second:      // allocate memory for the buffer and retrieve the      // entire table.      GetMem(pIfTable, TableSize);      ErrCode := GetIfTable(pIfTable, TableSize, 1);      if (ErrCode <> ERROR_SUCCESS) then      begin         Exit; // OK, that did not work.               // Not enough memory i guess.      end; // if-end.       // Read the ETHERNET addresses.      for i := 1 to pIfTable^.nRows do      try       if (pIfTable^.ifRow[i].dwType=MIB_IF_TYPE_ETHERNET) and           (pIfTable^.ifRow[i].dwOutOctets <> 0) then         begin            tmp := '';            for j:=0 to pIfTable^.ifRow[i].dwPhysAddrLen-1 do            begin               tmp := tmp + format('%.2x:',                    [ pIfTable^.ifRow[i].bPhysAddr[j] ] );            end; // for-end.            //-------------------------------------          if Length(tmp)>0 then            begin              Result := Copy(tmp, 1, Length(tmp) - 1);              Exit;            end;       end; // if-end.      except         Exit;      end; // if-try- except-end.   finally      if Assigned(pIfTable) then FreeMem(pIfTable, TableSize);   end; // if-try-finally-end.end; end.  Solution is not what we were trying to achieve, but it's same result, different path. Thanks for all the support. Regards, Ertan Küçükoğlu ---------------------------------------------------------------------- Jurassic Pork · 2016-04-09 11:37 hello, you can try also to use the iphlpapi   unit like that : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i // uses iphlpapi procedure TForm1.Button1Click(Sender: TObject);var pAdapterInfo:PIP_ADAPTER_INFO;   BufLen,Status:cardinal; i:Integer;   tmp : string;begin  BufLen:= sizeof(IP_ADAPTER_INFO);  GetAdaptersInfo(nil, BufLen);  pAdapterInfo:= AllocMem(BufLen);  try  Status:= GetAdaptersInfo(pAdapterInfo,BufLen);    if (Status = 0 ) then    begin      while (pAdapterInfo<>nil) do    begin    tmp := '';    if pAdapterInfo^.AddressLength>0 then    for i := 0 to pAdapterInfo^.AddressLength - 1 do      tmp :=  tmp + IntToHex(pAdapterInfo^.Address[I], 2) + ' ';     memo1.Append(tmp);   pAdapterInfo:=pAdapterInfo^.next;    end;    end;  finally    Freemem(pAdapterInfo);  end;end;     it's OK for me on my old PDA   Fuji Loox 420    PXA255   with      wince (pocket pc)  4.2    need to activate WLAN to see the MAC address of the Wifi adapter. Friendly, J.P ---------------------------------------------------------------------- ertank · 2016-04-09 12:24 That seems to be another solution. Shorter, easier to read. Are you sure that you need to activate WiFi in order to read MAC address? I tested my code when WiFi is closed and it run OK. Thanks. ---------------------------------------------------------------------- Jurassic Pork · 2016-04-09 12:30 on my device (fuji Loox 420) when i disable WLAN, i can't see the Wifi adapter infos from my program. What is your device ? ---------------------------------------------------------------------- ertank · 2016-04-09 12:36 Producer is Korean. Web address: http://www.dsic.co.kr/ You can find it under Mobile Solutions. Model is DS5 ---------------------------------------------------------------------- Jurassic Pork · 2016-04-09 12:43 your PDA is  relatively recent , mine is jurassic ( 2004)