寻找物理适配器的MAC地址 [英] looking for a MAC address of a physical adapter
问题描述
我想使用唯一标识符来确定我的应用程序是否移动到另一台计算机。 MAC地址似乎适合于这个目的。我使用的代码是这样的:
过程TForm4.GetMacAddress;
var item:TListItem;
objWMIService:OLEVariant;
colItems:OLEVariant;
colItem:OLEVariant;
oEnum:IEnumvariant;
iValue:LongWord;
wmiHost,root,wmiClass:string;
i:Int32;
函数GetWMIObject(const objectName:String):IDispatch;
var
chEaten:Integer;
BindCtx:IBindCtx; //访问绑定上下文
Moniker:IMoniker; //使您可以使用昵称对象
begin
OleCheck(CreateBindCtx(0,bindCtx) );
OleCheck(MkParseDisplayName(BindCtx,StringToOleStr(objectName),chEaten,Moniker)); //将字符串转换为标识由字符串命名的对象的标记
OleCheck(Moniker.BindToObject(BindCtx,nil ,IDispatch,Result)); //绑定到指定的对象
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');
如果colItem.IPEnabled
then Item.SubItems.Add('yes')
else Item.SubItems.Add('no');
Item.SubItems.Add(VarToStrNil(colItem.SettingID));
Item.SubItems.Add(IntToStr(colItem.InterfaceIndex));
结束// if
end; // GetMacAddress //
我的机器有一个网络端口,但是这个代码找到18个网络相关端口/事物/事物其中有四个MAC地址。我认为一个网络端口应该是IP启用,以便留下两个左边(在图像中标记的MAC)。假设这样筛选的端口是正确的,索引最低的那个端口是硬件端口?
编辑在Realtek适配器上面的快照是只有机器中的物理适配器。另一个适配器是VirtualBox虚拟适配器。 TLama的答案识别这两个适配器,但是有没有办法找到唯一的Physical(Realtek)适配器的地址?
更新1 EJP指出可以更改MAC地址。这有点破坏了我的目的,但是正如我正在寻找一种适合大多数情况的解决方案,我决定与之一起生活。
TLama和TOndrej指出了几个解决方案。两者都有可能无法找到物理适配器的情况。
更新2 TLama的优秀阅读列表显示有可能不是一定的方式来确定物理适配器。第一个项目中提到的文章显示了如何根据一些简单的假设缩小适配器的数量。第三个项目中的文章显示了如何选择连接到PCI总线的适配器,这其实正是我想知道的。文章中提到了一些奇怪的例外,但我认为这将在大多数情况下提供一个答案。
感谢大家的贡献!
使用 Win32_NetworkAdapter
类。它具有 PhysicalAdapter
成员。以下示例应列出物理适配器的MAC地址:
程序Program1;
{$ APPTYPE CONSOLE}
使用
SysUtils,ActiveX,ComObj,Variants;
程序GetWin32_NetworkAdapterInfo;
const
WbemUser ='';
WbemPassword ='';
WbemComputer ='localhost';
wbemFlagForwardOnly = $ 00000020;
var
ElementCount:LongWord;
FWMIService:OleVariant;
FWbemObject:OleVariant;
EnumVariant:IEnumVARIANT;
FSWbemLocator:OleVariant;
FWbemObjectSet:OleVariant;
开始;
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;
而EnumVariant.Next(1,FWbemObject,ElementCount)= 0 do
begin
Writeln(Format('MACAddress%s',[VarToStr(FWbemObject.MACAddress)]));
FWbemObject:=未分配;
结束
结束
begin
try
CoInitialize(nil);
try
GetWin32_NetworkAdapterInfo;
finally
CoUninitialize;
结束
除了
在E:EOleException do
Writeln(Format('EOleException%s%x',[E.Message,E.ErrorCode]));
在E:异常do
Writeln(E.Classname,':',E.Message);
结束
Writeln('按Enter键退出);
Readln;
结束。
根据 WMI Delphi代码创建者
。
更新:
您实际上正在尝试过滤掉属于虚拟机的适配器,这不是那么容易,因为它们被仿真成为物理的(您甚至可以在设备管理器中看到它们作为物理适配器),因此您无法区分它们,例如:
- by
DHCPEnabled
Win32_NetworkAdapter
类,因为甚至可以配置虚拟机,来自DHCP服务器的IP地址 - 由
AdapterTypeId
成员Win32_NetworkAdapter
类,因为没有特殊类型的虚拟适配器 - 通过 PhysicalAdapter 成员。 85%29.aspxrel =nofollow>
Win32_NetworkAdapter
类,因为它们被模拟为物理的 -
仅使用WMI Win32_NetworkAdapter类查找物理网络适配器
- 本文有一个很好的分析,可能会满足您的需求(我没有测试它) -
如何确定物理网络适配器t ype使用WMI
- 此问题目前正在开放,实际上正是您需要的。 -
如何确定物理网卡的MAC地址
- 有一个我喜欢的想法,我会亲自坚持,当我将100%确定PNPDeviceID
成员
Win32_NetworkAdapter
类不能从与PCI\\
(实际上我正在考虑当我比较数据时)
附加阅读:
I would like to use a unique identifier to determine whether my application moved to a different computer. The MAC address seems to be suitable for this purpose. The code I use is this:
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 //
My machine has one network port, but this code finds 18 network related ports/things/whatever. Among them there are four MAC adresses. I presume that a network port should be IP enabled so that leaves two left (labeled MAC in the image). Is it correct to assume that of the ports thus filtered, the one with the lowest index is the hardware port?
Edit in the snapshot above the Realtek adapter is the only physical adapter in the machine. The other adapter is the VirtualBox virtual adapter. The answer of TLama identifies these two adapters, but is there a way to find the address of the only Physical (Realtek) adapter?
Update 1 EJP pointed out that the MAC address can be changed. This somewhat undermines my purpose, but as I am looking for a solution that fits most situations I decided to live with it.
TLama and TOndrej pointed to several solutions. Both end up with a situation that a physical adapter could not be found without any doubt.
Update 2 TLama's excellent reading list shows that there is probably not a certain way to determine the physical adapter. The article mentioned in the first bullet shows how to shrink the amount of adapters based on some simple assumptions. The article in the third bullet shows how to select the adapter connected to the PCI bus, which is in fact exactly what I wanted to know. There are some weird exceptions mentioned in the article but I think this will provide an answer in most cases.
Thank you all for your contributions!
Use the Win32_NetworkAdapter
class instead. It has the PhysicalAdapter
member. The following example should list you the MAC addresses of physical adapters:
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.
Based on the code generated by the WMI Delphi Code Creator
.
Update:
You are actually trying to filter out the adapters which belongs to virtual machines, what is not that easy since they are simulated like to be as physical ones (you can even see them in Device Manager as the physical adapters), so you cannot distinguish them e.g.:
- by the
DHCPEnabled
member of theWin32_NetworkAdapter
class, because even virtual machines might be configured so they get IP address from a DHCP server - by the
AdapterTypeId
member of theWin32_NetworkAdapter
class, since there is no special type for virtual adapters - by the
PhysicalAdapter
member of theWin32_NetworkAdapter
class, because they are being simulated to be physical
Additional reading:
Find only physical network adapters with WMI Win32_NetworkAdapter class
- this article has a good analysis and it might fulfill your needs (I haven't tested it though)How to determine physical network adapter type using WMI
- this question is opened at this time and it is actually exactly what you needHow to determine MAC Address of the physical network card
- there is one idea I like and I would personally stick to when I'd be 100% sure that the root of thePNPDeviceID
member
of theWin32_NetworkAdapter
class for hardware adapters cannot start with something different from thePCI\\
(actually the same I was thinking about when I was comparing the data)
这篇关于寻找物理适配器的MAC地址的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!