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.:
DHCPEnabled
member of the Win32_NetworkAdapter
class, because even virtual machines might be configured so they get IP address from a DHCP serverAdapterTypeId
member of the Win32_NetworkAdapter
class, since there is no special type for virtual adaptersPhysicalAdapter
member of the Win32_NetworkAdapter
class, because they are being simulated to be physicalAdditional 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 need
How 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 the PNPDeviceID
member
of the Win32_NetworkAdapter
class for hardware adapters cannot start with something different from the PCI\\
(actually the same I was thinking about when I was comparing the data)
You could also use GetAdaptersAddresses
API from the IP Helper library. For a Delphi translation, Magenta Systems IP Helper Component looks good at first glance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With