2012-05-15 9 views
6

Vorrei utilizzare un identificatore univoco per determinare se la mia applicazione è stata spostata su un altro computer. L'indirizzo MAC sembra adatto a questo scopo. Il codice che uso è questo:cercare un indirizzo MAC di un adattatore fisico

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 // 

La mia macchina ha una porta di rete, ma questo codice trova 18 porte di rete correlati/cose/qualunque cosa. Tra questi ci sono quattro indirizzi MAC. Presumo che una porta di rete debba essere abilitata per IP in modo da lasciare due left (etichettati MAC nell'immagine). È corretto assumere che delle porte così filtrate, quella con l'indice più basso sia la porta hardware?

enter image description here

Modifica nell'istantanea sopra l'adattatore Realtek è l'unica scheda fisica nella macchina. L'altro adattatore è l'adattatore virtuale VirtualBox. La risposta di TLama identifica questi due adattatori, ma esiste un modo per trovare l'indirizzo dell'unico adattatore fisico (Realtek)?

Aggiornamento 1 EJP ha sottolineato che l'indirizzo MAC può essere modificato. Questo in qualche modo mina il mio scopo, ma mentre sto cercando una soluzione adatta alla maggior parte delle situazioni ho deciso di conviverci.

TLama e TOndrej hanno indicato diverse soluzioni. Entrambe finiscono con una situazione in cui non è stato possibile trovare un adattatore fisico senza alcun dubbio.

Aggiornamento 2 L'eccellente elenco di lettura di TLama mostra che probabilmente non esiste un determinato modo per determinare l'adattatore fisico. L'articolo menzionato nel primo punto mostra come ridurre la quantità di adattatori in base a semplici presupposti. L'articolo nel terzo punto mostra come selezionare l'adattatore connesso al bus PCI, che è in effetti esattamente quello che volevo sapere. Ci sono alcune strane eccezioni menzionate nell'articolo, ma penso che questo fornirà una risposta nella maggior parte dei casi.

Grazie a tutti per i vostri contributi!

+1

Buon punto, ho adottato il tuo suggerimento. Grazie. – Arnold

+0

L'indirizzo MAC non è adatto a questo scopo. Può essere modificato dall'utente. – EJP

+0

@EJP, qualsiasi puntatore a quello? – Arnold

risposta

7

Utilizzare invece la classe Win32_NetworkAdapter. Ha il membro PhysicalAdapter. L'esempio seguente dovrebbe elencare voi gli indirizzi MAC degli adattatori fisici:

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. 

Basato sul codice generato dal WMI Delphi Code Creator.

Aggiornamento:

In realtà si sta cercando di filtrare gli adattatori che appartiene alle macchine virtuali, ciò che non è così facile in quanto sono simulate come di essere come quelle fisiche (si può anche vedere loro in Gestione periferiche come adattatori fisici), quindi non è possibile distinguerli ad es:

  • dal DHCPEnabled membro della classe Win32_NetworkAdapter, perché anche le macchine virtuali possono essere configurati in modo da ottenere l'indirizzo IP da un server DHCP
  • dal AdapterTypeId membro della classe Win32_NetworkAdapter, dal momento che non v'è alcun tipo speciale per schede virtuali
  • dal PhysicalAdapter membro della classe Win32_NetworkAdapter, perché vengono simulati essere fisico

Letture addizionali:

+0

Ciò restituisce esattamente i due indirizzi MAC con IPEnabled nella mia domanda. Mi chiedo come sia possibile distinguere tra la vera scheda di rete (Realtek) e la scheda di rete virtuale di VirtualBox. La mia domanda non era chiara a quel punto. Ci aggiungerò. – Arnold

+0

Fondamentalmente direi che questo potrebbe essere distinto, ad es. in 'Win32_NetworkAdapterConfiguration' dal membro' DHCPEnabled', ma anche quegli adattatori virtuali potrebbero essere configurati per ottenere l'indirizzo IP dal server DHCP. C'è anche 'PNPDeviceID' che inizia con' PCI \ ... 'ma non è ancora così significativo. In ['this'] (http://www.codeproject.com/Articles/18135/Getting-the-Network-Adaptor-MAC-Address-with-WMI) hanno usato' AdapterTypeId', tuttavia gli adattatori virtuali non hanno tipo speciale per questo, quindi non è nemmeno una soluzione. – TLama

+2

Sembra che stavo pensando al ['modo simile'] (http://weblogs.sqlteam.com/mladenp/archive/2010/11/04/find-only-physical-network-adapters-with-wmi-win32_networkadapter-class aspx). Temo che quegli adattatori siano gestiti da Windows come un vero adattatore di rete fisico, quindi senza questo modo complicato è impossibile distinguerli da quelli hardware. – TLama

4

Si potrebbe anche usare GetAdaptersAddresses API dalla libreria IP Helper . Per una traduzione Delphi, Magenta Systems IP Helper Component sembra buono a prima vista.

+0

Grazie per il puntatore. Codice interessante, ma per quanto posso vedere risulta più o meno lo stesso tipo di output della classe WMI "Win32_NetworkAdapterConfiguration", che alla fine genera lo stesso output: due porte che fungono da adattatore fisico. – Arnold

Problemi correlati