前天專案經理安排我做Wince6下面的APN撥號連線,任務很急,但我對APN一點概念都沒有,之前經理也收集了一些資料代碼,另外iPhone項目組的同事也提供了設定APN的參數資訊(APN網路名稱、帳號、手機號等)。由於以前做過Wince下普通的撥號連線,就在直接上面修改代碼。
一邊看MSDN,一邊百度資料,還是有收穫,對APN有了一定瞭解:APN只是在普通撥號的基礎上多了一些設定參數而已。然而就是這些參數,折磨了我兩天。
要建立APN,首先要使用RasSetEntryProperties來修改撥號連線的屬性參數,函數原型如下:
DWORD RASAPI RasSetEntryProperties (
LPWSTR lpszPhoneBook,
LPWSTR szEntry,
LPRASENTRY lpEntry,
DWORD dwEntrySize,
LPBYTE lpb,
DWORD dwSize
);
第一個參數直接設為NULL,第二個參數為撥號連線的名稱,比如‘寬頻連線’,如果名稱不存在,那麼系統自動建立一個新的串連,第三個參數是撥號連線的屬性,裡面內容繁多,不過只需要設定部分內容即可,代碼如下:
RASENTRY g_objEntry = { 0 };
g_objEntry.dwSize = sizeof( RASENTRY );
_tcscpy( g_objEntry.szDeviceType, RASDT_Modem ); //裝置類型
_tcscpy( g_objEntry.szDeviceName, _T( "CDMAModem" )); //裝置名稱
_tcscpy( g_objEntry.szAreaCode, _T( "10" )); //區號
_tcscpy( g_objEntry.szLocalPhoneNumber, _T( "*99#" )); //手機號碼
g_objEntry.dwCountryCode = 86; //國家、地區代碼
g_objEntry.dwfNetProtocols = RASNP_Ip; //Negotiate the TCP/IP protocol.
g_objEntry.dwFramingProtocol = RASFP_Ppp; //Point-to-Point Protocol (PPP)
最關鍵的參數 g_objEntry.dwfOptions,因為項目裡面規定用使用PAP身分識別驗證協議,剛開始我就直接賦值:
g_objEntry.dwfOptions = RASEO_ProhibitPAP;
沒想到就這一步錯大了,都怪我E文水平不高,最重要的是沒認真看MSDN的說明,下面是關於RASEO_ProhibitPAP的說明,各位看了就知道為什麼我錯了:
If this flag is set to 1, the use of the Password Authentication Protocol (PAP) authentication method is disabled. If the flag is set to zero (0), then the client can negotiate the use of the PAP authentication
method with the server.
g_objEntry.dwfOptions最後賦值為:
g_objEntry.dwfOptions =
RASEO_ProhibitCHAP
| RASEO_ProhibitEAP
| RASEO_ProhibitMsCHAP
| RASEO_ProhibitMsCHAP2
| RASEO_UseCountryAndAreaCodes
| RASEO_SwCompression
| RASEO_IpHeaderCompression;
注意,這裡要根據具體的情況而定,我們的項目指定要PAP。
下面繼續RasSetEntryProperties的第四個參數dwEntrySize,直接賦值sizeof( RASENTRY )即可,第五個參數是串口參數以及APN命令,但是MSDN裡面沒有進一步說明資料是怎樣的格式,這樣導致我花了大量的時間去百度,最終功夫不負有心人,終於找到了答案,下面是網友提供的資料定義:
#pragma pack(1)
typedef struct
{
unsigned short Reserved1; // 0x00, 通常為0x20
unsigned short WaitForCreditCard; // 0x02
unsigned short CancelTimeOut; // 0x04
unsigned short Reserved2; // 0x06
unsigned char Test1; // 0x08
unsigned char Test2; // 0x09
unsigned short Reserved3; // 0x0A
unsigned int BaudRate; // 0x0C
unsigned short Terminal; // 0x10, 通常為0, 1, 3, 7
unsigned char DataBits; // 0x12
unsigned char StopBits; // 0x13
unsigned char Parity; // 0x14
unsigned char FlowControl; // 0x15
TCHAR AtCmd[115]; // 0x16,
} sDevConfig;
#pragma pack()
我在使用中發現裡面有一個地方定義錯了,結構體成員Test1才是流量控制,而FlowControl則固定為1。
至於AtCmd,APN命令,因為電訊廠商以及手機卡的不同,內容可能不一樣,這裡我也把我所使用的格式發上來:
+CGDCONT=1,"IP","xxxx", xxxx代表APN網路名稱,注意,我所使用的命令後面有一個逗號結尾。
後面的內容和普通撥號連線沒什麼區別,我就直接發代碼了:
RASDIALPARAM g_objDialParam = { 0 };
g_objDialParam.dwSize = sizeof( RASDIALPARAMS );
wcscpy( g_objDialParam.szEntryName, wszEntryName );
wcscpy( g_objDialParam.szUserName, _T( "*******" )); //******代表使用者名稱
//採用非同步方式,通過視窗回調得到撥號連線的狀態
DWORD dwResult = RasDial( NULL, NULL, &g_objDialParam, 0xFFFFFFFF, hDlg, &g_hRasConn );
if ( 0 == dwResult )
{
WriteLogMsg( _T( "RasDial調用成功!" ));
}
else
{
WCHAR wszLog[256] = { 0 };
_stprintf( wszLog, _T( "RasDial調用失敗!錯誤碼:%d" ), dwResult );
WriteLogMsg( wszLog );
}
下面是處理撥號過程中的訊息:
case WM_RASDIALEVENT:
{
DWORD dwError = ( DWORD )lParam;
RASCONNSTATE connState = ( RASCONNSTATE )wParam;
switch ( connState )
{
case RASCS_OpenPort:
{ WriteLogMsg( _T( "RASCS_OpenPort..." )); break; }
case RASCS_PortOpened:
{ WriteLogMsg( _T( "RASCS_PortOpened." )); break; }
case RASCS_ConnectDevice:
{ WriteLogMsg( _T( "RASCS_ConnectDevice..." )); break; }
case RASCS_DeviceConnected:
{ WriteLogMsg( _T( "RASCS_DeviceConnected." )); break; }
case RASCS_AllDevicesConnected:
{ WriteLogMsg( _T( "RASCS_AllDevicesConnected." )); break; }
case RASCS_Authenticate:
{ WriteLogMsg( _T( "RASCS_Authenticate..." )); break; }
case RASCS_Authenticated:
{ WriteLogMsg( _T( "RASCS_Authenticated." )); break; }
case RASCS_Connected:
{ WriteLogMsg( _T( "RASCS_Connected." )); break; }
case RASCS_Disconnected:
{ WriteLogMsg( _T( "RASCS_Disconnected." )); break; }
default:
{ WCHAR wszBuf[32] = { 0 };
_stprintf( wszBuf, _T( "其他狀態: %d" ), connState );
WriteLogMsg( wszBuf ); break;
}
}
if ( 0 != dwError )
{
WCHAR wszErrorMsg[128] = { 0 };
_stprintf( wszErrorMsg, _T( "Dial Error: %d" ), dwError );
WriteLogMsg( wszErrorMsg );
}
這裡可以下載完整的撥號庫
.