Question

I'm using Delphi XE4.

I'm porting a piece of code that compiles are runs under vcl (using ActiveX, vcl.oleauto) to a firemonkey friendly version.

It throws a Invalid typecast error. Anyone know what's different between the vcl units and these?

Thanks

John.

uses

{$IFDEF MSWINDOWS}
  Winapi.Windows,
  System.Win.ComObj,
  Winapi.ole2,
{$ENDIF}

SysUtils;


function GetfirstMacAddress : String;
const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;

begin
  Result := '';
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT Description,MACAddress FROM Win32_NetworkAdapterConfiguration','WQL',wbemFlagForwardOnly);

  oEnum         := (IUnknown(FWbemObjectSet._NewEnum)) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    if not VarIsNull(FWbemObject.MACAddress) then
      begin
        Result := (FWbemObject.MACAddress);
        exit;
      end;
    FWbemObject:=Unassigned;
  end;
end;
Was it helpful?

Solution

Your code does not compile anywhere. Neither VCL nor FMX. So, this issue not related to FMX at all. Perhaps an earlier version did, but not the code here.

I think that @TLama is on the right track with your uses clauses. You need to the IEnumVARIANT from the ActiveX unit rather than the one from Ole2. The problem with the IEnumVARIANT in Ole2 is that it is missing its GUID which means the as cast fails. And the IUnknown in Ole2 is a class which is plain weird. And the IEnumVARIANT._Next in Ole2 uses a signed integer for the var parameter, also weird. So, Ole2 appears to be bad news!

Also, you were missing the Variants unit which is needed for VarIsNull.

This version of your code runs successfully:

{$APPTYPE CONSOLE}

uses
  System.Variants,
  Winapi.ActiveX,
  System.Win.ComObj;

function GetFirstMacAddress : String;
const
  wbemFlagForwardOnly = $00000020;
var
  FSWbemLocator : OLEVariant;
  FWMIService   : OLEVariant;
  FWbemObjectSet: OLEVariant;
  FWbemObject   : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;

begin
  Result := '';
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService   := FSWbemLocator.ConnectServer('localhost', 'root\CIMV2', '', '');
  FWbemObjectSet:= FWMIService.ExecQuery('SELECT Description,MACAddress FROM Win32_NetworkAdapterConfiguration','WQL',wbemFlagForwardOnly);

  oEnum         := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while oEnum.Next(1, FWbemObject, iValue) = 0 do
  begin
    if not VarIsNull(FWbemObject.MACAddress) then
      begin
        Result := (FWbemObject.MACAddress);
        exit;
      end;
    FWbemObject:=Unassigned;
  end;
end;

begin
  CoInitialize(nil);
  Writeln(GetFirstMacAddress);
  Readln;
end.

This is probably right back to where you started! You don't want to use ActiveX in you FMX code. But Ole2 is no good either. All you need to do is make a local declaration of IEnumVARIANT in your code. There's no need to take the whole of the ActiveX unit.

So start from the code above. Remove the ActiveX unit. And supply the good declaration of IEnumVARAINT copied from the ActiveX unit's source.


FWIW, I would not solve this problem with WMI. It is very heavyweight and just not needed for the task at hand.

Here's how I would obtain the MAC address of the local network interface:

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Winapi.Windows,
  Winapi.WinSock;

type
  TMacAddress = array [0..5] of Byte;

function SendARP(DestIP, SrcIP: ULONG; pMacAddr: Pointer; var PhyAddrLen: ULONG): DWORD; stdcall; external 'Iphlpapi.dll';

procedure InitializeWinSock;
var
  wsaData: TWSADATA;
begin
  Win32Check(Winapi.WinSock.WSAStartup($0202, wsaData)=0);
end;

procedure FinalizeWinSock;
begin
  Winapi.WinSock.WSACleanup;
end;

function inet_addr(const IPAddress: string): ULONG;
begin
  Result := ULONG(Winapi.WinSock.inet_addr(PAnsiChar(AnsiString(IPAddress))));
end;

function GetMacAddress(const IPAddress: string): TMacAddress;
var
  MaxMacAddrLen: ULONG;
begin
  MaxMacAddrLen := SizeOf(Result);
  if SendARP(inet_addr(IPAddress), 0, @Result, MaxMacAddrLen)<>NO_ERROR then begin
    raise Exception.CreateFmt('Unable to do SendARP on address: ''%s''', [IPAddress]);
  end;
end;

var
  i: Integer;
  mac: TMacAddress;

begin
  InitializeWinSock;
  mac := GetMacAddress('localhost');
  for i := low(mac) to high(mac) do
  begin
    Write(IntToHex(mac[i], 2));
    if i<high(mac) then
      Write(':');
  end;
  Writeln;
  FinalizeWinSock;
  Readln;
end.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top