首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET Compact Framework 3G 撥號上網實作:RAS API 完整封裝與事件驅動架構

     編輯:關於.NET

技術選型:AT Commands vs RAS

.NET CF 開發 3G 應用有兩條路:

  1. AT Commands:直接序列埠發送 AT 指令控制 Modem,控制細節最靈活,但需自行處理 PPP 協議、IP 協商、錯誤重試等極其複雜的流程。
  2. RAS (Remote Access Service):Windows 內建服務,封裝 AT Commands、PPP、IP 協商,提供統一 API 建立 TCP/IP 連線。強烈推薦使用 RAS,亦支援固網撥號、GPRS 等同類場景。

RAS 核心 Native API 對照 (coredll.dll)

API用途
RasGetEntryDialParams取得預設連線項參數 (含儲存的密碼)
RasDial撥號建立連線,支援非同步事件通知
RasHangUp掛斷連線
RasGetConnectStatus查詢目前連線狀態

關鍵 P/Invoke 簽名

[DllImport("coredll.dll", CharSet = CharSet.Auto)]
private static extern int RasGetEntryDialParams(string phoneBook, byte[] dialParams, out uint passwordFlag);

[DllImport("coredll.dll", CharSet = CharSet.Auto)]
private static extern int RasDial(IntPtr extensions, string phoneBook, byte[] dialParams, uint notifierType, IntPtr hwnd, ref int rasConn);

[DllImport("coredll.dll", CharSet = CharSet.Auto)]
private static extern int RasHangUp(int rasConn);

[DllImport("coredll.dll", CharSet = CharSet.Auto)]
private static extern uint RasGetConnectStatus(int rasConn, byte[] connStatus);

事件驅動架構:Windows 訊息 → C# 事件

RasDial 的 notifierType = 0xFFFFFFFF 時,hwnd 可接收 Windows 訊息通知連線狀態變更。繼承 MessageWindow 並覆寫 WndProc 將訊息轉為 C# 事件,實現 Observer 模式。

RasManager 完整實作

using System;
using System.Runtime.InteropServices;
using Microsoft.WindowsCE.Forms; // MessageWindow

public class RasManager : MessageWindow
{
    // 單例
    private static readonly RasManager _instance = new RasManager();
    public static RasManager Instance => _instance;

    // 事件定義
    public delegate void NotifyEvent();
    public event NotifyEvent OnConnectedEvent;
    public event NotifyEvent OnDisconnectedEvent;

    // RAS 狀態枚舉 (節錄關鍵值)
    private enum RASCONNSTATE
    {
        RASCS_Connected = 0x2000,
        RASCS_Disconnected = 0x2001
        // 完整枚舉請參考 RAS Reference
    }

    private const int WM_RASDIALEVENT = 0xCCCD; // RAS 專用訊息

    // WndProc 轉發 Windows 訊息為 C# 事件
    protected override void WndProc(ref Message msg)
    {
        if (msg.Msg == WM_RASDIALEVENT)
        {
            var state = (RASCONNSTATE)msg.WParam.ToInt32();
            switch (state)
            {
                case RASCONNSTATE.RASCS_Connected:
                    OnConnectedEvent?.Invoke();
                    break;
                case RASCONNSTATE.RASCS_Disconnected:
                    OnDisconnectedEvent?.Invoke();
                    break;
                    // 其他狀態可視需求處理 (認證中、重試等)
            }
        }
        base.WndProc(ref msg);
    }

    // 對外 API
    public void DialUp(string entryName, string user, string password)
    {
        // 1. 取得預設參數 (含密碼)
        // 2. 填入使用者輸入的 user/password (若提供)
        // 3. 呼叫 RasDial,傳入 this.Handle 作為 hwnd 接收事件
    }

    public void HangUp() => RasHangUp(_currentConn);

    public RASCONNSTATE GetStatus()
    {
        // 呼叫 RasGetConnectStatus 解析狀態
        return RASCONNSTATE.RASCS_Disconnected;
    }

    private int _currentConn = 0;
}

Client 端使用範例

public class Form1 : Form
{
    public Form1()
    {
        // 訂閱連線事件
        RasManager.Instance.OnConnectedEvent += OnConnected;
        RasManager.Instance.OnDisconnectedEvent += OnDisconnected;
    }

    private void OnConnected()
    {
        this.Invoke(() => lblStatus.Text = "已連線 - 3G");
    }

    private void OnDisconnected()
    {
        this.Invoke(() => lblStatus.Text = "未連線");
    }

    private void btnDial_Click(object sender, EventArgs e)
    {
        RasManager.Instance.DialUp("3G Connection", null, null);
    }

    private void btnHangUp_Click(object sender, EventArgs e)
    {
        RasManager.Instance.HangUp();
    }

    private void btnQueryStatus_Click(object sender, EventArgs e)
    {
        var state = RasManager.Instance.GetStatus();
        MessageBox.Show($"當前狀態: {state}");
    }
}

RAS 事件狀態機完整列表 (節錄)

RASCS_OpenPort → RASCS_PortOpened → RASCS_ConnectDevice → RASCS_DeviceConnected
→ RASCS_AllDevicesConnected → RASCS_Authenticate → RASCS_AuthNotify → RASCS_AuthRetry
→ RASCS_AuthCallback → RASCS_AuthChangePassword → RASCS_AuthProject → RASCS_AuthLinkSpeed
→ RASCS_AuthAck → RASCS_ReAuthenticate → RASCS_Authenticated
→ RASCS_PrepareForCallback → RASCS_WaitForModemReset → RASCS_WaitForCallback
→ RASCS_Projected → RASCS_Interactive → RASCS_RetryAuthentication
→ RASCS_CallbackSetByCaller → RASCS_PasswordExpired
→ RASCS_Connected (成功)
→ RASCS_Disconnected (斷線)

實務注意事項

  1. 連線項建立:可手動在裝置建立連線項,或透過登錄表 HKLM\Comm\RAS\Entries 程式化建立。
  2. 密碼處理:RasGetEntryDialParams 可取得儲存密碼;若使用者輸入新密碼,需更新 RASDIALPARAMS 結構體中對應欄位。
  3. 非同步與 UI:RasDial 為非同步,事件在 MessageWindow 執行緒觸發,**更新 UI 必須用 Invoke**。
  4. 錯誤代碼:RAS API 回傳非 0 表示失敗,常見錯誤:ERROR_INVALID_ENTRY (連線項不存在)、ERROR_NO_CARRIER (無訊號)、ERROR_AUTHENTICATION_FAILURE (認證失敗)。
  5. 多 SIM/多介面:同時間僅能有一條 RAS 連線;切換介面需先 HangUp 再 DialUp 新項目。

延伸應用

同樣的 RAS 封裝可直接支援:

  • 固網撥號 (Modem/ISDN)
  • GPRS/EDGE 上網
  • VPN 連線 (PPTP/L2TP/IPSec)
  • WiFi 連線 (透過 Wireless Zero Configuration 配合)

結語

.NET CF 3G 開發**首選 RAS API**,避開 AT Commands 的協議地獄。核心在於:正確 P/Invoke 簽名、MessageWindow 將 Windows 訊息轉為 C# 事件、Invoke 確保 UI 執行緒安全。掌握此架構,即可快速構建穩健的行動網路應用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved