在 NetAnts、Download Expert 等下載工具中,都內建了定時撥號上網的功能。對於一般使用者而言,撥號上網主要仰賴 Windows 的遠程存取服務。本文將詳細介紹如何在 Visual C++ 環境下,運用 RAS API 實作撥號連線功能。
Visual C++ 提供了 ras.h 標頭檔,其中宣告了 RAS API。實作撥號上網的關鍵在於 RasDial 函式,其原型如下:
DWORD RasDial(
LPRASDIALEXTENSIONS lpRasDialExtensions,
LPCTSTR lpszPhonebook,
LPRASDIALPARAMS lpRasDialParams,
DWORD dwNotifierType,
LPVOID lpvNotifier,
LPHRASCONN lphRasConn
);
RASDIALPARAMS 結構,包含連線名稱、電話號碼、使用者名稱、密碼等資訊。
dwSize:設為 sizeof(RASDIALPARAMS)szEntryName 與 szPhoneNumber:若指定連線名稱(如「我的連線」),電話號碼可留空;若直接指定號碼,連線名稱留空,電話號碼填入 ISP 號碼(如 169、663)。使用 201 電話卡時,格式為 201,,,帳號,密碼#,ISP號碼#(逗號代表暫停)。szUserName、szPassword:登入帳密,如 169 共用帳號 guest/guest。HRASCONN 變數,呼叫前必須設為 NULL,成功後會儲存連線控制代碼,供後續斷線使用。透過註冊視窗訊息 WM_RASEVENT,即可即時取得撥號過程狀態。在對話方塊類別中加入:
const UINT WM_RASEVENT = ::RegisterWindowMessageA(RASDIALEVENT);
於訊息對應表加入:
ON_REGISTERED_MESSAGE(WM_RASEVENT, OnRasDialEvent)
實作處理函式:
LRESULT CMyDialog::OnRasDialEvent(WPARAM wp, LPARAM lp)
{
RASCONNSTATE rasstate = (RASCONNSTATE)wp;
CListBox* pList = (CListBox*)GetDlgItem(IDC_INFOLIST);
switch (rasstate)
{
case RASCS_OpenPort: pList->AddString(_T("開啟連接埠...")); break;
case RASCS_PortOpened: pList->AddString(_T("連接埠已開啟")); break;
case RASCS_ConnectDevice: pList->AddString(_T("連接裝置...")); break;
case RASCS_DeviceConnected: pList->AddString(_T("裝置已連接")); break;
case RASCS_Authenticate: pList->AddString(_T("驗證帳號密碼")); break;
case RASCS_Authenticated: pList->AddString(_T("驗證通過")); break;
case RASCS_Connected: pList->AddString(_T("已連線")); break;
case RASCS_Disconnected:
pList->AddString(_T("連線已中斷"));
m_hRasConn = NULL; // 重設連線控制代碼
break;
}
return 0;
}
使用 RasHangUp 並搭配等待機制,避免連接埠未正確關閉導致下次無法撥號:
if (m_hRasConn != NULL)
{
RasHangUp(m_hRasConn);
m_hRasConn = NULL;
Sleep(2000); // 必須等待,否則可能導致連接埠殘留
}
更穩健的作法是輪詢 RasGetConnectStatus 直到傳回 ERROR_INVALID_HANDLE:
RASCONNSTATUS rStatus;
while (RasGetConnectStatus(m_hRasConn, &rStatus) != ERROR_INVALID_HANDLE)
{
::Sleep(0);
}
當瀏覽器停止回應或需重新執行程式控制連線時,可用 RasEnumConnections 列舉現有連線:
HRASCONN hRasConnect;
DWORD dwBufferSize, dwNumConnections;
LPRASCONN lpRasConn = new RASCONN[3];
lpRasConn[0].dwSize = sizeof(RASCONN);
dwBufferSize = 3 * sizeof(RASCONN);
if (RasEnumConnections(lpRasConn, &dwBufferSize, &dwNumConnections) == 0)
{
for (DWORD i = 0; i < dwNumConnections; i++)
{
hRasConnect = lpRasConn[i].hrasconn;
RasHangUp(hRasConnect);
::Sleep(2000);
}
}
delete[] lpRasConn;
至此,一個基本的撥號上網程式已完成。若需深入了解伺服器端程式設計,請參考 MSDN Platform SDK 中的 Remote Access Service 文件。