源:http://www.cnblogs.com/michael-zhangyu/archive/2009/07/04/1516797.html

本文主要介绍一些GPRS管理与创建APN拨号连接相关的知识点。新建拨号连接利用RAS实现,本文将详细介绍我在实现创建APN拨号连接时的一些经验,目前来说创建APN连接的资源非常少,尤其是c#实现的很少。GPRS管理主要是Connection Manager API,目前的资源比较丰富,我就不重复造轮子,提供一些参考资料以供参考。

Connection Manager API

Connection Manager(连接管理器,为一系列API的集合) 系列API的主要目的是为了集中管理基于Windows Mobile系列的设备网络连接的建立与维护。移动应用程序使用 连接管理器API去建立一个网络连接。

  参考资料:

       Windows Mobile上的GPRS连接(基于TcpClient)

http://www.cnblogs.com/fox23/archive/2008/08/15/Establish-a-GPRS-connection-with-TcpClient.html

       GPRS开发系列文章之入门篇

     http://www.cnblogs.com/jsjkandy/archive/2008/08/04/1260490.html

       GPRS开发系列文章之进阶篇

     http://www.cnblogs.com/jsjkandy/archive/2008/08/06/1262445.html

      【重点推荐】Windows Mobile 下使用C#进行GPRS、CDMA开发

http://blog.csdn.net/zhyuanshan/archive/2009/02/26/3940125.aspx

如下图:演示了Connection Manager API的基本功能(Connection Manager API DEMO与RAS DEMO下载在本文后面一起下载)

RAS

RAS(Remote Access Service),即“远程访问服务”,它允许用户从远程地点将自己的计算机连接到一个本地计算机网络。一旦建立了连接,就可以像与局域网中的计算机一样进行通讯,即使计算机实际连接的是一个远程网络。PS: RAS资料真的是相当少,在网上搜索了不少建立拨号连接的资料,基本不能用(大多是建立了连接不能拨号),关于如何输入APN指令更少。在测试新建拨号连接时,最好能准备一台真机测试,否则有些地方会测试不到(比如拨号)。演示DMEO如下图:

   1.新建拨号连接,新建APN连接

创建的拨号连接的操作,其实是创建一个Phone-Book Entries,并将其存入注册表中。

(1)相关API

Thisfunction changes the connection information for an entry in the phone book

or creates a new phone-book entry.

DWORD RasSetEntryProperties(
LPWSTR lpszPhoneBook,
LPWSTR szEntry,
LPRASENTRY lpbEntry,
DWORD dwEntrySize,
LPBYTE lpb,
DWORD dwSize
);
RasSetEntryProperties新建一个拨号连接或修改已存在的拨号连接。
如果想先判断是否存在已知的RAS Entry,可以调用RasValidateEntryName。
lpbEntry是一个RASENTRY指针,包含:连接选项、国家码、区域码、本地电话号,
可以指定本地IP地址、远程DNS服务器IP、远程WINS服务器IP等,
还可以指定连接控制脚本文件路径、采用的协议、调制解调器类型及名称等;
lpb:非常重要。包含一个指定设备配置的缓冲区,设置APN指令会在这里设置。

(2)注册表信息

RasSetEntryProperties建立的拨号连接将保存在注册表中,如下图:

上图中,HKEY_CURRENT_USER\COMM\RASBOOK\ 为所有的RAS连接,
(用RasEnumEntries函数枚举得到的集合就是RASBOOK下的集合);
其中,Entry即lpbEntry参数(RASENTRY结构体);DevCfg为lpb参数设置的设备设置信息;
利用RasSetEntryProperties建立了拨号连接后,我们会发现在WM机器上(设置->连接->连接->管理现有连接)
中找不到已建立的连接。可以通过注册表操作,使新建的拨号连接可以显示在机器的“管理现有连接”中。如下图:
 
 如上图:机器中的”管理现有连接“对应注册表中[Connections]下的集合;
(我们可以手工在机器上添加一个拨号连接,然后参考注册表中的设置,写入注册表)。
经过对比与测试,发现:[Enabled]:是否为默认连接;[RequirePW]是否需要用户名与密码;
[DestId]:表示属于哪种类型的连接;[ConnectionGUID]:表示连接的标识ID,
经过我搜索整个注册表,没有发现其它地方有这个值,因此,可以直接生成一个GUID,为其赋值;
那么[DestId]的值到底是什么呢?我们看看下面这个图,会更清楚。
我们会发现“测试连接2”\[DestId]的值与下图中"Internet 设置"\[DestId]的值相同,意义不言而喻了吧。
 

(3) c#代码测试代码:创建一个"cmnet"的拨号连接,如果是创建APN连接,则替换"cmnet",比如:"szxxxxxxgd.gd"

//新连接
private void button1_Click(object sender, EventArgs e)
{
if (RASManager.CreateRasEntry("测试连接2", "cmnet"))
{
listBox1.Items.Add("测试连接2");
button7.Enabled = true;
}
}
RasManager:
/// <summary>
/// 创建拨号连接
/// </summary>
/// <param name="entryName">连接名称</param>
/// <param name="apn">apn,访问站点,如:cmnet</param>
public static bool CreateRasEntry(string entryName, string apn)
{
RASENTRY rasEntry = new RASENTRY();
//RASDIALPARAMS RasDialParams = new RASDIALPARAMS(); ;
int dwSize = Marshal.SizeOf(rasEntry);
byte[] lpb = new byte[436];
int lpbSize = lpb.Length;
int ret = -1; //初始化
rasEntry.dwSize = dwSize; //测试
//ret = RasGetEntryProperties(null, "我的连接 2", ref rasEntry, ref dwSize, lpb, ref lpbSize); //对应WM中的选择调制解调器与调制解调器类型
rasEntry.szDeviceName = "Cellular Line";//蜂窝电话线路(GPRS)
rasEntry.szDeviceType = "modem";
//
rasEntry.szLocalPhoneNumber = string.Format("~GPRS!{0}", apn);
rasEntry.dwfOptions = 4194304;
rasEntry.dwfNetProtocols = 4;
rasEntry.dwFramingProtocol = 1; //设置lpb
//Pointer to a buffer that contains device-specific configuration information.
//This is opaque TAPI device configuration information
lpb[0] = 180;
lpb[1] = 1;
lpb[4] = 180;
lpb[5] = 1;
lpb[8] = 180;
lpb[9] = 1;
lpb[12] = 1;
lpb[16] = 15;
lpb[20] = 1;
lpb[24] = 2;
lpb[76] = 1;
lpb[80] = 2;
lpb[84] = 2; //从D88位开始设置APN指令
for (int i = 0, step = 0; i < apn.Length; i++, step += 2)
{
lpb[88 + step] = (byte)apn[i];
}
lpb[344] = 1;
lpb[348] = 1; ret = RasSetEntryProperties(null, entryName, ref rasEntry,
rasEntry.dwSize, lpb, lpbSize);
if (ret != 0)
{
return false;
}
//设置拨号用户名与密码。暂时不需要
//dwSize = Marshal.SizeOf(RasDialParams);
//RasDialParams.size = dwSize;
////RasDialParams.dwSize = sizeof(RasDialParams);
//RasDialParams.entryName = "";
//RasDialParams.callbackNumber = "";
//RasDialParams.userName = "";
//RasDialParams.password = ""; //ret = RasSetEntryDialParams("", ref RasDialParams, false);
//if (ret >= 1)
//{
// return false;
//} RegistryKey reg = Registry.LocalMachine;
reg = reg.CreateSubKey("Comm\\ConnMgr\\Providers\\{7C4B7A38-5FF7-4bc1-80F6-5DA7870BB1AA}");
reg = reg.CreateSubKey("Connections");
RegistryKey MASCA = reg.CreateSubKey(entryName);
reg = reg.CreateSubKey(entryName);
reg.SetValue("EntryType", 2, RegistryValueKind.DWord);
reg.SetValue("Enabled", 1, RegistryValueKind.DWord); //是否为默认连接
reg.SetValue("RequirePW", 0, RegistryValueKind.DWord); //是否需要密码 //Internet设置{ADB0B001-10B5-3F39-27C6-9742E785FCD4}
//WAP网络{7022E968-5A97-4051-BC1C-C578E2FBA5D9}
//安全WAP网络{F28D1F74-72BE-4394-A4A7-4E296219390C}
//单位设置{18AD9FBD-F716-ACB6-FD8A-1965DB95B814}
//默认Internet设置{436EF144-B4FB-4863-A041-8F905A62C572}
//默认单位设置{A1182988-0D73-439E-87AD-2A5B369F808B}
//中国移动Internet设置{BAE45F01-FD2D-4E80-B025-1F3B5DADC270}
//中国移动WAP设置{F462B82D-7E08-4AEA-B4BD-BAF97B106EA0}
reg.SetValue("DestId", "{ADB0B001-10B5-3F39-27C6-9742E785FCD4}", RegistryValueKind.String);
reg.SetValue("ConnectionGUID", NewGUID(), RegistryValueKind.String);
reg.Close();
return true;
}
我的思路:先手工在机器上创建一个需要的拨号连接,
然后通过如下代码,获取连接设置后,找到规律,调用RasSetEntryProperties创建新的连接;
ret = RasGetEntryProperties(null, "我的连接 2", ref rasEntry, ref dwSize, lpb, ref lpbSize);

(4)遗留问题 通过以上新建拨号连接的代码新建的拨号连接(比如:测试连接2),在WM机器中“管理现有连接”->右键->“连接”,无法拨号;但是通过IE浏览页面时,又能自动拨号成功;所以这个问题并不影响使用;当我们连接WebService或HttpRequest时,会自动按默认连接拨号; 另一问题:我用C++实现的新建连接,逻辑与上面的C#代码相同(实际上,我是先用C++做的测试,然后翻译为C#),在WM机器中“管理现有连接”->右键->“连接”,可以成功拨号;真是奇怪,希望哪个达人能解释一下:附上我的C++代码:

// 创建RAS连接
void CMFCDEMODlg::OnBnClickedButton4()
{
CreateRasEntry(L"测试连接2",L"cmnet");
}
int CMFCDEMODlg:: CreateRasEntry (LPWSTR lpszName,LPWSTR lpszAPN)
{
DWORD dwSize,
dwError;
TCHAR szError[100];
BYTE lpb[436];
unsigned long lpbSize = sizeof(lpb); RASENTRY RasEntry;
//RASDIALPARAMS RasDailParams; /*
// Validate the format of a connection entry name.
if (dwError = RasValidateEntryName (NULL, lpszName))
{
wsprintf (szError, TEXT("Unable to validate entry name.")
TEXT(" Error %ld"), dwError);
return FALSE;
}
*/
// Initialize the RASENTRY structure.
dwSize = sizeof (RASENTRY);
memset (&RasEntry, 0, dwSize);
memset(lpb,0,lpbSize);
RasEntry.dwSize = dwSize; //// Retrieve the entry properties.
//if (dwError = RasGetEntryProperties (NULL,L"",
// &RasEntry, &dwSize, lpb, &lpbSize))
//{
// wsprintf(szError, TEXT("Unable to read default entry properties.")
// TEXT(" Error %ld"), dwError);
// return FALSE;
//} //对应WM中的选择调制解调器与调制解调器类型
wcscpy(RasEntry.szDeviceName,L"Cellular Line");//蜂?窝?电?话?线?路?(GPRS)
wcscpy(RasEntry.szDeviceType,L"modem");
//
TCHAR szLocalPhoneNumber[RAS_MaxPhoneNumber + 1] = {0};
wsprintf(szLocalPhoneNumber,L"~GPRS!%s",lpszAPN); wcscpy(RasEntry.szLocalPhoneNumber,szLocalPhoneNumber );
RasEntry.dwfOptions=4194304;
RasEntry.dwfNetProtocols=4;
RasEntry.dwFramingProtocol=1; //设置lpb
//Pointer to a buffer that contains device-specific configuration information.
//This is opaque TAPI device configuration information
lpb[0]=180;
lpb[1]=1;
lpb[4]=180;
lpb[5]=1;
lpb[8]=180;
lpb[9]=1;
lpb[12]=1;
lpb[16]=15;
lpb[20]=1;
lpb[24]=2;
lpb[76]=1;
lpb[80]=2;
lpb[84]=2; //从D88位开始设置APN指令
for(int i=0,step=0;i<wcslen(lpszAPN);i++,step+=2)
{
*(lpb + 88 + step)=(BYTE)*(lpszAPN+i);
}
lpb[344]=1;
lpb[348]=1; // Create a new phone-book entry.
if (dwError = RasSetEntryProperties (NULL, lpszName,
&RasEntry, sizeof (RASENTRY),lpb, lpbSize))
{
wsprintf (szError, TEXT("Unable to create the phonebook entry.")
TEXT(" Error %ld"), dwError);
return FALSE;
} //// 设置访问用户名、密码;对应WM中设置中的用户名与密码
//memset (&RasDialParams, 0, sizeof (RASDIALPARAMS));
//RasDialParams.dwSize = sizeof (RASDIALPARAMS);
//wcscpy (RasDialParams.szEntryName, lpszName); //// Insert code here to fill up the RASDIALPARAMS structure.
//wcscpy(RasDialParams.szCallbackNumber, _T(""));//callback option
////disabled
//wcscpy (RasDialParams.szUserName, _T("")); //This is
////optional
//wcscpy (RasDialParams.szPassword, _T("")); //This is optional //// Change the connection data.
//if (dwError = RasSetEntryDialParams (NULL, &RasDialParams, FALSE))
//{
// return FALSE;
//} TCHAR buffer[256]={0};
LPWSTR key=buffer;
wcscat(key,RIL_KEY);
wcscat(key,L"\\");
wcscat(key,lpszName); unsigned long val1 = 0;
unsigned long val2 = 1;
unsigned long val3 = 2;
SetRegistryDWORD(HKEY_LOCAL_MACHINE, key, RIL_REQ_PE, val1); //是否需要密码
SetRegistryDWORD(HKEY_LOCAL_MACHINE, key, RIL_ENABLED, val2); //是否为默认连接
SetRegistryDWORD(HKEY_LOCAL_MACHINE, key, RIL_ENT_TYPE, val3); //Internet设置{ADB0B001-10B5-3F39-27C6-9742E785FCD4}
//WAP网络{7022E968-5A97-4051-BC1C-C578E2FBA5D9}
//安全WAP网络{F28D1F74-72BE-4394-A4A7-4E296219390C}
//单位设置{18AD9FBD-F716-ACB6-FD8A-1965DB95B814}
//默认Internet设置{436EF144-B4FB-4863-A041-8F905A62C572}
//默认单位设置{A1182988-0D73-439E-87AD-2A5B369F808B}
//中国移动Internet设置{BAE45F01-FD2D-4E80-B025-1F3B5DADC270}
//中国移动WAP设置{F462B82D-7E08-4AEA-B4BD-BAF97B106EA0}
SetRegistrySZ(HKEY_LOCAL_MACHINE, key, RIL_DES_ID,
_T("{ADB0B001-10B5-3F39-27C6-9742E785FCD4}")); //一般是Internet设置;
//ConnectionGUID,可以生成一个GUID;
SetRegistrySZ(HKEY_LOCAL_MACHINE, key, RIL_CONN_ID,
NewGUID());
return TRUE;
} LPCWSTR CMFCDEMODlg::NewGUID()
{
static TCHAR buf[38] = {0};
GUID guid;
if (S_OK == ::CoCreateGuid(&guid))
{
_snwprintf(buf, sizeof(buf)
, L"{%08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X}"
, guid.Data1
, guid.Data2
, guid.Data3
, guid.Data4[0], guid.Data4[1]
, guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5]
, guid.Data4[6], guid.Data4[7]);
}
return (LPCWSTR)buf;
} // Registry get and set helper-function implementations
bool CMFCDEMODlg::GetRegistryDWORD(const HKEY hRoot, const TCHAR* const psKeyName,
const TCHAR* const psValueName, DWORD *const pdwValue)
{
bool bSuccess = false;
HKEY hKey;
DWORD dwDWORD;
DWORD dwDisposition;
if((0 != pdwValue) && (ERROR_SUCCESS == RegCreateKeyEx(hRoot,
psKeyName, 0, TEXT(""), 0, KEY_READ, 0, &hKey, &dwDisposition)))
{
DWORD dwValueSize = sizeof(dwDWORD);
bSuccess = ((ERROR_SUCCESS == RegQueryValueEx(hKey, psValueName, 0,
0, (BYTE*)(&dwDWORD), &dwValueSize)) && (sizeof(dwDWORD) ==
dwValueSize));
RegCloseKey(hKey);
if(bSuccess)
{
*pdwValue = dwDWORD;
}
}
return bSuccess;
} bool CMFCDEMODlg::SetRegistryDWORD(const HKEY hRoot, const TCHAR* const psKeyName,
const TCHAR* const psValueName, const DWORD dwValue)
{
bool bSuccess = false;
HKEY hKey;
DWORD dwDisposition;
if(ERROR_SUCCESS == RegCreateKeyEx(hRoot, psKeyName, 0, TEXT(""), 0,
KEY_WRITE, 0, &hKey, &dwDisposition))
{
bSuccess = (ERROR_SUCCESS == RegSetValueEx(hKey, psValueName, 0,
REG_DWORD, (BYTE*)(&dwValue), sizeof(dwValue)));
RegCloseKey(hKey);
}
return bSuccess;
} bool CMFCDEMODlg::SetRegistrySZ(const HKEY hRoot, const TCHAR* const psKeyName,
const TCHAR* const psValueName, const TCHAR *const psValue)
{
bool bSuccess = false;
HKEY hKey;
DWORD dwDisposition;
if((0 != psValue) && (ERROR_SUCCESS == RegCreateKeyEx(hRoot,
psKeyName, 0, TEXT(""), 0, KEY_WRITE, 0, &hKey, &dwDisposition)))
{
bSuccess = (ERROR_SUCCESS == RegSetValueEx(hKey, psValueName, 0,
REG_SZ, (const BYTE* const)psValue, (_tcslen(psValue)
+1)*sizeof(TCHAR)));
RegCloseKey(hKey);
}
return bSuccess;
}
 

2.拨号,建立连接

(1)相关API

This function establishes a RAS connection between a RAS client and a RAS server. The connection data includes callback and user authentication information.

DWORD RasDial(
LPRASDIALEXTENSIONS dialExtensions,
LPTSTR phoneBookPath,
LPRASDIALPARAMS rasDialParam,
DWORD NotifierType,
LPVOID notifier,
LPHRASCONN pRasConn
);

(2)实现代码

请注意:利用RasDial实现拨号时,不会在标题栏上显示"正在拨号...."等等相关状态变化和提示;
但是可以通过Socket测试是否已经连接,或者通过枚举所有活动的RAS连接可以看到;
//测试拨号
private void button7_Click(object sender, EventArgs e)
{
if (listBox1.SelectedIndex >= 0)
{
try
{
RASManager.DialUp(listBox1.Text, null, null);
listBox1.Items.Add("拨号成功.测试连接www.g.cn");
TcpClient tcpClient = new TcpClient(AddressFamily.InterNetwork);
tcpClient.Connect("www.g.cn", 80);
NetworkStream ns = tcpClient.GetStream();
byte[] buf = new byte[128];
ns.Write(buf, 0, 128);
tcpClient.Client.Shutdown(SocketShutdown.Both);
ns.Close();
tcpClient.Close();
listBox1.Items.Add("连接成功,Wrote 128 bytes");
}
catch (Exception ex)
{
listBox1.Items.Add(ex.Message);
}
}
}
/// <summary>
/// 拨号连接
/// </summary>
/// <param name="pEntryName"></param>
/// <param name="pUserName"></param>
/// <param name="pPassword"></param>
public static void DialUp(string pEntryName, string pUserName, string pPassword)
{
//check the connection state first
if (IntPtr.Zero != m_pRasSession)
{
RASCONNSTATE resConnState = (RASCONNSTATE)GetStatus();
if (RASCONNSTATE.RASCS_Connected == resConnState ||
RASCONNSTATE.RASCS_PortOpened == resConnState ||
RASCONNSTATE.RASCS_DeviceConnected == resConnState ||
RASCONNSTATE.RASCS_AllDevicesConnected == resConnState ||
RASCONNSTATE.RASCS_Authenticate == resConnState ||
RASCONNSTATE.RASCS_AuthAck == resConnState ||
RASCONNSTATE.RASCS_Authenticated == resConnState)
{
return;
}
} RASDIALPARAMS rasDialParams = new RASDIALPARAMS();
rasDialParams.size = Marshal.SizeOf(rasDialParams);
rasDialParams.entryName = pEntryName;
rasDialParams.password = pPassword; int lResult = RasDial(IntPtr.Zero, IntPtr.Zero,
           RASDIALPARAMS.ToBytes(rasDialParams), 
           0xFFFFFFFF, IntPtr.Zero, ref m_pRasSession);

    if (lResult != 0)
{
throw new Exception(string.Format("RasDial failed: Error {0}", lResult));
}
}
(3)疑难问题
在实现DialUp方法时,碰到一个问题,
RasDial函数第3个参数:LPRASDIALPARAMS rasDialParam,表示一个RASDIALPARAMS的指针。

在声明RasDial函数时:一开始我声明如下:[DllImport("coredll.dll", CharSet = CharSet.Auto)] private static extern int RasDial(IntPtr pDialExtensions, IntPtr pPhonebook, ref RASDIALPARAMS pRasDialParam, uint pNotifierType, IntPtr pHwnd, ref IntPtr pRasConn);

 

但是始终拨号不成功,测试了好久,没办法,只好按结构体顺序生成byte[],传入一个byte[]才算成功,并把ref RASDIALPARAMS pRasDialParam改为byte[] pRasDialParam;具体代码请下载源码查看;

 

3.终止拨号,断开连接

  (1)相关API

This function terminates a remote access connection. The connection is specified with a RAS connection handle. RasHangUpreleases all RASAPI32.DLL resources associated with the handle.

DWORD RasHangUp(
HRASCONN Session
);
Parameters
Session
Handle to the remote access connection to terminate. This is a handle returned from a previous call to RasDial or RasEnumConnections.
Return Values

Zero indicates success. A nonzero error value listed in the RAS header file or ERROR_INVALID_HANDLE indicates failure.

Include Raserror.h for definitions of the RAS error codes.

Remarks

The connection is terminated even if the RasDialcall has not yet been completed.

After this call, the Sessionhandle can no longer be used.

(2)实现代码

//断开连接
private void button8_Click(object sender, EventArgs e)
{
Connection.RASManager.RASCONN[] conns = RASManager.GetRASConnections();
if (conns !=null && conns.Length > 0)
{
foreach (RASManager.RASCONN conn in conns)
{
try
{
RASManager.HangUp(conn.hRasConnHandle);
listBox1.Items.Add(conn.entryName + " 断开成功!");
}
catch (Exception ex)
{
listBox1.Items.Add(ex.Message);
}
}
}
}
/// <summary>
/// 断开连接
/// </summary>
/// <param name="hSession"></param>
public static void HangUp(IntPtr hSession)
{
int lStatus = RasHangUp(hSession);
if (lStatus != 0)
{
throw new Exception(string.Format("RasHangUp failed: Error {0}", lStatus));
}
}

4.枚举所有活动的(已连接)RAS Connection

(1)相关API

This function lists all active RAS connections. It returns each connection's handle and phone book entry name.

DWORD RasEnumConnections(
LPRASCONN lprasconn,
LPDWORD lpcb,
LPDWORD lpcConnections
);

(2)C#实现代码

//枚举所有活动的RAS连接
private void button3_Click(object sender, EventArgs e)
{
Connection.RASManager.RASCONN[] conns = RASManager.GetRASConnections();
listBox1.Items.Clear();
if (conns != null && conns.Length > 0)
{
listBox1.BeginUpdate();
foreach (RASManager.RASCONN conn in conns)
{
listBox1.Items.Add(conn.entryName);
}
listBox1.EndUpdate();
}
}
/// <summary>
/// 枚举所有的活动的RAS连接
/// </summary>
/// <returns></returns>
public static RASCONN[] GetRASConnections()
{
RASCONN[] retval = null; int size = Marshal.SizeOf(typeof(RASCONN));
int cb = size;
int iConnections = 0;
IntPtr pConnections = IntPtr.Zero; bool retry = false;
do
{
try
{
if (pConnections != IntPtr.Zero)
{
Marshal.FreeHGlobal(pConnections);
}
pConnections = Marshal.AllocHGlobal(cb);
Marshal.Copy(BitConverter.GetBytes(size), 0, pConnections, 4); //pConnections->size = size; int ret = RasEnumConnections(pConnections, ref cb, ref iConnections);
if (ret != 0) //ERROR_BUFFER_TOO_SMALL
{
retry = true;
}
else if(ret==0)
{
retry = false;
if (iConnections > 0)
{
retval = CreateArrayOfType<RASCONN>(pConnections, size, iConnections);
}
}
else
{
throw new Exception(string.Format("RasEnumConnections failed: Error {0}", ret));
}
}
finally
{
if (pConnections != IntPtr.Zero)
{
Marshal.FreeHGlobal(pConnections);
}
}
} while (retry); return retval;
}

(3) C++实现代码

//列举所有的活动RAS连接
void CMFCDEMODlg::OnBnClickedButton3()
{
DWORD dwCb = sizeof(RASCONN);
DWORD dwErr = ERROR_SUCCESS;
DWORD dwRetries = 5;
DWORD dwConnections = 0;
LPRASCONN lpRasConn = NULL; //
// Loop through in case the information from RAS changes between calls.
//
while (dwRetries--)
{
//
// If the memory is allocated, free it.
//
if (NULL != lpRasConn)
{
LocalFree(lpRasConn);
lpRasConn = NULL;
}
//
// Allocate the size needed for the RAS structure.
//
lpRasConn = (LPRASCONN)LocalAlloc(LPTR,dwCb);
if (NULL == lpRasConn)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//
// Set the structure size for version checking purposes.
//
lpRasConn->dwSize = sizeof(RASCONN);
//
// Call the RAS API then exit the loop if we are successful or an unknown
// error occurs.
//
dwErr = RasEnumConnections(
lpRasConn,
&dwCb,
&dwConnections);
if (ERROR_BUFFER_TOO_SMALL != dwErr)
{
break;
}
}
//
// In the success case, print the names of the connections.
//
if (ERROR_SUCCESS == dwErr)
{
DWORD i; printf("The following RAS connections are currently active\n\n");
for (i = 0; i < dwConnections; i++)
{
AfxMessageBox(lpRasConn[i].szEntryName);//测试
printf("%s\n", lpRasConn[i].szEntryName);
}
}
else
{
printf("RasEnumConnections failed: Error = %d\n", dwErr);
}
//
// Free the memory if necessary.
//
if (NULL != lpRasConn)
{
HeapFree(GetProcessHeap(), 0, lpRasConn);
lpRasConn = NULL;
}
}

5.枚举所有可用的RAS 连接

(1)相关API

This function lists all entry names in a remote access phone book.

DWORD RasEnumEntries(
LPWSTR Reserved,
LPWSTR lpszPhoneBookPath,
LPRASENTRYNAME lprasentryname,
LPDWORD lpcb,
LPDWORD lpcEntries
);
(2)C#实现代码
//枚举所有RAS连接
private void button2_Click(object sender, EventArgs e)
{
RASManager.RASENTRYNAME[] entries = RASManager.GetRASEntries();
listBox1.Items.Clear(); if (entries !=null && entries.Length > 0)
{
listBox1.BeginUpdate();
foreach (RASManager.RASENTRYNAME entry in entries)
{
listBox1.Items.Add(entry.szEntryName);
}
listBox1.EndUpdate();
button7.Enabled = true;
}
}
/// <summary>
/// 枚举RAS
/// </summary>
/// <returns></returns>
public static RASENTRYNAME[] GetRASEntries()
{
RASENTRYNAME[] result = null; int size = Marshal.SizeOf(typeof(RASENTRYNAME));
int Cb = size;//new IntPtr(size);
int cEntries = 0; //IntPtr.Zero; IntPtr pEntries = IntPtr.Zero;
try
{
pEntries = Marshal.AllocHGlobal(Cb);
Marshal.Copy(BitConverter.GetBytes(size), 0, pEntries, 4);//pEntries->dwSize = size; uint ret = RasEnumEntries(IntPtr.Zero, IntPtr.Zero, pEntries, ref Cb, ref cEntries);
if (ret != 0) //ERROR_BUFFER_TOO_SMALL;ERROR_NOT_ENOUGH_MEMORY
{
if (pEntries != IntPtr.Zero)
{
Marshal.FreeHGlobal(pEntries);
}
pEntries = Marshal.AllocHGlobal(Cb);
Marshal.Copy(BitConverter.GetBytes(size), 0, pEntries, 4);//pEntries->dwSize = size;
} ret = RasEnumEntries(IntPtr.Zero, IntPtr.Zero, pEntries, ref Cb, ref cEntries);
if (ret == 0) //SUCCESS
{
if (cEntries > 0)
{
result = CreateArrayOfType<RASENTRYNAME>(pEntries, size, cEntries);
}
}
else
{
throw new Exception(string.Format("RasEnumEntries failed: Error {0}", ret));
}
}
finally
{
if (pEntries != IntPtr.Zero)
{
Marshal.FreeHGlobal(pEntries);
}
} return result;
}

(3)C++代码

// 列举所有的RAS void CMFCDEMODlg::OnBnClickedButton2() {     LPRASENTRYNAME lprasentryname={0};     lprasentryname = (LPRASENTRYNAME)LocalAlloc(LPTR, sizeof(RASENTRYNAME));     lprasentryname->dwSize = sizeof(RASENTRYNAME);     DWORD nRet=-1;     DWORD cb=0;     DWORD cEntries=0;

if ((nRet = RasEnumEntries(NULL, NULL, lprasentryname, &cb, &cEntries))         == ERROR_BUFFER_TOO_SMALL) //ERROR_BUFFER_TOO_SMALL     {         if(lprasentryname)         {             ::LocalFree(lprasentryname);             lprasentryname=NULL;         }         lprasentryname = (LPRASENTRYNAME)LocalAlloc(LPTR, cb);         lprasentryname->dwSize = sizeof(RASENTRYNAME);     }

// Calling RasEnumEntries to enumerate the phonebook entries       nRet = RasEnumEntries(NULL, NULL, lprasentryname, &cb, &cEntries);

if (nRet != ERROR_SUCCESS)     {         printf("RasEnumEntries failed: Error %d\n", nRet);     }     else     {         printf("Phone book entries in the default phonebook:\n\n");         for(int i=0;i < cEntries;i++)         {             printf("%S\r\n",lprasentryname->szEntryName);             lprasentryname++;         }     }

if(lprasentryname)     {         ::LocalFree(lprasentryname);         lprasentryname=NULL;     }

}

6.枚举所有的RAS设备

(1)相关API

This function returns the name and type of all available RAS-capable devices.

DWORD RasEnumDevices( 
LPRASDEVINFOW lpRasDevinfo,
LPDWORD lpcb,
LPDWORD lpcDevices
);
(2)C#实现代码
//枚举RAS设备
private void button4_Click(object sender, EventArgs e)
{
Connection.RASManager.RASDEVINFO[] devices = RASManager.GetRASDevices();
listBox1.Items.Clear(); if (devices !=null && devices.Length > 0)
{
listBox1.BeginUpdate();
foreach (RASManager.RASDEVINFO device in devices)
{
listBox1.Items.Add(device.name + " " + device.type);
}
listBox1.EndUpdate();
}
}
/// <summary>
/// 枚举所有的可用的连接设备
/// </summary>
/// <returns></returns>
public static RASDEVINFO[] GetRASDevices()
{
RASDEVINFO[] retval = null; int size = Marshal.SizeOf(typeof(RASDEVINFO));
int lpCb = size;
int lpcDevices = 0;
bool retry = false;
do
{
IntPtr pDevices = IntPtr.Zero;
try
{
if (pDevices != IntPtr.Zero)
{
Marshal.FreeHGlobal(pDevices);
} pDevices = Marshal.AllocHGlobal(lpCb);
Marshal.Copy(BitConverter.GetBytes(size), 0, pDevices, 4); int ret = RasEnumDevices(pDevices, ref lpCb, ref lpcDevices);
if (ret !=0)
{
retry = true;
}
else if (ret ==0)
{
retry = false; retval = CreateArrayOfType<RASDEVINFO>(pDevices, size, lpcDevices);
}
else
{
throw new Exception(string.Format("RasEnumDevices failed: Error {0}", ret));
}
}
finally
{
if (pDevices != IntPtr.Zero)
{
Marshal.FreeHGlobal(pDevices);
}
}
}
while (retry);
return retval;
}

(3)C++实现代码

/枚举所有RAS设备
void CMFCDEMODlg::OnBnClickedButton5()
{
DWORD dwCb = sizeof(RASDEVINFO);
DWORD dwErr = ERROR_SUCCESS;
DWORD dwRetries = 5;
DWORD dwDevices = 0;
RASDEVINFO* lpRasDevInfo = NULL; //
// Loop through in case the information from RAS changes between calls.
//
while (dwRetries--)
{
//
// If the memory is allocated, free it.
//
if (NULL != lpRasDevInfo)
{
HeapFree(GetProcessHeap(), 0, lpRasDevInfo);
lpRasDevInfo = NULL;
}
//
// Allocate the size need for the RAS structure.
//
lpRasDevInfo =(RASDEVINFO*) HeapAlloc(GetProcessHeap(), 0, dwCb);
if (NULL == lpRasDevInfo)
{
dwErr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
//
// Set the structure size for version checking purposes.
//
lpRasDevInfo->dwSize = sizeof(RASDEVINFO);
//
// Call the RAS API, bail on the loop if we are successful or an unknown
// error occurs.
//
dwErr = RasEnumDevices(
lpRasDevInfo,
&dwCb,
&dwDevices);
if (ERROR_BUFFER_TOO_SMALL != dwErr)
{
break;
}
}
//
// In the success case print the names of the devices.
//
if (ERROR_SUCCESS == dwErr)
{
DWORD i; printf("The following RAS capable devices were found on this machine:\n\n");
for (i = 0; i < dwDevices; i++)
{
printf("%S\n", lpRasDevInfo[i].szDeviceName);
}
}
else
{
printf("RasEnumDevices failed: Error = %d\n", dwErr);
}
//
// Free the memory if necessary.
//
if (NULL != lpRasDevInfo)
{
HeapFree(GetProcessHeap(), 0, lpRasDevInfo);
lpRasDevInfo = NULL;
}
}

好了,到此RAS介绍完毕;之所以有2套代码,因为基本都是先用C++实现后,再翻译到C#去的。

C#调用P/Invoke实在很痛苦,声明常量,结构体,函数声明等等,实在太麻烦,仅仅声明相关结构体与常量就花了近一天时间,调试过程碰到好几次,因为结构体长度对应不上而出错,又到C++ 头文件中一个一个对比。

DEMO下载,请点击这里。(环境:vs2008 + wm 5.0 SDK)

GPRS管理与创建APN拨号连接(转)的更多相关文章

  1. 启用密码管理之前创建的用户连接Oracle报ORA-28002处理一则

    处理方法其实很简单.只要:     alter user <username> identified by <same password>;  这个操作后,恢复正常了 下面作个 ...

  2. 全易通人事考勤工资验厂管理系统软件创建连接SQL2000数据库的操作方法和说明

    全易通人事考勤工资验厂管理系统软件创建连接SQL2000数据库的操作方法和说明.全易通人事考勤工资验厂管理系统软件,有2种数据库,一个是ACCESS,另一个是SQL.不过由于ACCESS数据库比较小, ...

  3. Code First开发系列之管理数据库创建,填充种子数据以及LINQ操作详解

    返回<8天掌握EF的Code First开发>总目录 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LINQ to ...

  4. 8天掌握EF的Code First开发系列之3 管理数据库创建,填充种子数据以及LINQ操作详解

    本文出自8天掌握EF的Code First开发系列,经过自己的实践整理出来. 本篇目录 管理数据库创建 管理数据库连接 管理数据库初始化 填充种子数据 LINQ to Entities详解 什么是LI ...

  5. javax.transaction.xa.XAException: java.sql.SQLException: 无法创建 XA 控制连接。(SQL 2000,SQL2005,SQL2008)

    javax.transaction.xa.XAException: java.sql.SQLException:无法创建 XA 控制连接.错误: 未能找到存储过程'master..xp_sqljdbc ...

  6. NT平台ADSL拨号连接密码恢复原理

    NT平台拨号连接密码恢复原理 创建时间:2004-11-08 更新时间:2004-11-12文章属性:原创文章提交:eyas (eyas_at_xfocus.org)NT平台拨号连接密码恢复原理Aut ...

  7. Entity Framework应用:使用Code First模式管理数据库创建和填充种子数据

    一.管理数据库连接 1.使用配置文件管理连接之约定 在数据库上下文类中,如果我们只继承了无参数的DbContext,并且在配置文件中创建了和数据库上下文类同名的连接字符串,那么EF会使用该连接字符串自 ...

  8. ZeroMQ接口函数之 :zmq_connect - 由一个socket创建一个对外连接

    ZeroMQ 官方地址 :http://api.zeromq.org/4-0:zmq_connect zmq_connect(3)  ØMQ Manual - ØMQ/3.2.5 Name zmq_c ...

  9. ADSL拨号连接

    dotras是一个提供远程访问服务的组件,使用它可以方便的 进行宽带拨号连接 由于不同的系统中dotras调用的底层api可能不同,所以使用时要根据不同的系统使用不同的dotras. dotras目前 ...

随机推荐

  1. jQuery.on() 函数详解[http://www.365mini.com/page/jquery-on.htm]

    中文手册 2014年09月01日 暂无评论 on()函数用于为指定元素的一个或多个事件绑定事件处理函数. 此外,你还可以额外传递给事件处理函数一些所需的数据. 从jQuery 1.7开始,on()函数 ...

  2. chrome浏览器debug

    Chrome浏览器审查元素 1.Elements标签页 Elements标签页的左侧就是对页面HTML结构的查看与编辑,你可以直接在某个元素上双击修改元素的属性. 1.Edit as HTML直接对元 ...

  3. OC-之NSDate、万年历

    万年历计算星期-基姆拉尔森公式 #include /* * 基姆拉尔森计算公式* W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7*/int week(int ...

  4. POJ 3548 Restoring the digits

    暴力搜索.注意题目说每个字母对应的数字不同,这句话表明最多只有10个字母,所以暴力DFS绝对不会TLE. #include<cstdio> #include<cstring> ...

  5. Linux 压缩解压命令

    .tar 解包:tar xvf FileName.tar 打包:tar cvf FileName.tar DirName (注:tar是打包,不是压缩!) ---------------------- ...

  6. Chapter 1 First Sight——31

    I took notes carefully anyway, always looking down. 不论怎么样我都仔细的记着笔记,一直低着头. I couldn't stop myself fro ...

  7. git远程分支--remote

    查看所有远程引用: $ git ls-remote From ssh://someone@example/testgit ebf3ef7551603cd57a699e80db0bfab36d1aa7b ...

  8. Dokan虚拟磁盘开发实战

    因工作需要,最近与同事合作使用Dokan开发了一个虚拟磁盘的简单程序,初步实现了远程目录映射到本地虚拟磁盘的功能. 远程服务端是用Python写的,主要是将远程主机上的目录文件传给客戶端,在这里就不细 ...

  9. 括号匹配(C++ Stack)

    最近在学习C++,所以使用stack容器来实现括号匹配 /**********************************************************/ stack<Ty ...

  10. 解决centos网速特别慢的最佳解决办法

    摘自:http://www.centoscn.com/CentosBug/osbug/2014/0614/3138.html 我使用了centOS,但是发现网速实在是卡得几乎不能上网,连百度都打不开, ...