热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

GPRS管理与创建APN拨号连接

本文主要介绍一些GPRS管理与创建APN拨号连接相关的知识点。新建拨号连接利用RAS实现,本文将详细介绍我在实现创建APN拨号连接时的一些经验,目前来说创建APN连接的资源非常少,尤其是c#实

    本文主要介绍一些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如下图:

              RASManagerDemo

   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建立的拨号连接将保存在注册表中,如下图:

reg4-RASBook

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

(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:
/// 
/// 创建拨号连接
/// 
/// 连接名称
/// apn,访问站点,如:cmnet
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 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// 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);
        }
    }
}
/// 
/// 拨号连接
/// 
/// 
/// 
/// 
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);
            }
        }
    }
}
/// 
/// 断开连接
/// 
/// 
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();
    }
}
/// 
/// 枚举所有的活动的RAS连接
/// 
/// 
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 //测试
            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;
    }
}
/// 
/// 枚举RAS
/// 
/// 
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         {
            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();
    }
}
/// 
/// 枚举所有的可用的连接设备
/// 
/// 
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 "%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)


推荐阅读
  • DNN Community 和 Professional 版本的主要差异
    本文详细解析了 DotNetNuke (DNN) 的两种主要版本:Community 和 Professional。通过对比两者的功能和附加组件,帮助用户选择最适合其需求的版本。 ... [详细]
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 解决JAX-WS动态客户端工厂弃用问题并迁移到XFire
    在处理Java项目中的JAR包冲突时,我们遇到了JaxWsDynamicClientFactory被弃用的问题,并成功将其迁移到org.codehaus.xfire.client。本文详细介绍了这一过程及解决方案。 ... [详细]
  • 本题探讨如何通过最大流算法解决农场排水系统的设计问题。题目要求计算从水源点到汇合点的最大水流速率,使用经典的EK(Edmonds-Karp)和Dinic算法进行求解。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 本文详细分析了Hive在启动过程中遇到的权限拒绝错误,并提供了多种解决方案,包括调整文件权限、用户组设置以及环境变量配置等。 ... [详细]
  • 使用Python在SAE上开发新浪微博应用的初步探索
    最近重新审视了新浪云平台(SAE)提供的服务,发现其已支持Python开发。本文将详细介绍如何利用Django框架构建一个简单的新浪微博应用,并分享开发过程中的关键步骤。 ... [详细]
  • 基于KVM的SRIOV直通配置及性能测试
    SRIOV介绍、VF直通配置,以及包转发率性能测试小慢哥的原创文章,欢迎转载目录?1.SRIOV介绍?2.环境说明?3.开启SRIOV?4.生成VF?5.VF ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 在Linux系统中配置并启动ActiveMQ
    本文详细介绍了如何在Linux环境中安装和配置ActiveMQ,包括端口开放及防火墙设置。通过本文,您可以掌握完整的ActiveMQ部署流程,确保其在网络环境中正常运行。 ... [详细]
  • 本题探讨了在一个有向图中,如何根据特定规则将城市划分为若干个区域,使得每个区域内的城市之间能够相互到达,并且划分的区域数量最少。题目提供了时间限制和内存限制,要求在给定的城市和道路信息下,计算出最少需要划分的区域数量。 ... [详细]
author-avatar
手机用户2502925313
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有