Remote Access Phonebook

Before you can establish a dial-up connection to a remote server, the host you are attempting to connect with must have an entry in the RAS phonebook. Just like a regular telephone book, the RAS phonebook contains entries that enable a remote dialer to specify a connection by name. A phonebook entry contains all the necessary information that the device needs to establish a connection to the server, including the phone number to call, the IP address it is assigned once connected (or a flag to get a dynamic IP allocated to it), any user authentication information, and any other connection properties.

The RAS phonebook is stored in the registry under the HKEY_CURRENT_USER\Comm\RasBook key. Each entry has its own set of values underneath its named key, as shown in Figure 6.2.

Figure 6.2. The RAS phonebook (as seen by the registry)

graphics/06fig02.gif

The following sections describe how you can create, modify, and delete phonebook entries using the RAS phonebook programming interfaces.

Adding a Phonebook Entry

Before you can create a new entry, you must first ensure that the phonebook does not already contain an entry of the same name. In addition, you must verify that an entry contains no illegal characters. To do this, you can call the following function:

DWORD RasValidateEntryName(LPWSTR lpszPhonebook, LPWSTR lpszEntry);

Because Pocket PC stores its phonebook in the registry, the first parameter, lpszPhonebook, should be set to a NULL value. The second parameter, lpszEntry, must contain a pointer to a null-terminated string that contains the entry name you want to validate. In order to qualify as a "good" entry, the name must be 1?20 characters in length, and not contain any of the following: | > < ? * \ / :

The function will return a 0 if successful or ERROR_INVALID_NAME if the name is not valid. If an entry with the same name is already in the phonebook, an ERROR_ALREADY_EXISTS error is returned. This is useful if you are going to modify an entry, as you will see shortly.

Once you have confirmed that the name is valid, you can go ahead and add your new entry using the RasSetEntryProperties() function, which is defined as follows:

DWORD RasSetEntryProperties(LPWSTR lpszPhoneBook, LPWSTR szEntry,
   LPRASENTRY lpEntry, DWORD dwEntrySize, LPBYTE lpb, DWORD dwSize);

As with all of the phonebook APIs on Pocket PC, the lpszPhoneBook parameter should be set to NULL. The szEntry parameter should be set to the name of the new entry, which was previously validated with your call to RasValidateEntryName(). This is followed by lpEntry, a pointer to a RASENTRY structure that contains the actual phonebook entry information. Next, dwEntrySize should specify the size of the lpEntry structure, in bytes. Finally, lpb and dwSize are used to set device-specific configuration information about a Telephone API (TAPI) device. For now, to add new phonebook entries, you can set lpb to NULL and dwSize to 0.

Once the function is called, it will return 0 if the new entry has been successfully added to the phonebook. If the RASENTRY structure passed into lpEntry is invalid, an ERROR_BUFFER_INVALID return code will be returned. If you are returned an ERROR_CANNOT_OPEN_PHONEBOOK result, the phonebook may be corrupted.

The RASENTRY structure that contains a phonebook entry's information looks like the following:

typedef struct tagRasEntry {
   DWORD dwSize;
   DWORD dwfOptions;
   DWORD dwCountryID;
   DWORD dwCountryCode;
   TCHAR szAreaCode[RAS_MaxAreaCode+1];
   TCHAR szLocalPhoneNumber[RAS_MaxPhoneNumber+1];
   DWORD dwAlternatesOffset;
   RASIPADDR ipaddr;
   RASIPADDR ipaddrDns;
   RASIPADDR ipaddrDnsAlt;
   RASIPADDR ipaddrWins;
   RASIPADDR ipaddrWinsAlt;
   DWORD dwFrameSize;
   DWORD dwfNetProtocols;
   DWORD dwFramingProtocol;
   TCHAR szScript[MAX_PATH];
   TCHAR szAutoDialDll[MAX_PATH];
   TCHAR szAutoDialFunc[MAX_PATH];
   TCHAR szDeviceType[RAS_MaxDeviceType+1];
   TCHAR szDeviceName[RAS_MaxDeviceName+1];
   TCHAR szX25PadType[RAS_MaxPadType+1];
   TCHAR szX25Address[RAS_MaxX25Address+1];
   TCHAR szX25Facilities[RAS_MaxFacilities+1];
   TCHAR szX25UserData[RAS_MaxUserData+1];
   DWORD dwChannels;
   DWORD dwReserved1;
   DWORD dwReserved2;
} RASENTRY, *LPRASENTRY;

Table 6.1 describes the RASENTRY structure that is used for a phonebook entry.

Table 6.2 describes the options that can be used for the dwfOptions field of the RASENTRY structure.

Table 6.1. RASENTRY Field Descriptions

Value

Description

dwSize

Size of the RASENTRY structure.

dwfOptions

Indicates the specific connection options. See Table 6.2 for more information.

dwCountryID

Not used.

dwCountryCode

Country code (valid only if dwfOptions specifies the RASEO_UseCountryAndAreaCodes flag).

szAreaCode

Area code (valid only if dwfOptions specifies the RASEO_UseCountryAndAreaCodes flag).

szLocalPhoneNumber

Phone number of remote host. If dwfOptions specifies the RASEO_UseCountryAndAreaCodes flag, the phone number is combined with dwCountryCode and szAreaCode; otherwise, szLocalPhoneNumber is used as the entire phone number.

dwAlternatesOffset

Alternate phone numbers are not supported on Pocket PC. Not used.

ipaddr

IP address to use when connected (valid only if dwfOptions specifies the RASEO_SpecificIpAddr flag).

ipaddrDns

IP address of the primary DNS server (valid only if dwfOptions specifies the RASEO_SpecificNameServers flag).

ipaddrDnsAlt

IP address of the secondary DNS server (valid only if dwfOptions specifies the RASEO_SpecificNameServers flag).

ipaddrWins

IP address of the primary WINS server (valid only if dwfOptions specifies the RASEO_SpecificNameServers flag).

ipaddrWinsAlt

IP address of the secondary WINS server (valid only if dwfOptions specifies the RASEO_SpecificNameServers flag).

dwFrameSize

Network protocol frame size; should be set to 1,006 or 1,500 (valid only if dwFramingProtocol specifies the RASFP_Slip flag).

dwfNetProtocols

Should be set to RASNP_Ip, specifying TCP/IP as the dial-up protocol.

dwFramingProtocol

Specifies the dial-up protocol to use, and can be set to either RASFP_Slip (to use the Serial Line Internet Protocol (SLIP)) or RASFP_Ppp (to use the point-topoint protocol).

szScript

Not supported.

szAutoDialDll

Not supported.

szAutoDialFunc

Not supported.

szDeviceType

Specifies the device type specified by szDeviceName. Can be set to either RASDT_Modem (for a modem) or RASDT_Direct (for a direct serial connection).

szDeviceName

Null-terminated string that specifies the TAPI device to use with the entry.

szX25PadType

Not supported.

szX25Address

Not supported.

szX25Facilities

Not supported.

szX25UserData

Not supported.

dwChannels

Number of channels supported on the device.

dwReserved1

Reserved.

dwReserved2

Reserved.

Table 6.2. RAS Entry Options

Value

Description

RASEO_UseCountryAndAreaCodes

RAS should use the dwCountryCode and szAreaCode members to build the phone number; otherwise, just use szLocalPhoneNumber.

RASEO_SpecificIpAddr

RAS should use the ipaddr member of RASENTRY for the IP address of the dial-up connection; otherwise, request an address via DHCP.

RASEO_SpecificNameServers

RAS should use the ipaddrDns, ipaddrDnsAlt, ipaddrWins, and ipaddrWinsAlt members as the primary and secondary DNS and WINS name servers, respectively.

RASEO_IpHeaderCompression

Use IP header compression to improve performance if it is supported by the dial-up server.

RASEO_RemoteDefaultGateway

Ignored.

RASEO_DisableLcpExtensions

If set, PPP will ignore LCP extensions (RFC 1570); used to maintain compatibility with older PPP implementations.

RASEO_ModemLights

Ignored.

RASEO_SwCompression

If set, software compression (CCP) is negotiated upon establishing a link with the server. This should typically be set for better performance.

RASEO_RequireEncryptedPw

If set, only secure passwords can be used; prevents PPP from using plain-text authentication. This turns on PPP's CHAP authentication scheme.

RASEO_RequireMsEncryptedPW

If set, only Microsoft secure passwords can be used; prevents PPP from using plain-text authentication or MD5 CHAP. This will turn on the Microsoft CHAP authentication scheme.

RASEO_RequireDataEncryption

If set, data encryption must be negotiated between the client and the server; otherwise, the connection is dropped. This is used in conjunction with the RASEO_RequireMsEncryptedPW flag.

RASEO_NetworkLogon

After a connection is established, RAS should log into the network.

RASEO_UseLogonCredentials

RAS should use the username, password, and domain of the current user for logging onto the server.

RASEO_PromoteAlternates

Ignored.

RASEO_DialAsLocalCall

RAS should construct the phone number as a local call.

As you can see, numerous options are available for creating a new phonebook entry. The following example illustrates what's actually involved in creating a new entry called "Work" in the RAS phonebook:

// Add an entry to the phonebook
TCHAR tchNewEntry[MAX_PATH+1] = TEXT("\0");
wsprintf(tchNewEntry, TEXT("Work"));

// Validate the entry name
if(RasValidateEntryName(NULL, tchNewEntry) != 0) {
   MessageBox(NULL, TEXT("Invalid Entry Name"), TEXT("RAS
   phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Set up the RASENTRY structure. Use the country/area codes
RASENTRY rasEntry;
DWORD dwResult = 0;
memset(&rasEntry, 0, sizeof(RASENTRY));

rasEntry.dwSize = sizeof(RASENTRY);
rasEntry.dwfOptions = RASEO_UseCountryAndAreaCodes;
rasEntry.dwCountryCode = 1;
wsprintf(rasEntry.szAreaCode, TEXT("425"));
wsprintf(rasEntry.szLocalPhoneNumber,TEXT("5551212"));

// Create the entry
dwResult = RasSetEntryProperties(NULL, tchNewEntry, &rasEntry,
   sizeof(RASENTRY), NULL, 0);

// Check for any errors
if(dwResult != 0) {
   TCHAR tchError[256] = TEXT("\0");

   // Print out the error
   wsprintf(tchError, TEXT("Could not create entry -- Error %ld"),
      dwResult);
   MessageBox(NULL, tchError, TEXT("RAS phonebook Error"),
      MB_ICONERROR|MB_OK);
   return FALSE;
}

User Credentials

When a RAS entry from the phonebook is dialed, a dialog box appears that enables the user to set up any access credentials (such as a username or password) associated with the session.

To get the current security settings for an entry, you can call the RasGetEntryDialParams() function, which is prototyped as follows:

DWORD RasGetEntryDialParams(LPWSTR lpszPhoneBook,
   LPRASDIALPARAMS lpRasDialParams, LPBOOL lpfPassword);

The first parameter, lpszPhoneBook, should be set to NULL. Next, lpRasDialParams should point to a RASDIALPARAMS structure. When retrieving information from the phonebook, you must first set the dwSize member of lpRasDialParams to the size of the RASDIALPARAMS structure, and set the szEntryName parameter to a valid phonebook entry for which you want the data. To test whether the phonebook entry is valid, you can use the RasValidateEntryName() function described earlier in this chapter.

The final parameter, lpfPassword, should point to a BOOL variable that specifies whether the password was returned in the RASDIALPARAMS structure when the function is complete.

If the function returns successfully, it will return 0. If either the lpRasDialParams or lpfPassword pointers are invalid, it will return ERROR_BUFFER_INVALID. If the phonebook entry is not valid, or there are any other issues with the RASDIALPARAMS structure (such as the dwSize member being set to an invalid value), you will be returned ERROR_CANNOT_FIND_PHONEBOOK_ENTRY.

The RASDIALPARAMS structure looks like the following:

typedef struct tagRASDIALPARAMS {
   DWORD dwSize;
   TCHAR szEntryName[RAS_MaxEntryName+1];
   TCHAR szPhoneNumber[ RAS_MaxPhoneNumber+1];
   TCHAR szCallbackNumber[RAS_MaxCallbackNumber+1];
   TCHAR szUserName[UNLEN+1];
   TCHAR szPassword[PWLEN+1];
   TCHAR szDomain[DNLEN+1];
} RASDIALPARAMS, *LPRASDIALPARAMS;

The fields of the structure are defined as follows:

  • dwSize should be set to the size of the RASDIALPARAMS structure.

  • szEntryName specifies a null-terminated string that contains the phonebook entry name.

  • szPhoneNumber is not used, and can be set to NULL. If you want to retrieve the phone number information, you can use the RasGetEntryProperties() function, as described in the section "Modifying Existing Entries."

  • szCallbackNumber is not used, and can be set to a NULL value.

  • szUserName is a null-terminated string that contains the logon name of the user.

  • szPassword is a null-terminated string that contains the password of the user.

  • szDomain is a null-terminated string containing the domain that will be logged into.

For example, if you wanted the user credentials for the phonebook entry called "Work," you would do the following:

TCHAR tchRasEntry[MAX_PATH+1] = TEXT("\0");
wsprintf(tchRasEntry, TEXT("Work"));

// Validate the entry name to make sure it exists
if(RasValidateEntryName(NULL, tchRasEntry) !=
   ERROR_ALREADY_EXISTS) {
   MessageBox(NULL, TEXT("Entry does not exist"), TEXT("RAS
   phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Get the dial parameters
RASDIALPARAMS rasDialParams;
BOOL fPassword = FALSE;

memset(&rasDialParams, 0, sizeof(RASDIALPARAMS));
rasDialParams.dwSize = sizeof(RASDIALPARAMS);
_tcsncpy(rasDialParams.szEntryName, tchRasEntry,
  RAS_MaxEntryName);

if(RasGetEntryDialParams(NULL, &rasDialParams, &fPassword)
  != 0) {
   MessageBox(NULL, TEXT("Could not get dial parameters"),
   TEXT("RAS phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

To change the user credentials, you can use the RasSetEntryDialParams() function:

DWORD RasSetEntryDialParams(LPWSTR lpszPhoneBook,
   LPRASDIALPARAMS lpRasDialParams, BOOL fRemovePassword);

The first parameter is not used, and should be set to NULL. Next, lpRasDialParams should point to a structure that contains the updated RASDIALPARAMS information for the connection. Finally, fRemovePassword should be set to TRUE if you want to remove the password associated with this entry.

If the function returns 0, then it was successful. If an ERROR_CANNOT_FIND_PHONEBOOK_ENTRY error code is returned, then the buffer you are using to pass into lpRasDialParams is invalid.

The following example shows how you could change the user credentials for the entry "Work":

// Get the entry as above, then set the values to change
wsprintf(rasDialParams.szUserName, TEXT("The Big Ragu"));
wsprintf(rasDialParams.szDomain, TEXT("CarminesDomain"));

if(RasSetEntryDialParams(NULL, &rasDialParams, FALSE) != 0) {
   MessageBox(NULL, TEXT("Could not modify dial parameters"),
   TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

Modifying Existing Entries

Now that you know how to add a new entry to the RAS phonebook, you can easily modify one. Basically, all you need to do is get the current properties for the entry you want to change, modify its RASENTRY structure, and call RasSetEntryDialParams() with the newly changed information. Because the szEntry name will already exist, Pocket PC will update the entry, rather than create a new one.

To get the current properties for an entry, you can use the RasGetEntryProperties() function:

DWORD RasGetEntryProperties(LPWSTR lpszPhoneBook, LPWSTR
   szEntry, LPRASENTRY lpEntry, LPDWORD lpdwEntrySize, LPBYTE lpb,
   LPDWORD lpdwSize);

As usual for RAS phonebook operations, the first parameter should be set to NULL. The next parameter, szEntry, should be set to a valid null-terminated string that represents the entry you want to retrieve. This is followed by a pointer to a RASENTRY structure that will receive the actual data for the entry. You need to make sure that before calling RasGetEntryProperties(), the dwSize member of the lpEntry structure is set to the size of RASENTRY. Next, lpdwEntrySize should contain a pointer to a variable that contains the size of the lpEntry structure, in bytes. If you do not know the size, you can set lpEntry to NULL, and lpdwEntrySize will return the number of bytes that are required. Finally, you can set the last two parameters to NULL.

If the function is successful, you will be returned a 0. If you specify an invalid size in the dwSize member of the lpEntry structure, you will be returned an ERROR_INVALID_SIZE. If the buffer is invalid or too small, you will be returned either ERROR_BUFFER_INVALID or ERROR_BUFFER_TOO_SMALL, respectively. Finally, an ERROR_CANNOT_FIND_PHONEBOOK_ENTRY error specifies that the entry is not in the phonebook.

The following example changes the phone number in the entry "Work":

// Modify a RAS phonebook entry
TCHAR tchRasEntry[MAX_PATH+1] = TEXT("\0");
wsprintf(tchRasEntry, TEXT("Work"));

// Validate the entry name to make sure it exists
if(RasValidateEntryName(NULL, tchRasEntry) !=
   ERROR_ALREADY_EXISTS) {
   MessageBox(NULL, TEXT("Entry does not exist"), TEXT("RAS
   phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Set up the RASENTRY structure to get our entry
RASENTRY rasEntry;
DWORD dwResult = 0;
DWORD dwEntrySize = sizeof(RASENTRY);

memset(&rasEntry, 0, sizeof(RASENTRY));
rasEntry.dwSize = sizeof(RASENTRY);

// Get the entry
dwResult = RasGetEntryProperties(NULL, tchRasEntry,
   &rasEntry, &dwEntrySize, NULL, 0);
if(dwResult != 0) {
   MessageBox(NULL, TEXT("Could not get entry information"),
   TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Change the phone number of the entry
wsprintf(rasEntry.szLocalPhoneNumber,TEXT("5551234"));

// Save changes
dwResult = RasSetEntryProperties(NULL, tchRasEntry, &rasEntry,
   sizeof(RASENTRY), NULL, 0);
if(dwResult != 0) {
   TCHAR tchError[256] = TEXT("\0");

   // Print out the error
   wsprintf(tchError, TEXT("Could not modify entry -- Error
   %ld"), dwResult);
   MessageBox(NULL, tchError, TEXT("RAS phonebook Error"),
      MB_ICONERROR|MB_OK);
   return FALSE;
}

Getting device-specific configuration information is a bit different from a standard desktop Windows platform. With Windows, you would use the RasGetEntryProperties() function combined with the last two parameters to return any device information. On Pocket PC, you must use the RasGetEntryDevConfig() function instead:

DWORD RasGetEntryDevConfig(LPCTSTR szPhonebook, LPCTSTR
  szEntry, LPDWORD pdwDeviceID, LPDWORD pdwSize, LPVARSTRING
  pDeviceConfig);

The first parameter is set to NULL, which is followed by a valid phonebook entry name. Next, pdwDeviceID should be set to a pointer that will retrieve the DWORD device ID when the function returns. Finally, pdwSize should be set to a pointer that specifies the size of a buffer that will retrieve the device configuration information. The pointer to that buffer should be set at the parameter value for pDeviceConfig.

To set any device configuration information, you can use the RasSetEntryDevConfig() function:

DWORD RasSetEntryDevConfig(LPCTSTR szPhonebook, LPCTSTR
   szEntry, DWORD dwDeviceID, LPVARSTRING lpDeviceConfig);

The first parameter, szPhonebook, should be set to NULL. Next, szEntry should be set to a valid phonebook entry name, and is followed by setting dwDeviceID to the device ID that was returned from a previous call to RasGetEntryDevConfig(). Finally, set lpDeviceConfig to a pointer that represents the configuration buffer.

For example, if you wanted to get the device configuration information for the phonebook entry "My Connection", you could do the following:

// Get/set device configuration
TCHAR tchRasEntry[MAX_PATH+1] = TEXT("\0");
wsprintf(tchRasEntry, TEXT("My Connection"));

// Validate the entry name to make sure it exists
if(RasValidateEntryName(NULL, tchRasEntry) !=
   ERROR_ALREADY_EXISTS) {
   MessageBox(NULL, TEXT("Entry does not exist"), TEXT("RAS
   phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Get the device configuration
DWORD dwDeviceID = 0;
DWORD dwSize = 0;
TCHAR tchDevConfig[256] = TEXT("\0");
LPVARSTRING vsDevConfig;

vsDevConfig = (LPVARSTRING)tchDevConfig;
vsDevConfig->dwTotalSize = sizeof(tchDevConfig);
dwSize = sizeof(tchDevConfig);

DWORD dwError = RasGetEntryDevConfig(NULL, tchRasEntry,
   &dwDeviceID, &dwSize, vsDevConfig);
if(dwError != 0) {
   MessageBox(NULL, TEXT("Could not get device information."),
   TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

If all you need to do is rename a phonebook entry, you use the RasRenameEntry() function:

DWORD RasRenameEntry(LPWSTR lpszPhonebook, LPWSTR
   lpszOldEntry, LPWSTR lpszNewEntry);

After setting lpszPhonebook to NULL, all you need to provide is the valid name of an old entry in the lpszOldEntry parameter, which is followed by a valid name to change it to in lpszNewEntry.

Finally, if all you need to do is get the current phone number information for a particular entry, you can use the following function:

DWORD RasGetDispPhoneNum(LPCWSTR szPhonebook, LPCWSTR
   szEntry, LPWSTR szPhoneNum, DWORD dwPhoneNumLen);

As usual, you need to set the szPhonebook parameter to NULL, and szEntry to a valid phonebook entry name. The szPhoneNum parameter should point to a buffer that will contain the phone number of the entry when the function returns. The last parameter, dwPhoneNumLen, should specify, in bytes, the size of the buffer you are using for the szPhoneNum buffer.

Copying Entries

To copy a phonebook entry, you can use a combination of the functions already described:

// Copy an entry
TCHAR tchRasEntry[MAX_PATH+1] = TEXT("\0");
TCHAR tchNewRasEntry[MAX_PATH+1] = TEXT("\0");
wsprintf(tchRasEntry, TEXT("Work"));
wsprintf(tchNewRasEntry, TEXT("Copy of Work"));

// Validate the entry name to make sure it exists
if(RasValidateEntryName(NULL, tchRasEntry) !=
   ERROR_ALREADY_EXISTS) {
   MessageBox(NULL, TEXT("Entry does not exist"), TEXT("RAS
   phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Validate the entry name we're going to copy to
if(RasValidateEntryName(NULL, tchNewRasEntry) != 0) {
   MessageBox(NULL, TEXT("New entry name is not valid"),
   TEXT("RAS phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Get the data of the source
RASENTRY rasEntry;
RASDIALPARAMS rasDialParams;
BOOL fPassword = FALSE;
DWORD dwResult = 0;
DWORD dwEntrySize = sizeof(RASENTRY);

memset(&rasEntry, 0, sizeof(RASENTRY));
rasEntry.dwSize = sizeof(RASENTRY);

memset(&rasDialParams, 0, sizeof(RASDIALPARAMS));
rasDialParams.dwSize = sizeof(RASDIALPARAMS);

// Get the source entry
dwResult = RasGetEntryProperties(NULL, tchRasEntry,
   &rasEntry, &dwEntrySize, NULL, 0);
if(dwResult != 0) {
   MessageBox(NULL, TEXT("Could not get source entry information"),
   TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

_tcsncpy(rasDialParams.szEntryName, tchRasEntry,
   RAS_MaxEntryName);

if(RasGetEntryDialParams(NULL, &rasDialParams, &fPassword)
   != 0) {
   MessageBox(NULL, TEXT("Could not get source dial parameters"),
   TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Save changes
dwResult = RasSetEntryProperties(NULL, tchNewRasEntry,
   &rasEntry, sizeof(RASENTRY), NULL, 0);
if(dwResult != 0) {
   TCHAR tchError[256] = TEXT("\0");

   // Print out the error
   wsprintf(tchError, TEXT("Could not copy the entry - Error
   %ld"), dwResult);
   MessageBox(NULL, tchError, TEXT("RAS phonebook Error"),
      MB_ICONERROR|MB_OK);
   return FALSE;
}

if(RasSetEntryDialParams(NULL, &rasDialParams, FALSE) != 0) {
   MessageBox(NULL, TEXT("Could not modify dial
   parameters"), TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

Removing Entries

To remove an entry from the RAS phonebook, you can simply call the RasDeleteEntry() function:

DWORD RasDeleteEntry(LPWSTR lpszPhonebook, LPWSTR
  lpszEntry);

The first parameter, lpszPhonebook, should be set to NULL. The lpszEntry parameter should be set to a null-terminated string that contains the name of the valid RAS entry you want to delete.

If the function returns 0, the entry has been successfully deleted from the phonebook.

The following removes the entry "Work" that was created earlier:

if(RasDeleteEntry(NULL, TEXT("Work"))) != 0) {
   MessageBox(NULL, TEXT("Could not remove RAS entry"),
   TEXT("RAS Phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

Enumerating Entries

When developing an application that uses RAS, it is often useful to display a list of entries in the RAS phonebook. Instead of having to walk through various registry keys, you can use the RasEnumEntries() function to return the contents of the phonebook. The function is defined as follows:

DWORD RasEnumEntries(LPWSTR Reserved, LPWSTR lpszPhoneBookPath,
   LPRASENTRYNAME lprasentryname, LPDWORD lpcb, LPDWORD lpcEntries);

The first two parameters should be set to NULL. This is followed by lprasentryname, which points to a buffer that will receive an array of RASENTRYNAME structures?one for each entry in the phonebook. Next, lpcb should point to a DWORD value that specifies the size, in bytes, of the lprasentryname buffer. Finally, lpcEntries should point to a DWORD value, which will be set with the actual number of entries that were written to the lprasentryname buffer.

In order to properly use RasEnumEntries(), you must set the dwSize member of the first RASENTRYNAME structure in the array to the size of RASENTRYNAME.

If the call to RasEnumEntries is successful, you will be returned a 0. The lprasentryname array will now point to a list of RASENTRYNAME structures that contains the phonebook, lpcb will contain the total number of bytes that lprasentryname uses, and lpcEntries will contain the number of RASENTRYNAME structures in the array. If the call fails, you will be returned either an ERROR_BUFFER_TOO_SMALL or ERROR_NOT_ENOUGH_MEMORY error code.

The RASENTRYNAME structure looks as follows:

typedef struct tagRASENTRYNAME {
   DWORD dwSize;
   TCHAR szEntryName[ RAS_MaxEntryName + 1 ];
} RASENTRYNAME, *LPRASENTRYNAME;

The first member, dwSize, should be set to the size of RASENTRYNAME, and szEntryName will contain a null-terminated string for the phonebook entry name.

Instead of guessing the number of phonebook entries, it can be useful to use the ERROR_BUFFER_TOO_SMALL error to determine the actual size of the phonebook. To do this, you would first call RasEnumEntries() to get the size required, and then call it a second time after you have allocated the proper buffer. For example, to enumerate the phonebook, you would do the following:

// Enumerate the phonebook.
// First, we will call the enum function with no data to get
// the size
DWORD dwReturn = 0;
DWORD dwEntriesSize = 0;
DWORD dwNumEntries = 0;

dwReturn = RasEnumEntries(NULL, NULL, NULL, &dwEntriesSize,
  &dwNumEntries);
if(dwReturn != ERROR_BUFFER_TOO_SMALL) {
   MessageBox(NULL, TEXT("Could not get the size of the RAS
   phonebook"), TEXT("RAS phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Allocate a buffer to store the rasentrynames
LPRASENTRYNAME pRasEntries = NULL;

pRasEntries = (LPRASENTRYNAME)LocalAlloc(LPTR, dwEntriesSize);
if(!pRasEntries)
   return FALSE;

pRasEntries->dwSize = sizeof(RASENTRYNAME);

// Get the entries
dwReturn = RasEnumEntries(NULL, NULL, pRasEntries,
   &dwEntriesSize, &dwNumEntries);
if(dwReturn != 0) {
   MessageBox(NULL, TEXT("Could not get the size of the RAS
   phonebook"), TEXT("RAS phonebook Error"), MB_ICONERROR|MB_OK);
   return FALSE;
}

// Print out the entries
for(WORD wEntry = 0; wEntry<dwNumEntries; wEntry++) {
   MessageBox(NULL, (pRasEntries[wEntry]).szEntryName,
   TEXT("RAS phonebook Entry"), MB_OK);
}

// Clean up memory
LocalFree(pRasEntries);