A Remote Access Service (RAS) Client Class

This article was contributed by Qing Zhang qzhang7@tfn.net.

Remote Access Service (RAS), also as "Dial-Up Networking" in Windows 95/98, lets users at remote locations work as if connected directly to a computer network, accessing one or more RAS servers.

The Microsoft® Win32® RAS API enables RAS client applications to perform the tasks such as display RAS common dialog boxes, start and end a RAS connection, operate to phone-book, work with entries in the RAS AutoDial mapping database, and get RAS information, etc. For Windows NT version 4.0, the Win32 API also provides support for RAS server administration and connection management. Windows 95 does not provide RAS server support.

This Article introduces a RAS client class, CRasClient, which wraps the client part functions of RAS API, and supports for both Windows 95/98 and NT4.0. In order to make your application using CRasClient works on both Windows 95/98 and Windows NT4.0, instead of wrapping some RAS API functions like RAS common dialog boxes, phone-book (.PBK) file operation, the class doesn't include the functions which only work on Windows NT4.0. S you don't need to worry about the operating system when you include this class in your application. And it is easy to add Dial-Up Networking common dialog boxes, such as establishing and monitoring connections, or working with phone-book entries, by yourself in your application.

The functions CRasClient supported include:

  1. Create Phone-book entry, and set the properties.
  2. Get information about RAS-capable devices configured on the local computer.
  3. Start and end RAS connection operation.
  4. Get information about existing RAS connections
  5. Get error message and notifications when a RAS connection begins or ends.

The RAS API functions wrapped include:

  • RasGetEntryProperties
  • RasEnumDevices
  • RasSetEntryProperties
  • RasDial
  • RasGetErrorString
  • RasEnumConnections
  • RasValidateEntryName
  • RasGetConnectStatus
  • RasHangUp

The CRasClient header list below:


class CRasClient
{
public:
	CRasClient();
	~CRasClient();

	HRASCONN m_ConnectionHandle;
	RASCONNSTATUS m_rasStatus;

	long GetDeviceCount();
	BOOL GetDeviceNameType(int nIndex, CString& strName, CString& strType);
	BOOL GetModemName(CString* strModemNameArray);
	long GetModemCount();
	DWORD ChangeEntryName(CString strOldName, CString strNewName);
	DWORD CreateNewEntry(CString strEntry, CString strDeviceType, 
		CString strDeviceName, 
		DWORD dwfNetProtocols, DWORD dwFrameProtocal, 
		CString strLocalPhone, CString strPhoneBook = _T(""));
	DWORD SetEntryOption(CString strEntry, DWORD dwfOptions, 
		BOOL bSet = TRUE, CString strPhoneBook = _T(""));
	DWORD SetEntryServerProtocal(CString strEntry, 
		DWORD dwfNetProtocols, CString strPhoneBook = _T(""));
	DWORD SetEntryIPAddress(CString strEntry, int nIPType,
		BYTE a, BYTE b, BYTE c, BYTE d, CString strPhoneBook = _T(""));
	DWORD GetEntryProperties(CString strEntry, RASENTRY* lpRasEntry
		, LPTSTR lpszPhoneBook = NULL);
	DWORD RasDialSyn(CString strEntry, CString strPhoneNumber = _T(""),
		CString strUserName = _T(""), CString strPassword = _T(""), CString strDomain = _T(""), 
		CString strPhoneBook = _T(""));
	DWORD RasDialAsyn(CString strEntry, LPVOID lpvNotifer, CString strPhoneNumber = _T(""),
		CString strUserName = _T(""), CString strPassword = _T(""), CString strDomain = _T(""), 
		CString strPhoneBook = _T(""));
	DWORD RasDialAsynCallback1(CString strEntry, LPVOID lpvNotifer, CString strPhoneNumber = _T(""),
		CString strUserName = _T(""), CString strPassword = _T(""), CString strDomain = _T(""), 
		CString strPhoneBook = _T(""));
	BOOL GetRasStatusString(CString &strStatus, BOOL& bConnected);
	DWORD GetRasErrorString(CString &strError);
	DWORD GetRasErrorStringByErrorCode(DWORD dwError,
		DWORD dwExtendError, CString &strError);
	BOOL HangUpConnection(CString strEntry);
	BOOL GetRasConnection(CString strEntry, HRASCONN &hrasConn);
};

To add the support of Dial-Up Networking to your application, normally you need to implement the tasks as following:

  1. Create a phone-book entry for dialing up.
  2. Set the phone-book entry properties, like phone number, user name, device to use for dialing, dial-up server type, network protocol, etc... just like you set your Dial-up Networking in your desktop.
  3. Start to dial, and try to make connection.
  4. End your connection.

How to use CRasClient class?

Simply add CRasClient.h and CRasClient.cpp to your project, make an instance of the class, and call the functions you need. You don't need to add the library "rasapi32.lib" to your project setting.

About Demo Project

The demo project, "RasDemo", sets up a PPP dial-up networking connection with a Dial-Up Server (Windows 95/98), or RAS Server (Windows NT4.0) using modem connection. After connect, two computers will be able to see each other just like wire network. The protocol "RasDemo" uses is NetBEUI, dial-up server type is PPP.

How to run RasDemo?

  1. Setup your Dial-Up Server. For Windows 95/98, go to "My Computer/Dial-Up Networking", under the menu "Connections", you can find "Dial-Up Server...". You shall have Windows 95 Plus! or Windows 98 installed to have this server. Also make your Dial-Up Adapter attached to NetBEIU in your computer's network setting, and "File and Print Sharing" enabled.
  2. Run RasDemo, enter the phone number you use to dial, click "Connect". If it connects successfully, you will see the dial-up networking monitor icon on task tray, if not, you will get error messages.

How RasDemo works?

When RasDemo Dialog box appears, the modem names which RAS can use to dial fill a combo box, the function CRasDemoDlg::FillModemCombo() is called.


BOOL CRasDemoDlg::FillModemCombo()
{
	int iModemCount = m_pRas->GetModemCount();
	if(iModemCount == 0)
	{
		m_strStatus = "There is no modem installed in your computer!";
		return FALSE;
	}
	else if(iModemCount < 0)
	{
		m_strStatus = "Fail to get modem count!";
		return FALSE;
	}
	CString* strName = new CString[iModemCount];
	BOOL bResult = m_pRas->GetModemName(strName);
	if(bResult)
	{
		for(int i = 0; i < iModemCount; i++)
			m_ctrlModem_Combo.AddString(strName[i]);
		m_ctrlModem_Combo.SetCurSel(0);
		UpdateData(TRUE);
	}
	else
		m_strStatus = "Fail to get Modem name!";
	delete []strName;
	return bResult; 
}

When you click the button "Connect", the code below shows what happens.


void CRasDemoDlg::OnConnectBtn() 
{
	// TODO: Add your control notification handler code here
	m_bUserCancel = FALSE;
	GetDlgItem(IDC_CONNECT_BTN)->EnableWindow(FALSE);
	CString strEntry = "My Network";
	HRASCONN hrasConn;
	if(CreateDialUpEntry())
	{
		if(!m_pRas->GetRasConnection(strEntry, hrasConn))
		{
			if(!DialUpNetwork())
			{
				GetDlgItem(IDC_CONNECT_BTN)->EnableWindow(TRUE);
			}
		}
	}
}

It tries to create a new entry call "My Network" in your default phone-book, and set the properties.


BOOL CRasDemoDlg::CreateDialUpEntry()
{
	UpdateData(TRUE);
	CString strEntry = "My Network";
	if(m_pRas->CreateNewEntry(strEntry, "modem",
		m_strModemName, RASNP_NetBEUI, RASFP_Ppp, m_strPhone) 
		!= ERROR_ALREADY_EXISTS)
	{
		DWORD dwfOptions = RASEO_SwCompression | 
			RASEO_RequireEncryptedPw | RASEO_RequireMsEncryptedPw |
			RASEO_UseLogonCredentials;
		if(m_pRas->SetEntryOption(strEntry, dwfOptions, TRUE) != 0) 
		{
			m_strStatus = "Fail to Set My Network Entry!";
			UpdateData(FALSE);
			return FALSE;
		}
	}
	return TRUE;
}

Then check to see if the connection is already existed for that entry. If not, try to make an asynchronous dial operation: the call returns immediately. You shall supply a callback function, we call rasCallback1 here. Before the connection is established, the window also shows the text messages about the dialing progress.


BOOL CRasDemoDlg::DialUpNetwork()
{
	m_dwError = 0;
	CString strEntry = "My Network";
	DWORD dwError = m_pRas->RasDialAsynCallback1(strEntry, 
		rasCallback1, m_strPhone, m_strUser, m_strPasswd);

	BOOL bLoop = TRUE;
	BOOL bConnected = FALSE;
	while(bLoop)
	{
		CtrlYield();
		if(m_bUserCancel)
			return FALSE;

		bLoop = !(m_pRas->GetRasStatusString(m_strStatus, bConnected));
		GetDlgItem(IDC_CONNECT_STATIC)->SetWindowText(m_strStatus);
		::Sleep(100);	 

		if(m_dwError != 0)
		{
			m_pRas->GetRasErrorStringByErrorCode(m_dwError, m_dwExtendError, m_strStatus);
			GetDlgItem(IDC_CONNECT_STATIC)->SetWindowText(m_strStatus);
			AfxMessageBox("Error: " + m_strStatus);
			m_pRas->HangUpConnection(strEntry);	
			return FALSE; 
		} 
	}

	if(bConnected) 
		return TRUE;
	else
		return FALSE;
}

If you click the "Cancel" to disconnect the connection, it calls CRasClient::HangupConnection(CString strEntry) to disconnect.

Environment:

The Win32 RAS functions are in RASAPI32.DLL. You can revise CRasClient to load this DLL explicitly by using LoadLibrary if RAS is not installed. Shall you want to know how to do this to CRasClient, drop me a mail.

CRasClient is tested under Windows 95, Windows 98, and Windows NT 4.0 SP3 using modem connection. Built by Visual C++ 5.0.

Download demo project - 49 KB

Download source - 5 KB