windws下取MAC地址的三种方法

方法一:利用NetBIOS API来获取MAC
缺陷:依赖于NetBIOS协议,这个协议现在的网卡有些已经不支持了,所以有些情况下会取不到(不推荐使用这种方法)

HRESULT GetMAC( char * pszMAC, unsigned  int  uCount)
...
{
    
if (!pszMAC)
        
return E_INVALIDARG;
    
if (uCount < SIZE_OF_MAC)
        
return E_INVALIDARG;

    HRESULT hr 
= E_FAIL;

    NCB Ncb;
    UCHAR uRetCode;
    LANA_ENUM   lenum;
    ASTAT        Adapter;

    memset( 
&Ncb, 0sizeof(Ncb) );
    Ncb.ncb_command 
= NCBENUM;
    Ncb.ncb_buffer 
= (UCHAR *)&lenum;
    Ncb.ncb_length 
= sizeof(lenum);
    uRetCode 
= Netbios( &Ncb );
    printf( 
"The NCBENUM return code is: 0x%x  ", uRetCode );
    
    
//for(i=0; i < lenum.length ;i++)
    ...{
        memset( 
&Ncb, 0sizeof(Ncb) );
        Ncb.ncb_command 
= NCBRESET;
        Ncb.ncb_lana_num 
= (unsigned char)lenum.lana;
        
        uRetCode 
= Netbios( &Ncb );
        printf( 
"The NCBRESET on LANA %d return code is: 0x%x  ",
            lenum.lana, uRetCode );
        
        memset( 
&Ncb, 0sizeof (Ncb) );
        Ncb.ncb_command 
= NCBASTAT;
        Ncb.ncb_lana_num 
= (unsigned char)lenum.lana;
        
        strcpy( (
char*)Ncb.ncb_callname,  "*               " );
        Ncb.ncb_buffer 
= (unsigned char *&Adapter;
        Ncb.ncb_length 
= sizeof(Adapter);
        
        uRetCode 
= Netbios( &Ncb );
        printf( 
"The NCBASTAT on LANA %d return code is: 0x%x  ",
            lenum.lana, uRetCode );
        
if ( uRetCode == 0 )
        ...
{
            memset(pszMAC, 
0, uCount);
            sprintf(pszMAC, 
                    
"%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] );
            hr 
= S_OK;
        }

    }
    
    
    
return hr;
}


方法二:ipconfig猥琐法取MAC地址
通过对ipconfig /all的输出进行字条串查找来得到MAC。
缺陷:不能通用各语言版本的Windows系统。我试过,因为w2k和xp下用ipconfig /all显示出来都是英文的所以可以通用,但vista不同语言就不一样了。(不推荐使用这种方法)
//网卡MAC地址的前导信息
const char szSearchMAC_EN[MAX_PATH] = "Physical Address. . . . . . . . . : ";
const char szSearchMAC_CN[MAX_PATH] = "物理地址. . . . . . . . . . . . . : ";

// 省略号 ... ...

BOOL __getIpconfigOutput( char *  pszOutput, unsigned  int  uCount)
{
    BOOL bRet 
= FALSE; 
    SECURITY_ATTRIBUTES sa;
    HANDLE hReadPipe,hWritePipe;
    
    sa.nLength 
= sizeof(SECURITY_ATTRIBUTES);
    sa.lpSecurityDescriptor 
= NULL;
    sa.bInheritHandle 
= TRUE;
    
    
// 创建管道
    bRet = CreatePipe(&hReadPipe, &hWritePipe, &sa, 0);
    
if(!bRet)
    
{
        
return FALSE;
    }

    
    STARTUPINFO si;                
// 控制命令行窗口信息
    PROCESS_INFORMATION pi;        // 返回进程信息
    
    si.cb 
= sizeof(STARTUPINFO);
    GetStartupInfo(
&si);
    si.hStdError 
= hWritePipe;
    si.hStdOutput 
= hWritePipe;
    si.wShowWindow 
= SW_HIDE; //隐藏命令行窗口
    si.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    
    
// 创建获取命令行输出的进程
    bRet = CreateProcess (NULL, szFetCmd, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi );
    
if (bRet)
    
{
        WaitForSingleObject (pi.hProcess, INFINITE);

        unsigned 
long count;
        memset(pszOutput, 
0x00, uCount);
        bRet 
= ReadFile(hReadPipe, pszOutput, uCount, &count, 0);
    }

    
    
//关闭所有的句柄
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    CloseHandle(hWritePipe);
    CloseHandle(hReadPipe);
    
    
return bRet;
}


BOOL __analyseMACOutput(
char *  pszOutput,  char *  pszMAC, unsigned  int  uCount)
{
    
if (!pszOutput
        
|| !pszMAC)
        
return FALSE;

    KString strBuffer;
    strBuffer 
= pszOutput;
    
long ipos;
    
if (-1 != (ipos = strBuffer.find(szSearchMAC_EN)))
    
{
        
// 提取MAC地址串
        strBuffer = strBuffer.substr(ipos+strlen(szSearchMAC_EN));
        strBuffer 
= strBuffer.substr(0, MAC_STRING_LENGTH);        
    }

    
else if ( -1 != (ipos = strBuffer.find(szSearchMAC_CN)))
    
{
        strBuffer 
= strBuffer.substr(ipos+strlen(szSearchMAC_CN));
        strBuffer 
= strBuffer.substr(0, MAC_STRING_LENGTH);
    }


    strncpy(pszMAC, strBuffer.c_str(), strBuffer.size() 
> uCount ? uCount : strBuffer.size());

    
return TRUE;
}


HRESULT __getMAC(
char * pszMAC, unsigned  int  uCount)
{
    
if (!pszMAC)
        
return E_INVALIDARG;
    
if (uCount < MAC_STRING_LENGTH)
        
return E_INVALIDARG;

    HRESULT hr 
= S_OK;
   
char szBuffer[MAX_COMMAND_SIZE+1]; //放置命令行输出缓冲区

    memset(pszMAC, 
0x00sizeof(pszMAC));
    BOOL bRet 
= __getIpconfigOutput(szBuffer, MAX_COMMAND_SIZE);
    
if (!bRet)
        
return E_FAIL;
    
    
char szAnalysedMAC[MAX_PATH] = {0};
    bRet 
= __analyseMACOutput(szBuffer, szAnalysedMAC, MAX_PATH);
    
if (!bRet)
        
return E_FAIL;

    int j = 0;
    
int nLen = strlen(szAnalysedMAC);
    
for(int i=0; i < nLen; i++)
    
{
        
if(szAnalysedMAC[i] != '-')
        
{
            pszMAC[j] 
= szAnalysedMAC[i];
            j
++;
        }

    }


    
return S_OK;
}

方法三,IP Helper API取MAC
这种方法我喜欢,其实就是用IDA分析ipconfig.exe时发现他用到一系列api叫iphlpapi,感觉应该就是用这么个东东来取网卡相关的信息。查msdn得知:
Platform SDK: IP Helper下有一个叫GetAdaptersInfo的api可以得到网卡的物理地址。
BOOL KLocalUUID::__getMacByIPHelper(char* pszMac, unsigned int uCount)
{
    if (!pszMac
        || uCount <= 0)
        return FALSE;

    PIP_ADAPTER_INFO pAdapterInfo = NULL;
    DWORD dwRetVal = 0;
   
    pAdapterInfo = (IP_ADAPTER_INFO *) malloc( sizeof(IP_ADAPTER_INFO) );
    ULONG ulOutBufLen = sizeof(IP_ADAPTER_INFO);
   
    // Make an initial call to GetAdaptersInfo to get
    // the necessary size into the ulOutBufLen variable
    if (GetAdaptersInfo( pAdapterInfo, &ulOutBufLen) == ERROR_BUFFER_OVERFLOW)
    {
        free (pAdapterInfo);
        pAdapterInfo = (IP_ADAPTER_INFO *) malloc (ulOutBufLen);
    }   
   
    if ((dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen)) == NO_ERROR)
    {
        char* p = pszMac;
        int nWriteLen = 0;   
        while (pAdapterInfo)
        {
            ULONG i = 0;
            while(i < pAdapterInfo->AddressLength)
            {
                if (nWriteLen >= (uCount - 2))
                    break;
                sprintf(p, "%02x", pAdapterInfo->Address[i]);
                nWriteLen += strlen(p);
                p += strlen(p);
                i++;
            }
           
            pAdapterInfo = pAdapterInfo->Next;
        }
    }
    else
    {
        printf("Call to GetAdaptersInfo failed./n");
    }
   
    printf("MAC : %s/n", pszMac);
    free (pAdapterInfo);
   
    return TRUE;
}

结束语:
这里代码都是源代码文件中的关键部分,需要的头文件或是一些无关紧要的宏定义我就没贴出来。
取MAC地址应该会时常用到,一次弄明白了,下次就舒服了~~
+_+

// ---------------------------------------end line by debehe---------------------------------------

你可能感兴趣的:(windws下取MAC地址的三种方法)