アクセスポイント一覧と信号強度を取得する

昔の日記にコメントがあったので、それに関連して、Windowsでアクセスポイントの一覧と信号強度を取得する方法について書いてみたり(・∀・)


これらの情報の取得にはDeviceIoControl()を使うんですが、そこに渡すハンドルをオープンするためにネットワークアダプタの情報*1が必要になります。
この情報の取得は、レジストリから取得するか、IP Helper APIのGetInterfaceInfo()を使って取得するかって所ですが。
あと、WMIでも行けるかな(・∀・)?
とりあえず、お手軽なところで、GetInterfaceInfo()を使うやりかたで。

IP_INTERFACE_INFO* pIfTable;
ULONG dwOutBufLen;

if ( ::GetInterfaceInfo( NULL, &dwOutBufLen ) == ERROR_INSUFFICIENT_BUFFER )
{
     pIfTable = (IP_INTERFACE_INFO*)malloc( dwOutBufLen );
}

DWORD dwRet = ::GetInterfaceInfo( pIfTable, &dwOutBufLen );
if ( dwRet == NO_ERROR )
{
    // pIfTable->NumAdapters件のデータが取得できたので、
    // 該当するアダプタのpIfTable->Adapter[ index ].Nameから
    // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}の部分を取得
}

次にハンドルのオープンですが、変数szAdapterNameが上で取得した情報だとしてこんな感じになります。

TCHAR szFileName[ MAX_PATH ];
_stprintf( szFileName, _T( "\\\\.\\%s" ), szAdapterName );

HANDLE hDevice = ::CreateFile( szFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
                               NULL, OPEN_EXISTING, 0, NULL);

そしてこのハンドルからアクセスポイント一覧の情報(NDIS_802_11_BSSID_LIST)を取得します。
格納用のバッファを用意して、OID_802_11_BSSID_LIST_SCANによりスキャン、OID_802_11_BSSID_LISTでスキャンした値を取得します。

#define MAX_BSSID 16
#define BUFFER_SIZE ( sizeof(NDIS_WLAN_BSSID) * MAX_BSSID + sizeof(ULONG) )

NDIS_802_11_BSSID_LIST* pBSSIDList = (NDIS_802_11_BSSID_LIST*)malloc( BUFFER_SIZE );
if ( pBSSIDList == NULL )
{
    return NULL;
}

DWORD nBytesReturned;
DWORD dwIoControlCode;

::ZeroMemory( pBSSIDList, BUFFER_SIZE );
dwIoControlCode = OID_802_11_BSSID_LIST_SCAN;

::DeviceIoControl( hDevice, IOCTL_NDIS_QUERY_GLOBAL_STATS,
                   &dwIoControlCode, sizeof(dwIoControlCode),
                   (LPVOID)NULL, 0,
                   &nBytesReturned, NULL );

// wait
::Sleep( 2000 );

::ZeroMemory( pBSSIDList, BUFFER_SIZE );
dwIoControlCode = OID_802_11_BSSID_LIST;

if ( ::DeviceIoControl( hDevice, IOCTL_NDIS_QUERY_GLOBAL_STATS,
                        &dwIoControlCode, sizeof(dwIoControlCode),
                        pBSSIDList, sizeof(NDIS_802_11_BSSID_LIST) * MAX_BSSID,
                        &nBytesReturned, NULL ) )
{
    return pBSSIDList;
}
else
{
    return NULL;
}

ウエイトを入れたりしてるので、この取得処理はバックグラウンドスレッドなんかでやってください(・ω・)
で、NDIS_802_11_BSSID_LISTからは取得したアクセスポイントの件数と、各アクセスポイントの情報(NDIS_WLAN_BSSID)が取得でき、信号強度はそのRssiメンバで参照できます。
表示用のテストコードはこんな感じですかね。

NDIS_WLAN_BSSID* pBssid= pBSSIDList->Bssid;
for( int i = 0; i < pBSSIDList->NumberOfItems; i++ )
{
    // 表示
    USES_CONVERSION;
    TRACE( _T("%s: %d\n"), A2T( (LPCSTR)pBssid->Ssid.Ssid ), pBssid->Rssi );
    
    pBssid = (NDIS_WLAN_BSSID*)( (BYTE*)pBssid + pBssid->Length );
}

これでNetwork Stumblerモドキが作れるのですよ(・∀・)

*1:{9D0D91EE-6365-4A43-B619-80F5E118E20B}みたいなの