Looks for the MAC address of a physical adapter

I would like to use a unique identifier to determine if my application has moved to another computer. It seems that the MAC address is suitable for this purpose. The code I'm using 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 ports / things / something else. Among them are four MAC addresses. I assume that the network port must have IP enabled in order to leave two on the left (indicated by the MAC in the image). Is it right to assume that of the filters filtered in this way, the one with the lowest index is the hardware port?

enter image description here

Change in the screenshot above the adapter Realtek is the only physical adapter in the machine. Another adapter is the VirtualBox virtual adapter. TLama's answer identifies these two adapters, but is there a way to find the address of a single Physical (Realtek) adapter?

EJP Update 1 indicated that the MAC address can be changed. This somewhat undermines my goal, but since I am looking for a solution that is suitable for most situations, I decided to live with it.

TLama and Tndrej pointed to several solutions. Both end up in a situation where the physical adapter cannot be found without any doubt.

Update 2 TLama's excellent reading list shows that there is probably no definite way to determine the physical adapter. The article mentioned in the first brochure shows how to reduce the number of adapters based on some simple assumptions. The article in the third brochure shows how to choose an adapter connected to the PCI bus, which is actually exactly what I wanted to know. There are some strange exceptions mentioned in the article, but I think this will give an answer in most cases.

Thank you all for your input!

+6
source share
2 answers

Use Win32_NetworkAdapter . It has a member PhysicalAdapter . In the following example, you must specify the MAC addresses of the 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 WMI Delphi Code Creator .

Update:

In fact, you are trying to filter out adapters belonging to virtual machines, which is not so simple since they are simulated as physical ones (you can even see them in the Device Manager as physical adapters), so you cannot distinguish between them, for example:

  • a member of the DHCPEnabled class of the DHCPEnabled class, since even virtual machines can be configured so that they receive an IP address from a DHCP server
  • a member of the AdapterTypeId class of Win32_NetworkAdapter , since there is no special type for virtual adapters
  • member of the PhysicalAdapter of the Win32_NetworkAdapter class, because they are simulated as physical

Additional reading:

+7
source

You can also use the GetAdaptersAddresses API from the IP Helper library. To translate Delphi Magenta Systems IP Helper Component looks good at first glance.

+4
source

Source: https://habr.com/ru/post/915764/


All Articles