When a RAS client application is ready to make a connection to a remote computer, it must call the RasDial function. RasDial is quite complex, offering many call parameters that are used for dialing, authenticating, and establishing a remote connection to a RAS server. RasDial is defined as
DWORD RasDial( LPRASDIALEXTENSIONS lpRasDialExtensions, LPCTSTR lpszPhonebook, LPRASDIALPARAMS lpRasDialParams, DWORD dwNotifierType, LPVOID lpvNotifier, LPHRASCONN lphRasConn ); |
The lpRasDialExtensions parameter is an optional pointer to a RASDIALEXTENSIONS structure that causes your application to enable extended features for RasDial. On Windows 95, Windows 98, and Windows CE, this parameter is ignored and should be set to NULL. The RASDIALEXTENSIONS structure is defined as
typedef struct tagRASDIALEXTENSIONS { DWORD dwSize; DWORD dwfOptions; HWND hwndParent; ULONG_PTR reserved; #if (WINVER >= 0x500) ULONG_PTR reserved1; RASEAPINFO RasEapInfo; #endif } RASDIALEXTENSIONS; |
Notice that this structure is sized differently during compilation based on the value of WINVER, as described earlier. The above fields are described as follows:
Table 16-1. RasDial bit flags of the extension features
Flag | Description |
---|---|
RDEOPT_UsePrefixSuffix | Makes RasDial use the prefix and suffix associated with the specified dialing device. |
RDEOPT_PausedStates | Allows RasDial to enter a paused operating state so that users can retry logons, change passwords, and set callback numbers. |
RDEOPT_IgnoreModemSpeaker | Makes RasDial ignore the modem speaker settings in the RAS phonebook. |
RDEOPT_SetModemSpeaker | Turns on the modem speaker if RDEOPT_IgnoreModemSpeaker is set. |
RDEOPT_IgnoreSoftwareCompression | Makes RasDial ignore software compression settings. |
RDEOPT_SetSoftwareCompression | Turns on software compression if RDEOPT_IgnoreSoftwareCompression is set. |
RDEOPT_PauseOnScript | Used internally by RasDialDlg. You should not set this flag. |
The lpszPhonebook parameter of RasDial identifies the path to a phonebook file on Windows 2000 and Windows NT. This parameter must be NULL on Windows 95, Windows 98, and Windows CE because the phonebook is stored in the system Registry. A phonebook is a collection of RAS dialing properties that define how to set up a RAS connection. However, you are not required to use a phonebook to make a RAS connection. RasDial features enough dialing parameters to allow you to set up a basic connection. We will discuss the details of a RAS phonebook later in this chapter.
The RASDIALPARAMS structure pointer lpRasDialParams defines dialing and user authentication parameters that the RasDial function uses to establish a remote connection. It's defined as
typedef struct _RASDIALPARAMS { 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] ; #if (WINVER >= 0x401) DWORD dwSubEntry; DWORD dwCallbackId; #endif } RASDIALPARAMS; |
The fields of RASDIALPARAMS are described as follows:
The next RasDial parameters, dwNotifierType and lpvNotifier, determine the operating mode of RasDial (whether it can be called synchronously or asynchronously). The final RasDial parameter, lphRasConn, is a pointer to a RAS connection handle of type HRASCONN. Before you call RasDial, you must set this parameter to NULL. If RasDial completes successfully, a reference handle to the RAS connection is returned.
Let's begin by demonstrating how to call RasDial. As we mentioned, RasDial can execute in two operating modes: synchronous and asynchronous. In synchronous mode, RasDial blocks until it either completes a connection or fails to do so. In asynchronous mode, RasDial completes a connection immediately, allowing your application to perform other actions while connecting.
If the lpvNotifier parameter of RasDial is set to NULL, RasDial will operate synchronously. When the lpvNotifier parameter is NULL, the dwNotifierType parameter is ignored. Calling RasDial synchronously is the easiest way to use this function; however, you won't be able to monitor the connection as you can in asynchronous mode, which we will describe in a moment. Figure 16-1 demonstrates how to call RasDial synchronously. Notice that this code listing does not specify a phonebook or a phonebook entry. Instead, it demonstrates how simple forming a RAS connection is.
Figure 16-1. Calling RasDial synchronously
RASDIALPARAMS RasDialParams; HRASCONN hRasConn; DWORD Ret; // Always set the size of the RASDIALPARAMS structure RasDialParams.dwSize = sizeof(RASDIALPARAMS); hRasConn = NULL; // Setting this field to an empty string will allow // RasDial to use default dialing properties lstrcpy(RasDialParams.szEntryName, ""); lstrcpy(RasDialParams.szPhoneNumber, "867-5309"); lstrcpy(RasDialParams.szUserName, "jenny"); lstrcpy(RasDialParams.szPassword, "mypassword"); lstrcpy(RasDialParams.szDomain, "mydomain"); // Call RasDial synchronously (the fifth parameter // is set to NULL) Ret = RasDial(NULL, NULL, &RasDialParams, 0, NULL, &hRasConn); if (Ret != 0) { printf("RasDial failed: Error = %d\n", Ret); } |
Calling RasDial asynchronously is a lot more complicated than calling this function in synchronous mode. If the lpvNotifier parameter of RasDial is not set to NULL, RasDial will operate asynchronously—meaning the call returns immediately but the connection proceeds. Calling RasDial asynchronously is the preferred method for making a RAS connection because you can monitor the connection's progress. The lpvNotifier parameter can be either a pointer to a function that is called when a connection activity occurs in RasDial or a window handle that receives progress notification via Windows messages. The dwNotifierType parameter of RasDial determines the type of function or window handle that is passed into lpvNotifier. Table 16-2 describes the values that you can specify in dwNotifierType.
Table 16-2. RasDial asynchronous notification methods
Notifier Type | Meaning |
---|---|
0 | The lpvNotifier parameter causes RasDial to use the RasDialFunc function pointer to manage connection events. |
1 | The lpvNotifier parameter causes RasDial to use the RasDialFunc1 function pointer to manage connection events. |
2 | The lpvNotifier parameter causes RasDial to use the RasDialFunc2 function pointer to manage connection events. |
0xFFFFFFFF | The lpvNotifier parameter makes RasDial send a window message during connection events. |
Table 16-2 shows the three function prototypes that you can supply to RasDial in the lpvNotifier parameter for receiving callback notification of connection events: RasDialFunc, RasDialFunc1, and RasDialFunc2. The first one, RasDialFunc, is prototyped as
VOID WINAPI RasDialFunc( UINT unMsg, RASCONNSTATE rasconnstate, DWORD dwError ); |
The unMsg parameter receives the type of event that has occurred. Currently this event can be only WM_RASDIALEVENT, which means that this parameter is not useful. The rasconnstate parameter receives the connection activity that the RasDial function is about to start. Table 16-3 defines the possible connection activities. The dwError parameter receives a RAS error code if one of the connection activities experiences failure.
Table 16-3 shows three operating states associated with connection activities in an asynchronous RasDial call: running, paused, and terminal. The running state indicates that the RasDial call is still in progress, and each running-state activity offers progress status information.
The paused state indicates that RasDial needs more information to establish the connection. By default, the paused state is disabled. You can enable this notification process by setting the RDEOPT_PausedStates flag in the RASDIALEXTENSIONS structure that we mentioned earlier. When a paused state activity occurs, it indicates one of the conditions listed below.
Table 16-3. RAS connection activities
Activity | State | Description |
---|---|---|
RASCS_OpenPort | Running | A communication port is about to be opened. |
RASCS_PortOpened | Running | The communication port is open. |
RASCS_ConnectDevice | Running | A device is about to be connected. |
RASCS_ DeviceConnected | Running | The device has successfully connected. |
RASCS_ AllDevicesConnected | Running | A physical link has been established. |
RASCS_ Authenticate | Running | The RAS authentication process has started. |
RASCS_ AuthNotify | Running | An authentication event has occurred. |
RASCS_ AuthRetry | Running | The client has requested another authentication attempt. |
RASCS_ AuthCallback | Running | The server has requested a callback number. |
RASCS_ AuthChangePassword | Running | The client has requested to change the password on the RAS account. |
RASCS_ AuthProject | Running | The protocol projection is starting. |
RASCS_ AuthLinkSpeed | Running | The link speed is being calculated. |
RASCS_ AuthAck | Running | An authentication request is being acknowledged. |
RASCS_ ReAuthenticate | Running | The authentication process after a callback is starting. |
RASCS_ Authenticated | Running | The client has successfully completed the authentication. |
RASCS_ PrepareForCallback | Running | The line is about to disconnect to prepare for a callback. |
RASCS_ WaitForModemReset | Running | The client is waiting for the modem to reset before preparing for a callback. |
RASCS_ WaitForCallback | Running | The client is waiting for an incoming call from the server. |
RASCS_ Projected | Running | The protocol projection is complete. |
RASCS_ StartAuthentication | Running | User authentication is being started or retried. (This applies to Windows 95 and Windows 98 only.) |
RASCS_ CallbackComplete | Running | The client has been called back. (This applies to Windows 95 and Windows 98 only.) |
RASCS_ LogonNetwork | Running | The client is logging on to a remote network. (This applies to Windows 95 and Windows 98 only.) |
RASCS_ SubEntryConnected | Running | A subentry of a multilink phonebook entry has connected. The dwSubEntry parameter of RasDialFunc2 will contain an index of the subentry connected. |
RASCS_ SubEntryDisconnected | Running | A subentry of a multilink phonebook entry has disconnected. The dwSubEntry parameter of RasDialFunc2 will contain an index of the subentry disconnected. |
RASCS_ RetryAuthentication | Paused | RasDial is awaiting new user credentials. |
RASCS_ CallbackSetByCaller | Paused | RasDial is awaiting a callback number from the client. |
RASCS_ PasswordExpired | Paused | RasDial expects the user to supply a new password. |
RASCS_InvokeEapUI | Paused | On Windows 2000, RasDial is awaiting a custom user interface to obtain EAP information. |
RASCS_ Connected | Terminal | The RAS connection succeeded and is active. |
RASCS_ Disconnected | Terminal | The RAS connection failed or is inactive. |
These activities pertain to information in the RASDIALPARAMS structure described earlier in this chapter. When a paused state activity occurs, RasDial will notify your callback function (or window procedure). If the paused state is disabled, RAS will send an error to your notification function and RasDial will fail. If enabled, the RasDial function will be in a paused state that allows your application to supply the necessary information through a RASDIALPARAMS structure. When RasDial is paused, you can resume by calling it again with the original call's connection handle (lphRasConn) and notification function (lpvNotifier), or you can simply end the paused operation by calling RasHangUp (described later in this chapter). If you resume the paused connection, you will have to supply the necessary user input via the RASDIALPARAMS structure passed to the resumed RasDial call.
NOTE
Do not resume the paused state by calling RasDial directly from a notification handler function such as RasDialFunc. RasDial is not designed to handle this situation, so you should resume RasDial directly from your application thread.
The final state—terminal—indicates that the RasDial connection has either succeeded or failed. It can also indicate that the RasHangUp function closed the connection.
Now that you have a basic understanding of how you can monitor the connection of an asynchronous RasDial call, we'll demonstrate how to set up a simple program that calls RasDial asynchronously. Figure 16-2 shows this procedure. You'll also find an asynchronous RasDial example on the companion CD.
Figure 16-2 Calling RasDial asynchronously
void main(void) { DWORD Ret; RASDIALPARAMS RasDialParams; HRASCONN hRasConn; // Fill in the RASDIALPARAMS structure with call parameters // as was done in the synchronous example ... if ((Ret = RasDial(NULL, NULL, &RasDialParams, 0, &RasDialFunc, &hRasConn)) != 0) { printf("RasDial failed with error %d\n", Ret); return; } // Perform other tasks while RasDial is processing ... } // Callback function RasDialFunc() void WINAPI RasDialFunc(UINT unMsg, RASCONNSTATE rasconnstate, DWORD dwError) { char szRasString[256]; // Buffer for error string if (dwError) { RasGetErrorString((UINT)dwError, szRasString, 256); printf("Error: %d - %s\n",dwError, szRasString); return; } // Map each of the states of RasDial, and display on the // screen the next state that RasDial is entering switch (rasconnstate) { case RASCS_ConnectDevice: printf ("Connecting device...\n"); break; case RASCS_DeviceConnected: printf ("Device connected.\n"); break; // Add other connection activities here ... default: printf ("Unmonitored RAS activity.\n"); break; } } |
Table 16-2 also mentioned two other callback notification functions: RasDialFunc1 and RasDialFunc2. These functions are prototyped as
VOID WINAPI RasDialFunc1( HRASCONN hrasconn, UINT unMsg, RASCONNSTATE rascs, DWORD dwError, DWORD dwExtendedError ); DWORD WINAPI RasDialFunc2( DWORD dwCallbackId, DWORD dwSubEntry, HRASCONN hrasconn, UINT unMsg, RASCONNSTATE rascs, DWORD dwError, DWORD dwExtendedError ); |
The RasDialFunc1 function is just like the RasDialFunc function discussed earlier except that it features two additional parameters: hrasconn and dwExtendedError. The hrasconn parameter is the handle to the connection that RasDial returned. The dwExtendedError parameter allows you to retrieve extended error information when the following types of errors occur in the dwError parameter during the connection.
The RasDialFunc2 function is just like RasDialFunc1 except that it features two additional parameters: dwCallbackId and dwSubEntry. The dwCallbackId parameter contains an application-defined value that was originally set in the dwCallbackId field of a RASDIALPARAMS structure passed to the RasDial call we described earlier. The dwSubEntry parameter receives the subentry phonebook index that caused the callback to the RasDialFunc2 function.
RAS features a stand-alone function named RasConnectionNotification that allows your application to determine when an asynchronous RAS connection has been created or terminated. RasConnectionNotification is defined as
DWORD RasConnectionNotification( HRASCONN hrasconn, HANDLE hEvent, DWORD dwFlags ); |
The hrasconn parameter is a connection handle returned from RasDial. The hEvent parameter is an event handle that your application creates using the CreateEvent function. The dwFlags parameter can be set to a combination of the following connection activity flags:
Note that these flags function in the same way as the connection activity flags described in Table 16-3. If any of these activities occur during your connection, your event will become signaled. Your application should use Win32 wait functions, such as WaitForSingleObject, to determine when the object becomes signaled.
Closing a connection established by RasDial is simple. All you have to do is call RasHangUp, which is defined as
DWORD RasHangUp( HRASCONN hrasconn ); |
The hrasconn parameter is a handle returned from RasDial. Although this function is easy to use, you have to consider how connections are managed internally in RAS. A connection uses a modem port, and it takes time for the port to reset internally when a connection shuts down. Therefore, you should wait until the port connection closes completely. To do this, you can call RasGetConnectionStatus to determine when your connection is reset. RasGetConnectionStatus is defined as
DWORD RasGetConnectStatus( HRASCONN hrasconn, LPRASCONNSTATUS lprasconnstatus ); |
The hrasconn parameter is a handle returned from RasDial. The lprasconnstatus parameter is a RASCONNSTATUS structure that receives the current connection status. A RASCONNSTATUS structure is defined as
typedef struct _RASCONNSTATUS { DWORD dwSize; RASCONNSTATE rasconnstate; DWORD dwError; TCHAR szDeviceType[RAS_MaxDeviceType + 1]; TCHAR szDeviceName[RAS_MaxDeviceName + 1]; } RASCONNSTATUS; |
These fields are defined as follows:
We recommend that you check the state of your connection until you receive the RASCS_ Disconnected activity status. Obviously, you might have to call RasGetConnectionStatus several times until the connection is reset. Once the connection is reset, you can exit your application or make another connection.