【问题标题】:XE4 Firemonkey OLE typecast error (fetching win MAC Address)XE4 Firemonkey OLE 类型转换错误(获取 win MAC 地址)
【发布时间】:2014-03-03 09:35:51
【问题描述】:

我正在使用 Delphi XE4。

我正在将一段在 vcl(使用 ActiveX,vcl.oleauto)下运行编译的代码移植到对 firemonkey 友好的版本。

它会抛出一个无效的类型转换错误。有人知道 vcl 单元和这些单元有什么不同吗?

谢谢

约翰。

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;

【问题讨论】:

    标签: delphi com firemonkey ole delphi-xe4


    【解决方案1】:

    您的代码无法在任何地方编译。 VCL 和 FMX 都不是。所以,这个问题根本与 FMX 无关。也许早期版本有,但不是这里的代码。

    我认为@TLama 与您的使用条款走在正确的轨道上。您需要来自ActiveX 单元的IEnumVARIANT,而不是来自Ole2 的那个。 Ole2 中的 IEnumVARIANT 的问题在于它缺少其 GUID,这意味着 as 转换失败。而Ole2 中的IUnknown 是一个很奇怪的类。而Ole2 中的IEnumVARIANT._Nextvar 参数使用有符号整数,这也很奇怪。所以,Ole2 似乎是个坏消息!

    另外,您缺少VarIsNull 所需的Variants 单元。

    这个版本的代码运行成功:

    {$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.
    

    这可能就回到了你开始的地方!您不想在 FMX 代码中使用 ActiveX。但是Ole2 也不好。您需要做的就是在您的代码中对IEnumVARIANT 进行本地声明。无需占用整个ActiveX 单元。

    所以从上面的代码开始。移除ActiveX 单元。并提供从ActiveX 单元源复制的IEnumVARAINT 的良好声明。


    FWIW,我不会用 WMI 解决这个问题。它非常重量级,手头的任务不需要它。

    以下是获取本地网络接口的 MAC 地址的方法:

    {$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.
    

    【讨论】:

    • 感谢,它比以前的版本更轻。
    猜你喜欢
    • 2013-05-07
    • 2014-07-24
    • 2014-10-26
    • 1970-01-01
    • 2023-03-31
    • 2012-05-28
    • 1970-01-01
    相关资源
    最近更新 更多