Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Portierung Delphi 6 -> Delphi XE (MAC-Adresse ermitteln)


Manfred - Di 10.05.16 16:02
Titel: Portierung Delphi 6 -> Delphi XE (MAC-Adresse ermitteln)
Hallo Leidgenossen,

vor einiger Zeit habe ich unter Delphi 6 eine Routine in diesem Forum gefunden, die mir das Auslesen der MAC-Adresse ermöglicht:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
unit MyLAN;

interface

uses SysUtils,nb30;

function MACAddress():string;
// Liefert die MAC-Adressen aller Netzwerkkarten durch Komma getrennt

implementation

FUNCTION GetAdapters: TLanaEnum;
VAR
  NCB        :TNCB;                   //Network Control Block
  LanaEnum   :TLanaEnum;              //Enumeration of Lan Adapters
  ReturnCode :Char;                   //Retun Code

BEGIN
 //Clear NCB Structure
 FillChar(NCB, SizeOf(NCB), 0);
 //Clear LanaEnum Stucture
 FillChar(LanaEnum, SizeOf(TLanaEnum), 0);

 //Programm NBC to ask for the number of Lana's
 NCB.ncb_Command := CHAR(NCBENUM);
 NCB.ncb_buffer  := @LanaEnum;
 NCB.ncb_length  := Sizeof(LanaEnum);

 //Ask NetBios
 ReturnCode := NetBios(@NCB);

 //Check if query was successfull, if returnCode = #0
 IF ReturnCode <> #0 THEN
 BEGIN
  LanaEnum.Length  := CHAR(0);
  LanaEnum.Lana[0] := ReturnCode;
 END;

 // Return valid LanaEnum Structure
 Result := LanaEnum;
END;

FUNCTION GetMacAddress(AdapterID : Integer) : String;
TYPE
 TAdapterStatusA = record
   Adapt : TAdapterStatus;
   NameBuff : array[0..30of TNameBuffer;
 end;
VAR
  NCB           :TNCB;                   //Network Control Block
  ReturnCode    :Char;                   //Retun Code
  AdapterStatus :TAdapterStatus;
BEGIN
 //Clear NCB Structure
 FillChar( NCB, SizeOf(NCB), 0 );

 //Programm NBC to fulfill a reset. It doublecheck if the Lana is present.
 //Can be removed, when used with GetAdapters.
 NCB.ncb_command  := Char(NCBRESET);
 NCB.ncb_lana_num := Char(AdapterID);

 //Process NCB to NetBios
 ReturnCode := NetBios(@NCB);

 //Check if query was successfull, if returnCode = #0
 IF ReturnCode <> #0 THEN
 BEGIN
  // Return the negative returncode
  // Error check would be "if ReturnCode[0]='-' Then..."
  Result:=Char(Ord(ReturnCode)*-1);
  Exit;
 END;

 //Clear NCB Structure
 FillChar( NCB, SizeOf(NCB), 0 );

 //Programm NBC to ask for the MAC associated with the LanaID
 NCB.ncb_command  := Char(NCBASTAT);
 NCB.ncb_lana_num := Char(AdapterID);
 NCB.ncb_buffer   := @AdapterStatus;
 NCB.ncb_length   := SizeOf(AdapterStatus);
 StrCopy(NCB.ncb_callname, '* ' );

 //Process NCB to NetBios
 ReturnCode := Netbios( @NCB );

 //Check if query was successfull, if returnCode = #0
 IF ReturnCode <> #0 THEN
 BEGIN
  // Return the negative returncode
  Result:=Char(Ord(ReturnCode)*-1);
  Exit;
 END;

 //Return formated MAC
 result:=Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',
                [ORD(AdapterStatus.Adapter_address[0]),
                 ORD(AdapterStatus.Adapter_address[1]),
                 ORD(AdapterStatus.Adapter_address[2]),
                 ORD(AdapterStatus.Adapter_address[3]),
                 ORD(AdapterStatus.Adapter_address[4]),
                 ORD(AdapterStatus.Adapter_address[5])])
end;

function MACAddress():string;
VAR
 L_Enum :TLanaEnum;
 i,cnt : integer ;
 s : string ;
BEGIN
  s := '' ;
  L_Enum := GetAdapters;                        { enumerate lanas for WIN NT }
  cnt := byte(L_Enum.Length) ;
  for i:= 1 to cnt do begin
    s := s + getMACaddress(BYTE(L_Enum.Lana[i-1]));
    if i<cnt then s:=s+',';
  end ;
  result := s;
end ;

end.


Nun bin ich dabei, diese Routine für XE zu portieren und habe bislang die Char in AnsiChar-Variablen gewandelt:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
unit MyLAN;

interface

uses SysUtils,nb30;

function MACAddress():string;
// Liefert die MAC-Adressen aller Netzwerkkarten durch Komma getrennt

implementation

FUNCTION GetAdapters: TLanaEnum;
VAR
  NCB        :TNCB;                   //Network Control Block
  LanaEnum   :TLanaEnum;              //Enumeration of Lan Adapters
  ReturnCode :AnsiChar;                   //Retun Code

BEGIN
 //Clear NCB Structure
 FillChar(NCB, SizeOf(NCB), 0);
 //Clear LanaEnum Stucture
 FillChar(LanaEnum, SizeOf(TLanaEnum), 0);

 //Programm NBC to ask for the number of Lana's
 NCB.ncb_Command := AnsiCHAR(NCBENUM);
 NCB.ncb_buffer  := @LanaEnum;
 NCB.ncb_length  := Sizeof(LanaEnum);

 //Ask NetBios
 ReturnCode := NetBios(@NCB);

 //Check if query was successfull, if returnCode = #0
 IF ReturnCode <> #0 THEN
 BEGIN
  LanaEnum.Length  := AnsiCHAR(0);
  LanaEnum.Lana[0] := ReturnCode;
 END;

 // Return valid LanaEnum Structure
 Result := LanaEnum;
END;

FUNCTION GetMacAddress(AdapterID : Integer) : String;
TYPE
 TAdapterStatusA = record
   Adapt : TAdapterStatus;
   NameBuff : array[0..30of TNameBuffer;
 end;
VAR
  NCB           :TNCB;                   //Network Control Block
  ReturnCode    :AnsiChar;                   //Retun Code
  AdapterStatus :TAdapterStatus;
BEGIN
 //Clear NCB Structure
 FillChar( NCB, SizeOf(NCB), 0 );

 //Programm NBC to fulfill a reset. It doublecheck if the Lana is present.
 //Can be removed, when used with GetAdapters.
 NCB.ncb_command  := AnsiChar(NCBRESET);
 NCB.ncb_lana_num := AnsiChar(AdapterID);

 //Process NCB to NetBios
 ReturnCode := NetBios(@NCB);

 //Check if query was successfull, if returnCode = #0
 IF ReturnCode <> #0 THEN
 BEGIN
  // Return the negative returncode
  // Error check would be "if ReturnCode[0]='-' Then..."
  Result:=AnsiChar(Ord(ReturnCode)*-1);
  Exit;
 END;

 //Clear NCB Structure
 FillChar( NCB, SizeOf(NCB), 0 );

 //Programm NBC to ask for the MAC associated with the LanaID
 NCB.ncb_command  := AnsiChar(NCBASTAT);
 NCB.ncb_lana_num := AnsiChar(AdapterID);
 NCB.ncb_buffer   := @AdapterStatus;
 NCB.ncb_length   := SizeOf(AdapterStatus);
 StrCopy(NCB.ncb_callname, '* ' );

 //Process NCB to NetBios
 ReturnCode := Netbios( @NCB );

 //Check if query was successfull, if returnCode = #0
 IF ReturnCode <> #0 THEN
 BEGIN
  // Return the negative returncode
  Result:=AnsiChar(Ord(ReturnCode)*-1);
  Exit;
 END;

 //Return formated MAC
 result:=Format('%2.2x-%2.2x-%2.2x-%2.2x-%2.2x-%2.2x',
                [ORD(AdapterStatus.Adapter_address[0]),
                 ORD(AdapterStatus.Adapter_address[1]),
                 ORD(AdapterStatus.Adapter_address[2]),
                 ORD(AdapterStatus.Adapter_address[3]),
                 ORD(AdapterStatus.Adapter_address[4]),
                 ORD(AdapterStatus.Adapter_address[5])])
end;

function MACAddress():string;
VAR
 L_Enum :TLanaEnum;
 i,cnt : integer ;
 s : string ;
BEGIN
  s := '' ;
  L_Enum := GetAdapters;                        { enumerate lanas for WIN NT }
  cnt := byte(L_Enum.Length) ;
  for i:= 1 to cnt do begin
    s := s + getMACaddress(BYTE(L_Enum.Lana[i-1]));
    if i<cnt then s:=s+',';
  end ;
  result := s;
end ;

end.


Offenbar habe ich aber etwas übersehen.

Der NetBios-Aufruf in Zeile 30 endet mit dem Errorcode #7

Ich kenne mich einfach nicht mehr aus, wenn char<>char, word<>word und integer<>integer ist.

Kann mir jemand auf die Sprünge helfen?

Danke.

Moderiert von user profile iconChristian S.: Code- durch Delphi-Tags ersetzt
Moderiert von user profile iconNarses: Titel erweitert.


baumina - Di 10.05.16 16:13

Ist die Unit nb30 ebenfalls umgestellt worden?


Narses - Di 10.05.16 18:36

Moin!

Ohne jetzt im Detail auf die hier angesprochene Portierungsproblematik einzugehen (leider auch für die gleich folgende Referenz nicht ganz unzutreffend :?, sorry, hab nicht viel Zeit): Ich halte es für mittlerweile ... sportlich :zwinker: ... sich auf die Anwesenheit von NETBIOS-API-Funktionen in modernen Betriebssystemen zu verlassen (diese nb30-Unit wird wohl eine Kapsel für die NETBIOS-API sein, schätze ich mal :lupe:). Heute schon nur noch mit hoher (und nicht mehr mit an Sicherheit grenzender) Wahrscheinlichkeit anzutreffen, aber das wird nicht mehr ewig so bleiben. :idea:

Vermutlich ist es besser, man geht ähnlich wie hier beschrieben [https://www.entwickler-ecke.de/viewtopic.php?t=94099] vor und holt sich die lokale(n) IP-Adresse(n), die man dann per ARP-API (IPHELPER) in eine MAC-Adresse "übersetzt" (immer dran denken: man kann auch mehr als eine Netzwerkkarte haben, also auch mehr als eine MAC-Adresse!).

Ein anderer Ansatz könnte auch Suche in: Delphi-Forum, Delphi-Library WMI sein, hier dürften sich ebenfalls mit hoher Wahrscheinlichkeit auch in Zukunft funktionsfähig die MAC-Adresse(n) ermitteln lassen. :les:

cu
Narses


Manfred - Di 10.05.16 21:17

Danke für die schnelle Antwort.

nb30 kapselt die netapi32.dll und bietet dafür Konstanten und Types an. Die Versionen für D6 und XE sind nicht identisch, da auch hier Char in AnsiChar etc. gewandelt wurden. Andere Abweichungen habe ich nicht gefunden.

Die API ist jedoch definitiv vorhanden, da die "alte" Version einwandfrei funktioniert. Und das lückenlos von Win XP bis 10 bei 32 und 64 Bit.

Mit der von Narses angebotenen Lösung würde ich gerne liebäugeln, doch es gibt ein Problem: Ich benötige ausschließlich die eigenen MAC-Adressen, nicht alle aus dem Netz.

---

Nachtrag: Auch die Unit Ping ist unter XE offenbar nicht auffindbar


Narses - Di 10.05.16 22:35

Moin!

user profile iconManfred hat folgendes geschrieben Zum zitierten Posting springen:
Ich benötige ausschließlich die eigenen MAC-Adressen, nicht alle aus dem Netz.
Davon war auch nicht die Rede. ;) Die lokale(n) IP-Adresse(n) ermitteln (also die eigene(n)) und dann über die ARP-API die MAC dazu bestimmen lassen. :idea:

Wie gesagt, WMI geht auch. :nixweiss:

cu
Narses


Manfred - Mi 11.05.16 09:54

Hallo Narses,

im Prinzip habe ich mit Deiner Unit aber das selbe Problem: Es lässt sich bei mit nicht unter XE compilieren, da die Unit "Ping" fehlt.
Diese ist offenbar für "GetIPByName" erforderlich.


Narses - Mi 11.05.16 10:58

Moin!

user profile iconManfred hat folgendes geschrieben Zum zitierten Posting springen:
da die Unit "Ping" fehlt.
In dem ARP-MAC-Beitrag war doch ein Link auf den Ping-Thread [https://www.entwickler-ecke.de/topic_ICMPEchoAPI+Ping+WrapperUnit_53259.html]... :gruebel: :lupe:

cu
Narses

//EDIT: WMI ist auch eine wirklich gute und zuverlässige Quelle für sowas. Gib mal in einer Kommandozeile das hier ein: ;) (als Demo)

Quelltext
1:
wmic nic get description,macaddress                    


Manfred - Mi 11.05.16 14:34

Tut mir leid, hatte ich völlig übersehen.

Danke.

Das mit der Kommandozeile ist in der Tat genau das, was ich suche. Nun muss ich wmic nur noch ins Projekt integrieren.