EternalWindows
RAS / 電話帳エントリ

RASを学ぶにあたって、電話帳エントリの概念は避けては通れないものです。 ダイヤルアップ接続では、その接続先がプロバイダであったり会社や学校であったりと様々ですが、 これら各々の接続先はRASの世界でエントリという枠に区切られることになります。 全てのエントリは名前を持ち、同じくそれに接続するために必要な一連の情報を含んでおり、 電話帳ファイルと呼ばれるファイルの電話帳エントリとして記述されることになっています。 基本的には電話帳エントリはRASプログラミングのみで扱われるものですが、 たとえばその存在は、インターネットオプションの接続タブや、 マイネットワークのネットワーク接続から確認することができます。

電話帳エントリが作成される方法にはいくつかの種類があります。 最も簡単な例であれば、NTTなどより配布されたCD_ROMをセットし、 そこにプロバイダより提供されているアカウント名やパスワード、 ネームサーバーのアドレスなどを入力して完了する場合があります。 このような場合は、専用の接続ツールが提供されることもあるでしょう。 また、インターネットオプションの接続タブのセットアップからも、 プロバイダの電話番号などを入力することでエントリを作成することができます。 この2つ目の方法ならばたとえプロバイダと契約していなくても、 何か文字を入力さえすれば、ダイヤルはできないもののエントリ自体は作成できますから、 ダイヤルアップ接続の環境がない方でもいくつかのRAS関数の仕組みを知ることができます。

それでは、実際にRASを利用したプログラミングに入っていきたいと思います。 まず、最終的にダイヤルするにあたって必要なのはエントリ名ですから、 それらがどれだけ電話帳ファイルに存在するのかを列挙することにしましょう。 次に示すRasEnumEntriesは、複数のエントリ名を1つのバッファに格納します。

DWORD RasEnumEntries(
  LPCTSTR reserved,
  LPTCSTR lpszPhonebook,
  LPRASENTRYNAME lprasentryname,
  LPDWORD lpcb,
  LPDWORD lpcEntries
);

reservedは、予約されているためNULLを指定します。 lpszPhonebookは、電話帳ファイルのフルパスを指定します。 NULLを指定すると、AllUsersプロファイル及び現在のユーザーのプロファイルに 存在する電話帳ファイル内のエントリが列挙されます。 lprasentrynameは、複数のRASENTRYNAME構造体からなる配列を受け取るためのアドレスを指定します。 構造体のバージョンを表すために、最初の要素のRASENTRYNAME構造体のdwSizeメンバを sizeof(RASENTRYNAME)で初期化する必要があります。 lpcbは、lprasentrynameのサイズを格納した変数のアドレスを指定します。 関数から制御が返ると、全てのRASENTRYNAME構造体を格納できるだけのサイズが返ります。 lpcEntriesは、電話帳エントリの数を受け取ります。 RAS関数の戻り値は、成功時には常に0が返るようになっています。

RASENTRYNAME構造体は、次のように定義されています。

typedef struct _RASENTRYNAME { 
  DWORD  dwSize; 
  TCHAR  szEntryName[RAS_MaxEntryName + 1]; 
#if (WINVER >= 0x500)
  DWORD dwFlags;
  TCHAR  szPhonebookPath[MAX_PATH + 1];
#endif
} RASENTRYNAME; 

dwSizeは、構造体のサイズを格納します。 szEntryNameは、電話帳エントリのエントリ名が格納されます。 dwFlagsは、取得したエントリを含む電話帳ファイルが、 どのユーザープロファイルに格納されているのかを示す定数が格納されます。 REN_AllUsersの場合はAllUsersプロファイル、REN_Userならば現在のユーザーのプロファイルです。 szPhonebookPathは、取得したエントリを含む電話帳ファイルのフルパスが格納されます。 基本的には、1つの電話帳ファイルに含まれるエントリのみを列挙することになるため、 szEntryName以外のメンバは、どのエントリでも同一の値になると思われます。

今回のプログラムは、RasEnumEntriesで初期化したRASENTRYNAME構造体の szEntryNameメンバの値を、メッセージボックスで順に表示していきます。 RASを利用する場合は、ras.hのインクルードとrasapi32.libのリンクが必要です。

#include <windows.h>
#include <ras.h>

#pragma comment (lib, "rasapi32.lib")

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	DWORD          i;
	DWORD          dwEntries;
	DWORD          dwBufferSize;
	RASENTRYNAME   tmp;
	LPRASENTRYNAME lpEntryName;

	tmp.dwSize   = sizeof(RASENTRYNAME);
	dwBufferSize = sizeof(RASENTRYNAME);
	RasEnumEntries(NULL, NULL, &tmp, &dwBufferSize, &dwEntries);
	
	lpEntryName = (LPRASENTRYNAME)HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
	lpEntryName->dwSize = sizeof(RASENTRYNAME);
	RasEnumEntries(NULL, NULL, lpEntryName, &dwBufferSize, &dwEntries);
	
	for (i = 0; i < dwEntries; i++) {
		MessageBox(NULL, lpEntryName->szEntryName, TEXT("OK"), MB_OK);
		lpEntryName++;
	}

	HeapFree(GetProcessHeap(), 0, lpEntryName);

	return 0;
}

RasEnumEntriesの2回の呼び出しは、必要な分だけのRASENTRYNAME構造のサイズが 事前に分からないことから生じています。 このようなサイズ取得の目的のみで関数を呼び出すという例はよくあるものですが、 RasEnumEntriesには少し融通が利かないところがあります。

tmp.dwSize   = sizeof(RASENTRYNAME);
dwBufferSize = sizeof(RASENTRYNAME);
RasEnumEntries(NULL, NULL, &tmp, &dwBufferSize, &dwEntries);

RasEnumEntriesの第3引数は、たとえ第4引数でサイズを取得する目的であっても、 必ずRASENTRYNAME構造体のアドレスを指定しなければなりません。 さらに、この関数はdwSizeメンバの初期化を必要とするとともに、 第3引数にRASENTRYNAME構造体を指定するがために、 第4引数の変数にも構造体のサイズを指定する必要が出てくるのです。 気持ちとしては第3引数にNULLを指定し、変数の初期化なしでサイズを 取得したいところですが、そのようにはいかないようです。

lpEntryName = (LPRASENTRYNAME)HeapAlloc(GetProcessHeap(), 0, dwBufferSize);
lpEntryName->dwSize = sizeof(RASENTRYNAME);
RasEnumEntries(NULL, NULL, lpEntryName, &dwBufferSize, &dwEntries);

取得したサイズを基に、複数のRASENTRYNAME構造体を格納できるだけのメモリを確保します。 最初の要素にはdwSizeメンバに構造体のサイズを代入しておく必要があります。

for (i = 0; i < dwEntries; i++) {
	MessageBox(NULL, lpEntryName->szEntryName, TEXT("OK"), MB_OK);
	lpEntryName++;
}

エントリ名を順に表示していきます。 lpEntryName++とすることで配列の次の要素にアクセスすることができます。 RASはVPNサーバーへのアクセスに使うエントリも電話帳ファイルに含んでいるので、 VPNのエントリが表示されることもあります。

電話帳ファイルの正体

電話帳ファイルは各々のエントリの情報を含んでおり、 テキストファイルなどで開くことでその内部を確認することができます。 電話帳ファイルのパスはRASENTRYNAME構造体のszPhonebookPathから参照できますが、 基本的には下記に示す値となっているでしょう。

C:\Documents and Settings\All Users\Application Data\Microsoft\Network\Connections\Pbk\rasphone.pbk

rasphone.pbkというファイルが正に電話帳ファイルとなります。 実はこのファイルはINIファイルと同じ形式で構成され、 各セクションの名前には各エントリの名前が指定されています。 キーはエントリのプロパティであり、GetPrivateProfileIntや GetPrivateProfileStringを呼び出すことで、任意のキーの値を取得できるように思えますが、 ファイルの文字コードがUTF-8であるため、これらの関数は利用することができません。 プロパティの取得や設定は、RasGetEntryPropertiesやRasSetEntryPropertiesで行います。



戻る