2012-05-15 1 views
6

Я хотел бы использовать уникальный идентификатор, чтобы определить, переместилось ли мое приложение на другой компьютер. Кажется, что MAC-адрес подходит для этой цели. Код, который я использую, следующий:ищет 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 на изображении). Правильно ли предположить, что из фильтров, фильтрованных таким образом, один с самым низким индексом является аппаратным портом?

enter image description here

Edit на снимке выше адаптера Realtek является единственным физическим адаптером в машине. Другой адаптер - виртуальный адаптер VirtualBox. Ответ TLama идентифицирует эти два адаптера, но есть ли способ найти адрес единственного адаптера Physical (Realtek)?

Обновление 1 EJP указал, что MAC-адрес может быть изменен. Это несколько подрывает мою цель, но поскольку я ищу решение, которое подходит для большинства ситуаций, я решил жить с ним.

TLama и TOndrej указали на несколько решений. Оба заканчиваются ситуацией, когда физический адаптер не может быть найден без каких-либо сомнений.

Обновление 2 Отличный список чтения TLama показывает, что, возможно, нет определенного способа определить физический адаптер. Статья, упомянутая в первой брошюре, показывает, как уменьшить количество адаптеров на основе некоторых простых допущений. В статье в третьей брошюре показано, как выбрать адаптер, подключенный к шине PCI, что на самом деле именно то, что я хотел знать. Есть некоторые странные исключения, упомянутые в статье, но я думаю, что это даст ответ в большинстве случаев.

Спасибо всем за ваш вклад!

+1

Хорошо, я принял ваше предложение. Благодарю. – Arnold

+0

MAC-адрес не подходит для этой цели. Он может быть изменен пользователем. – EJP

+0

@EJP, любой указатель на это? – Arnold

ответ

7

Вместо этого используйте класс 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.

Update:

Вы на самом деле пытаетесь отфильтровать адаптеры, которые принадлежат к виртуальным машинам, что не так легко, так как они моделируются как быть, как физические (вы можете даже увидеть их в диспетчере устройств в качестве физических адаптеров), поэтому вы не можете их отличить, например:

  • по DHCPEnabled члену Win32_NetworkAdapter класса, потому что даже виртуальные машины могут быть настроены таким образом они получают IP-адрес от DHCP-сервера
  • от AdapterTypeId члена Win32_NetworkAdapter класса, так как нет специального типа для виртуальных адаптеров
  • от PhysicalAdapter члена Win32_NetworkAdapter класса, так как они моделируются быть физическим

Дополнительное чтение:

+0

Это дает точно два MAC-адреса с IPEnabled в моем вопросе. Интересно, как я могу отличить реальный сетевой адаптер (Realtek) и виртуальный сетевой адаптер от VirtualBox. В этот момент мой вопрос не был ясен. Я добавлю. – Arnold

+0

В основном я бы сказал, что это можно различить, например. в 'Win32_NetworkAdapterConfiguration' членом' DHCPEnabled', но даже эти виртуальные адаптеры могут быть настроены для получения IP-адреса с сервера DHCP. Существует также «PNPDeviceID», который начинается с «PCI \ ...», но он все еще не столь значителен. В примере ['this'] (http://www.codeproject.com/Articles/18135/Getting-the-Network-Adaptor-MAC-Address-with-WMI) они использовали' AdapterTypeId', однако у виртуальных адаптеров нет для него особый тип, поэтому он также не является решением. – TLama

+2

Кажется, я думал о '' подобном пути' '(http://weblogs.sqlteam.com/mladenp/archive/2010/11/04/find-only-physical-network-adapters-with-wmi-win32_networkadapter-class .aspx). Я боюсь, что эти адаптеры Windows обрабатываются как настоящие физические сетевые адаптеры, поэтому без этого сложного способа их отличить от аппаратных. – TLama

4

Вы также можете использовать GetAdaptersAddresses API из IP Helper библиотеки , Для перевода Delphi Magenta Systems IP Helper Component выглядит хорошо на первый взгляд.

+0

Спасибо за указатель. Интересный код, но, насколько я вижу, результатом этого является то же, что и для класса WMI класса Win32_NetworkAdapterConfiguration, в результате получается тот же вывод: два порта, которые действуют как физический адаптер. – Arnold