不能用NetBios,因为如果禁用NetBios就会无法取得,也不能用SendARP,因为在Win98下不能使用.
有没有一种更可靠的办法取得Mac地址?

解决方案 »

  1.   

    sorry,没注意看我有的就是用netbios做的:(
      

  2.   

    typedef struct _ASTAT_
    {
    ADAPTER_STATUS adapt;
    NAME_BUFFER    NameBuff[30];
    } ASTAT, * PASTAT;.........................................................
    void CGetMACAddressDlg::OnGet() 
    {
    CString strIP;
    GetLocalIPAddress(strIP);
    MessageBox(GetMacAddress(strIP));

    }CString CGetMACAddressDlg::GetMacAddress(CString sNetBiosName)
    {    ASTAT Adapter;

        NCB ncb;
        UCHAR uRetCode;

        memset(&ncb, 0, sizeof(ncb));
        ncb.ncb_command = NCBRESET;
        ncb.ncb_lana_num = 0;

        uRetCode = Netbios(&ncb);

        memset(&ncb, 0, sizeof(ncb));
        ncb.ncb_command = NCBASTAT;
        ncb.ncb_lana_num = 0;

        sNetBiosName.MakeUpper();

        FillMemory(ncb.ncb_callname, NCBNAMSZ - 1, 0x20);

        strcpy((char *)ncb.ncb_callname, (LPCTSTR) sNetBiosName);

        ncb.ncb_callname[sNetBiosName.GetLength()] = 0x20;
        ncb.ncb_callname[NCBNAMSZ] = 0x0;

        ncb.ncb_buffer = (unsigned char *) &Adapter;
        ncb.ncb_length = sizeof(Adapter);

        uRetCode = Netbios(&ncb);
        
        CString sMacAddress;

        if (uRetCode == 0)
        {
    sMacAddress.Format(_T("%02x%02x%02x%02x%02x%02x"),
    Adapter.adapt.adapter_address[0],
                Adapter.adapt.adapter_address[1],
                Adapter.adapt.adapter_address[2],
                Adapter.adapt.adapter_address[3],
                Adapter.adapt.adapter_address[4],
                Adapter.adapt.adapter_address[5]);
        }
        return sMacAddress;
    }BOOL CGetMACAddressDlg::GetLocalIPAddress(CString & sIP)
    {
    AfxSocketInit();
    CSocket soc;
    soc.Create(NULL);
    char szHostname[256];

    if (gethostname(szHostname, sizeof(szHostname)))
    {
    TRACE(_T("Failed in call to gethostname, WSAGetLastError returns %d\n"), WSAGetLastError());
    return FALSE;
    }

    //get host information from the host name HOSTENT* pHostEnt = gethostbyname(szHostname);
    if (pHostEnt == NULL)
    {
    TRACE(_T("Failed in call to gethostbyname, WSAGetLastError returns %d\n"), WSAGetLastError());
    return FALSE;
    }

    //check the length of the IP adress
    if (pHostEnt->h_length != 4)
    {
    TRACE(_T("IP address returned is not 32 bits !!\n"));
    return FALSE;
    }

    //call the virtual callback function in a loop
    int nAdapter = 0;
    in_addr address;
    CopyMemory(&address.S_un.S_addr, pHostEnt->h_addr_list[nAdapter], pHostEnt->h_length);
    //bContinue = EnumCallbackFunction(nAdapter, address);
            
    sIP.Format(_T("%d.%d.%d.%d"), address.S_un.S_un_b.s_b1, address.S_un.S_un_b.s_b2, address.S_un.S_un_b.s_b3, address.S_un.S_un_b.s_b4);
     //   AfxMessageBox(sIP); soc.Close();

    return TRUE;}
      

  3.   

    除了用不可靠的NetBios,还有没有其它的办法? Windows会不会把MAC地址写到注册表里?
      

  4.   

    我搜过了,用的都是NetBios和SendARP.大侠救命啊,很急.
      

  5.   

    ZT第一种方法使用Microsoft的Netbios API。 这是一套通过Winsock提供底层网络支持的命令。使用Netbios的最大缺点是您必须在系统中安装了Netbios服务(如果您在windows网络中启用了文件共享的话,这就不是问题了)。除此此外,这种方法又快又准确。Netbios API只包括了一个函数,就叫做Netbios。这个函数使用网络控制块(network control block)结构作为参数,这个结构告诉函数要做什么。结构的定义如下:
      typedef struct _NCB {
        UCHAR  ncb_command;
        UCHAR  ncb_retcode;
        UCHAR  ncb_lsn;
        UCHAR  ncb_num;
        PUCHAR ncb_buffer;
        WORD   ncb_length;
        UCHAR  ncb_callname[NCBNAMSZ];
        UCHAR  ncb_name[NCBNAMSZ];
        UCHAR  ncb_rto;
        UCHAR  ncb_sto;
        void (CALLBACK *ncb_post) (struct _NCB *);
        UCHAR  ncb_lana_num;
        UCHAR  ncb_cmd_cplt;
     #ifdef _WIN64
        UCHAR  ncb_reserve[18];
     #else
        UCHAR  ncb_reserve[10];
     #endif
        HANDLE ncb_event;
    } NCB, *PNCB; 重点在于ncb_command 成员。这个成员告诉Netbios该作什么。我们使用三个命令来探测MAC地址。他们在MSDN的定义如下:
    命令描述:
    NCBENUM Windows NT/2000: 列举系统中网卡的数量。使用此命令后,ncb_buffer成员指向由LANA_ENUM结构填充的缓冲区。
    NCBENUM 不是标准的 NetBIOS 3.0 命令。NCBRESET 重置网卡。网卡在接受新的NCB命令之前必须重置。
    NCBASTAT 接受本地或远程接口卡的状态。使用此命令后,ncb_buffer成员指向由ADAPTER_STATUS结构填充的缓冲区,随后是NAME_BUFFER结构的数组。下面就是取得您系统MAC地址的步骤:
    1》列举所有的接口卡。
    2》重置每块卡以取得它的正确信息。
    3》查询接口卡,取得MAC地址并生成标准的冒号分隔格式。下面就是实例源程序。
    netbios.cpp#include <windows.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <iostream>
    #include <string>using namespace std;
    #define bzero(thing,sz) memset(thing,0,sz)bool GetAdapterInfo(int adapter_num, string &mac_addr)
    {
      // 重置网卡,以便我们可以查询
      NCB Ncb;
      memset(&Ncb, 0, sizeof(Ncb));
      Ncb.ncb_command = NCBRESET;
      Ncb.ncb_lana_num = adapter_num;
      if (Netbios(&Ncb) != NRC_GOODRET) {
        mac_addr = "bad (NCBRESET): ";
        mac_addr += string(Ncb.ncb_retcode);
        return false;
      }  // 准备取得接口卡的状态块
      bzero(&Ncb,sizeof(Ncb);
      Ncb.ncb_command = NCBASTAT;
      Ncb.ncb_lana_num = adapter_num;
      strcpy((char *) Ncb.ncb_callname, "*");
      struct ASTAT
      {
        ADAPTER_STATUS adapt;
        NAME_BUFFER NameBuff[30];
      } Adapter;
      bzero(&Adapter,sizeof(Adapter));
      Ncb.ncb_buffer = (unsigned char *)&Adapter;
      Ncb.ncb_length = sizeof(Adapter);  // 取得网卡的信息,并且如果网卡正常工作的话,返回标准的冒号分隔格式。
      if (Netbios(&Ncb) == 0)
      {
        char acMAC[18];
        sprintf(acMAC, "%02X:%02X:%02X:%02X:%02X:%02X",
                int (Adapter.adapt.adapter_address[0]),
                int (Adapter.adapt.adapter_address[1]),
                int (Adapter.adapt.adapter_address[2]),
                int (Adapter.adapt.adapter_address[3]),
                int (Adapter.adapt.adapter_address[4]),
                int (Adapter.adapt.adapter_address[5]));
        mac_addr = acMAC;
        return true;
      }
      else
      {
        mac_addr = "bad (NCBASTAT): ";
        mac_addr += string(Ncb.ncb_retcode);
        return false;
      }
    }int main()
    {
      // 取得网卡列表
      LANA_ENUM AdapterList;
      NCB Ncb;
      memset(&Ncb, 0, sizeof(NCB));
      Ncb.ncb_command = NCBENUM;
      Ncb.ncb_buffer = (unsigned char *)&AdapterList;
      Ncb.ncb_length = sizeof(AdapterList);
      Netbios(&Ncb);  // 取得本地以太网卡的地址
      string mac_addr;
      for (int i = 0; i < AdapterList.length - 1; ++i)
      {
        if (GetAdapterInfo(AdapterList.lana[i], mac_addr))
        {
          cout << "Adapter " << int (AdapterList.lana[i]) <<
                  "'s MAC is " << mac_addr << endl;
        }
        else
        {
          cerr << "Failed to get MAC address! Do you" << endl;
          cerr << "have the NetBIOS protocol installed?" << endl;
          break;
        }
      }  return 0;
    }
      

  6.   

    第二种方法-使用COM GUID API
    这种方法使用COM API创建一个GUID(全局唯一标识符)并从那里继承MAC地址。GUID通常用来标识COM组件以及系统中的其他对象。它们是由MAC地址(结合其他东西)计算得来的,表面上MAC地址就包含在其中。我说表面上是因为事实上并没有包含。
    我提供这种方法更多的是为了作为反面教材。您也许用这种方法能够得到MAC地址,但有时候您只会得到随机的十六进制数值。
    下面的例子十分简单,无需多讲。我们使用CoCreateGuid创建GUID,并将最后六个字节放入字符串中。它们可能是MAC地址,但并不是必然的。uuid.cpp
    #include <windows.h>
    #include <iostream>
    #include <conio.h>using namespace std;int main()
    {
        cout << "MAC address is: ";    // 向COM要求一个UUID。如果机器中有以太网卡,
        // UUID最后的六个字节(Data4的2-7字节)应该是本地以太网卡的MAC地址。
        GUID uuid;
        CoCreateGuid(&uuid);
        // Spit the address out
        char mac_addr[18];
        sprintf(mac_addr,"%02X:%02X:%02X:%02X:%02X:%02X",
                uuid.Data4[2],uuid.Data4[3],uuid.Data4[4],
                uuid.Data4[5],uuid.Data4[6],uuid.Data4[7]);
        cout << mac_addr << endl;
        getch();
        return 0;
    }
      

  7.   

    第三种方法- 使用SNMP扩展API
    我要讨论的第三种方法是使用Windows的SNMP(简单网络管理协议)扩展来取得MAC地址。在我的经验里,这个协议很简单。代码也是直勾勾的向前的。基本步骤和Netbios相同:
    1》取得网卡列表
    2》查询每块卡的类型和MAC地址
    3》保存当前网卡
    我个人对SNMP了解不多,但如我刚刚所言,代码十分清楚。snmp.cpp
    #include <snmp.h>
    #include <conio.h>
    #include <stdio.h>typedef bool(WINAPI * pSnmpExtensionInit) (
            IN DWORD dwTimeZeroReference,
            OUT HANDLE * hPollForTrapEvent,
            OUT AsnObjectIdentifier * supportedView);typedef bool(WINAPI * pSnmpExtensionTrap) (
            OUT AsnObjectIdentifier * enterprise,
            OUT AsnInteger * genericTrap,
            OUT AsnInteger * specificTrap,
            OUT AsnTimeticks * timeStamp,
            OUT RFC1157VarBindList * variableBindings);typedef bool(WINAPI * pSnmpExtensionQuery) (
            IN BYTE requestType,
            IN OUT RFC1157VarBindList * variableBindings,
            OUT AsnInteger * errorStatus,
            OUT AsnInteger * errorIndex);typedef bool(WINAPI * pSnmpExtensionInitEx) (
            OUT AsnObjectIdentifier * supportedView);void main()
    {
      HINSTANCE m_hInst;
      pSnmpExtensionInit m_Init;
      pSnmpExtensionInitEx m_InitEx;
      pSnmpExtensionQuery m_Query;
      pSnmpExtensionTrap m_Trap;
      HANDLE PollForTrapEvent;
      AsnObjectIdentifier SupportedView;
      UINT OID_ifEntryType[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 3};
      UINT OID_ifEntryNum[] = {1, 3, 6, 1, 2, 1, 2, 1};
      UINT OID_ipMACEntAddr[] = {1, 3, 6, 1, 2, 1, 2, 2, 1, 6};
      AsnObjectIdentifier MIB_ifMACEntAddr =
        { sizeof(OID_ipMACEntAddr)  sizeof(UINT), OID_ipMACEntAddr };
      AsnObjectIdentifier MIB_ifEntryType =
        {sizeof(OID_ifEntryType)  sizeof(UINT), OID_ifEntryType};
      AsnObjectIdentifier MIB_ifEntryNum =
        {sizeof(OID_ifEntryNum)  sizeof(UINT), OID_ifEntryNum};
      RFC1157VarBindList varBindList;
      RFC1157VarBind varBind[2];
      AsnInteger errorStatus;
      AsnInteger errorIndex;
      AsnObjectIdentifier MIB_NULL = {0, 0};
      int ret;
      int dtmp;
      int i = 0, j = 0;
      bool found = false;
      char TempEthernet[13];
      m_Init = NULL;
      m_InitEx = NULL;
      m_Query = NULL;
      m_Trap = NULL;  /* 载入SNMP DLL并取得实例句柄 */
      m_hInst = LoadLibrary("inetmib1.dll");
      if (m_hInst < (HINSTANCE) HINSTANCE_ERROR)
      {
        m_hInst = NULL;
        return;
      }
      m_Init =
        (pSnmpExtensionInit) GetProcAddress(m_hInst, "SnmpExtensionInit");
      m_InitEx =
        (pSnmpExtensionInitEx) GetProcAddress(m_hInst,
                                              "SnmpExtensionInitEx");
      m_Query =
        (pSnmpExtensionQuery) GetProcAddress(m_hInst,
                                             "SnmpExtensionQuery");
      m_Trap =
        (pSnmpExtensionTrap) GetProcAddress(m_hInst, "SnmpExtensionTrap");
      m_Init(GetTickCount(), &PollForTrapEvent, &SupportedView);  /* 初始化用来接收m_Query查询结果的变量列表 */
      varBindList.list = varBind;
      varBind[0].name = MIB_NULL;
      varBind[1].name = MIB_NULL;  /* 在OID中拷贝并查找接口表中的入口数量 */
      varBindList.len = 1;        /* Only retrieving one item */
      SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryNum);
      ret =
        m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
                &errorIndex);
      printf("# of adapters in this system : %in",
           varBind[0].value.asnValue.number);
      varBindList.len = 2;  /* 拷贝OID的ifType-接口类型 */
      SNMP_oidcpy(&varBind[0].name, &MIB_ifEntryType);  /* 拷贝OID的ifPhysAddress-物理地址 */
      SNMP_oidcpy(&varBind[1].name, &MIB_ifMACEntAddr);  do
      {    /* 提交查询,结果将载入 varBindList。
           可以预料这个循环调用的次数和系统中的接口卡数量相等 */
        ret =
          m_Query(ASN_RFC1157_GETNEXTREQUEST, &varBindList, &errorStatus,
                  &errorIndex);
        if (!ret)
          ret = 1;
        else
            /* 确认正确的返回类型 */
          ret =
              SNMP_oidncmp(&varBind[0].name, &MIB_ifEntryType,
                           MIB_ifEntryType.idLength); if (!ret) {
        j++;
        dtmp = varBind[0].value.asnValue.number;
        printf("Interface #%i type : %in", j, dtmp);    /* Type 6 describes ethernet interfaces */
        if (dtmp == 6)
        {      /* 确认我们已经在此取得地址 */
          ret =
              SNMP_oidncmp(&varBind[1].name, &MIB_ifMACEntAddr,
                           MIB_ifMACEntAddr.idLength);
          if ((!ret) && (varBind[1].value.asnValue.address.stream != NULL))
          {
            if((varBind[1].value.asnValue.address.stream[0] == 0x44)
              && (varBind[1].value.asnValue.address.stream[1] == 0x45)
              && (varBind[1].value.asnValue.address.stream[2] == 0x53)
              && (varBind[1].value.asnValue.address.stream[3] == 0x54)
              && (varBind[1].value.asnValue.address.stream[4] == 0x00))
            {
              /* 忽略所有的拨号网络接口卡 */
              printf("Interface #%i is a DUN adaptern", j);
              continue;
            }
            if ((varBind[1].value.asnValue.address.stream[0] == 0x00)
                && (varBind[1].value.asnValue.address.stream[1] == 0x00)
                && (varBind[1].value.asnValue.address.stream[2] == 0x00)
                && (varBind[1].value.asnValue.address.stream[3] == 0x00)
                && (varBind[1].value.asnValue.address.stream[4] == 0x00)
                && (varBind[1].value.asnValue.address.stream[5] == 0x00))
            {
              /* 忽略由其他的网络接口卡返回的NULL地址 */
              printf("Interface #%i is a NULL addressn", j);
              continue;
            }
            sprintf(TempEthernet, "%02x%02x%02x%02x%02x%02x",
                    varBind[1].value.asnValue.address.stream[0],
                    varBind[1].value.asnValue.address.stream[1],
                    varBind[1].value.asnValue.address.stream[2],
                    varBind[1].value.asnValue.address.stream[3],
                    varBind[1].value.asnValue.address.stream[4],
                    varBind[1].value.asnValue.address.stream[5]);
            printf("MAC Address of interface #%i: %sn", j,
                   TempEthernet);}
          }
        }
      } while (!ret);         /* 发生错误终止。 */
      getch();  FreeLibrary(m_hInst);
      /* 解除绑定 */
      SNMP_FreeVarBind(&varBind[0]);
      SNMP_FreeVarBind(&varBind[1]);
    }
      

  8.   

    安装一个platform sdk可以得到 IP helper,然后运用里面的GetAdaptersInfo() 就可以得到网卡的很多信息,当然也包括了MAC地址
      

  9.   

    使用GetAdaptersInfo()获取多块网卡的MAC地址,实现如下:(需要安装SDK)#include <Iphlpapi.h>IP_ADAPTER_INFO * FixedInfo;
    ULONG ulOutBufLen;
    DWORD dwRetVal;
    CString strMsg;
    CString strOutPut;
    FixedInfo = (IP_ADAPTER_INFO *) GlobalAlloc( GPTR, sizeof( IP_ADAPTER_INFO ));
    ulOutBufLen = sizeof( IP_ADAPTER_INFO );if( ERROR_BUFFER_OVERFLOW == GetAdaptersInfo(FixedInfo,&ulOutBufLen))
    {
    GlobalFree( FixedInfo );
    FixedInfo =(IP_ADAPTER_INFO *) GlobalAlloc( GPTR, ulOutBufLen );
    }
    if ( dwRetVal = GetAdaptersInfo( FixedInfo, &ulOutBufLen ) )
    {
    strMsg.Format( 
    "Call to GetAdaptersInfo failed. Return Value: %08x\r\n", 
    dwRetVal );
    strOutPut+=strMsg;
    }
    else
    {
    do
    {
    strMsg.Format( 
    "MAC Address: %02x-%02x-%02x-%02x-%02x-%02x\r\n",
    FixedInfo->Address[0],
    FixedInfo->Address[1],
    FixedInfo->Address[2],
    FixedInfo->Address[3],
    FixedInfo->Address[4],
    FixedInfo->Address[5]);
    strOutPut+=strMsg;
    strOutPut+="\r\n--------------------------------------\r\n";
    FixedInfo = FixedInfo->Next;
    } while (FixedInfo);
    }AfxMessageBox(strOutPut);