【问题标题】:looking for a MAC address of a physical adapter查找物理适配器的 MAC 地址
【发布时间】:2012-05-22 17:18:54
【问题描述】:

我想使用唯一标识符来确定我的应用程序是否已移至另一台计算机。 MAC 地址似乎适合此目的。我使用的代码是这样的:

Procedure TForm4.GetMacAddress;
var item: TListItem;
    objWMIService : OLEVariant;
    colItems      : OLEVariant;
    colItem       : OLEVariant;
    oEnum         : IEnumvariant;
    iValue        : LongWord;
    wmiHost, root, wmiClass: string;
    i: Int32;

  function GetWMIObject(const objectName: String): IDispatch;
  var
    chEaten: Integer;
    BindCtx: IBindCtx;//for access to a bind context
    Moniker: IMoniker;//Enables you to use a moniker object
  begin
    OleCheck(CreateBindCtx(0, bindCtx));
    OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));//Converts a string into a moniker that identifies the object named by the string
    OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));//Binds to the specified object
  end;

begin
   wmiHost       := '.';
   root          := 'root\CIMV2';
   wmiClass      := 'Win32_NetworkAdapterConfiguration';
   objWMIService := GetWMIObject(Format('winmgmts:\\%s\%s',[wmiHost,root]));
   colItems      := objWMIService.ExecQuery(Format('SELECT * FROM %s',[wmiClass]),'WQL',0);
   oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;
   i := 0;
   while oEnum.Next(1, colItem, iValue) = 0 do
   begin
      Item := View.Items.Add;
      item.Caption := Copy (colItem.Caption, 2, 8);

      Item.SubItems.Add (colItem.Description);
      Item.SubItems.Add (colItem.ServiceName);
      Item.SubItems.Add (VarToStrNil (colItem.MACAddress));
      if (VarToStrNil(colItem.MACAddress) <> '')
         then Item.SubItems.Add ('yes')
         else Item.SubItems.Add ('no');
      if colItem.IPEnabled
         then Item.SubItems.Add ('yes')
         else Item.SubItems.Add ('no');
     Item.SubItems.Add (VarToStrNil (colItem.SettingID));
     Item.SubItems.Add (IntToStr (colItem.InterfaceIndex));
   end; // if
end; // GetMacAddress //

我的机器有一个网络端口,但是这段代码找到了 18 个与网络相关的端口/事物/任何东西。其中有四个MAC地址。我假设一个网络端口应该启用 IP,以便留下两个(在图像中标记为 MAC)。假设在如此过滤的端口中,索引最低的是硬件端口是否正确?

Edit 在上面的快照中,Realtek 适配器是机器中唯一的物理适配器。另一个适配器是 VirtualBox 虚拟适配器。 TLama 的答案识别了这两个适配器,但是有没有办法找到唯一的物理(Realtek)适配器的地址?

Update 1 EJP 指出可以更改 MAC 地址。这在某种程度上破坏了我的目的,但当我正在寻找适合大多数情况的解决方案时,我决定接受它。

TLama 和 TOndrej 指出了几种解决方案。两者最终都会出现毫无疑问无法找到物理适配器的情况。

Update 2 TLama 的优秀阅读清单表明,可能没有确定物理适配器的方法。第一个项目符号中提到的文章展示了如何基于一些简单的假设来缩减适配器的数量。第三个项目中的文章展示了如何选择连接到 PCI 总线的适配器,这实际上正是我想知道的。文章中提到了一些奇怪的例外,但我认为这将在大多数情况下提供答案。

感谢大家的贡献!

【问题讨论】:

  • 好点,我采纳了你的建议。谢谢。
  • MAC 地址不适用于此目的。它可以由用户更改。
  • Windows 控制面板开始。
  • 抱歉,找不到。
  • 适配器的网络属性。 Windows的版本越晚,你要钻的越深。但它就在那里。 Unix 和 Limux 也是如此。

标签: windows delphi networking wmi delphi-xe


【解决方案1】:

改用Win32_NetworkAdapter 类。它有PhysicalAdapter 成员。以下示例应列出物理适配器的 MAC 地址:

program Program1;

{$APPTYPE CONSOLE}

uses
  SysUtils, ActiveX, ComObj, Variants;

procedure GetWin32_NetworkAdapterInfo;
const
  WbemUser = '';
  WbemPassword = '';
  WbemComputer = 'localhost';
  wbemFlagForwardOnly = $00000020;
var
  ElementCount: LongWord;
  FWMIService: OleVariant;
  FWbemObject: OleVariant;
  EnumVariant: IEnumVARIANT;
  FSWbemLocator: OleVariant;
  FWbemObjectSet: OleVariant;
begin;
  FSWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
  FWMIService := FSWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2', WbemUser, WbemPassword);
  FWbemObjectSet := FWMIService.ExecQuery('SELECT * FROM Win32_NetworkAdapter WHERE PhysicalAdapter = 1', 'WQL', wbemFlagForwardOnly);
  EnumVariant := IUnknown(FWbemObjectSet._NewEnum) as IEnumVariant;
  while EnumVariant.Next(1, FWbemObject, ElementCount) = 0 do
  begin
    Writeln(Format('MACAddress %s', [VarToStr(FWbemObject.MACAddress)]));
    FWbemObject := Unassigned;
  end;
end;

begin
  try
    CoInitialize(nil);
    try
      GetWin32_NetworkAdapterInfo;
    finally
      CoUninitialize;
    end;
  except
    on E:EOleException do
      Writeln(Format('EOleException %s %x', [E.Message,E.ErrorCode]));
    on E:Exception do
      Writeln(E.Classname, ':', E.Message);
  end;
  Writeln('Press Enter to exit');
  Readln;
end.

基于WMI Delphi Code Creator生成的代码。

更新:

您实际上是在尝试过滤掉属于虚拟机的适配器,这并不容易,因为它们被模拟为物理适配器(您甚至可以在设备管理器中将它们视为物理适配器),所以您无法区分它们,例如:

  • Win32_NetworkAdapter 类的DHCPEnabled 成员,因为即使是虚拟机也可能被配置为从DHCP 服务器获取IP 地址
  • Win32_NetworkAdapter 类的AdapterTypeId 成员,因为虚拟适配器没有特殊类型
  • Win32_NetworkAdapter 类的PhysicalAdapter 成员,因为它们被模拟为物理的

补充阅读:

【讨论】:

  • 看来我在想similar way。恐怕这些适配器是由 Windows 像真正的物理网络适配器一样处理的,所以如果没有这种棘手的方法,就不可能将它们与硬件的适配器区分开来。
  • 我确实尝试过 TOndrej 指出的 IP Helper 库。 AdapterInfo 结构的信息也太少。不过,如果您不想依赖 WMI,这将是有趣的代码。
  • 我更希望 PNPDeviceID 的根为 PCI\...,但我不知道这是否适合 USB 适配器(如果有的话),但它是仍然没有那么顺利的解决方案。虚拟适配器就像真正的硬件适配器一样,因此很难区分它们。我一直在寻找有关此的类似主题,但似乎没有人以干净的方式解决此问题。
  • Manufacturer 属于Win32_NetworkAdapter 类,而不是Win32_NetworkAdapterConfiguration,所以它应该是SELECT * FROM Win32_NetworkAdapter WHERE Manufacturer != "Microsoft"。无论如何,最好的办法是下载最新的 RRUZ 的WMI Delphi Code Creator,它还包括一个查询编辑器,您可以在其中构建查询以及生成完整的 Delphi 代码。
  • 感谢您的澄清和指向 RRUZ 的指针。我现在使用 PNPDeviceID,如果它没有产生任何东西,它可能是一个虚拟机,我会选择第一个可用的。为了测试它是否在所有情况下都有效,我真的应该测试 100 台机器。无论如何,这回答了我的问题,非常感谢您的帮助和解释!
【解决方案2】:

您还可以使用来自IP Helper 库的GetAdaptersAddresses API。对于 Delphi 翻译,Magenta Systems IP Helper Component 乍一看还不错。

【讨论】:

  • 感谢您的指点。有趣的代码,但据我所见,产生的输出与“Win32_NetworkAdapterConfiguration”WMI 类的输出大致相同,最终产生相同的输出:两个端口充当物理适配器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
  • 2019-09-28
  • 1970-01-01
  • 2010-09-23
  • 1970-01-01
  • 2014-09-06
相关资源
最近更新 更多