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

C# 調用 RAS API 實現 ADSL 寬帶撥號上網完整封裝類

     編輯:關於C#

背景與前提

  • 適用於 Windows 系統已建立「寬帶連線」(PPPoE)撥號項目。
  • 基於 `rasapi32.dll`、`wininet.dll` 非托管 API,透過 P/Invoke 調用。
  • 封裝為 RASDisplay 類,提供連線、斷線、狀態查詢、流量統計功能。

核心結構體定義


[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASCONN
{
    public int dwSize;
    public IntPtr hrasconn;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szEntryName;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 17)]  public string szDeviceType;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 129)] public string szDeviceName;
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASSTATS
{
    public int dwSize;
    public int dwBytesXmited;   // 發送位元組
    public int dwBytesRcved;    // 接收位元組
    public int dwFramesXmited;
    public int dwFramesRcved;
    public int dwCrcErr;
    public int dwTimeoutErr;
    public int dwConnectionDuration; // 連線持續時間(毫秒)
    // ... 其他統計欄位
}

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct RASENTRYNAME
{
    public int dwSize;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 257)] public string szEntryName;
}

P/Invoke 簽名


internal static class NativeMethods
{
    [DllImport("rasapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    internal static extern int RasEnumConnections(
        ref RASCONN lprasconn, ref int lpcb, ref int lpcConnections);

    [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
    internal static extern uint RasGetConnectionStatistics(
        IntPtr hRasConn, [In, Out] ref RASSTATS lpStatistics);

    [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
    internal static extern uint RasHangUp(IntPtr hrasconn);

    [DllImport("rasapi32.dll", CharSet = CharSet.Auto)]
    internal static extern uint RasEnumEntries(
        string reserved, string lpszPhonebook,
        [In, Out] RASENTRYNAME[] lprasentryname,
        ref int lpcb, out int lpcEntries);

    [DllImport("wininet.dll", CharSet = CharSet.Auto)]
    internal static extern int InternetDial(
        IntPtr hwnd, string lpszConnectoid,
        uint dwFlags, ref int lpdwConnection, uint dwReserved);
}

高層封裝類:RASDisplay


public class RASDisplay
{
    private RASCONN _currentConn;
    private RASSTATS _stats;
    private bool _connected;

    public RASDisplay()
    {
        RefreshStatus();
    }

    /// 重新掃描當前撥號連線狀態
    public void RefreshStatus()
    {
        _connected = false;
        var conn = new RASCONN { dwSize = Marshal.SizeOf(typeof(RASCONN)) };
        int cb = conn.dwSize, count = 0;
        int ret = NativeMethods.RasEnumConnections(ref conn, ref cb, ref count);
        if (ret != 0 || count == 0) return;

        _currentConn = conn;
        var stats = new RASSTATS { dwSize = Marshal.SizeOf(typeof(RASSTATS)) };
        NativeMethods.RasGetConnectionStatistics(conn.hrasconn, ref stats);
        _stats = stats;
        _connected = true;
    }

    /// 撥號連線(entryName 為「網路連線」中顯示的名稱)
    public int Connect(string entryName)
    {
        const uint INTERNET_AUTODIAL_UNATTENDED = 2;
        int connHandle = 0;
        return NativeMethods.InternetDial(IntPtr.Zero, entryName, 
            INTERNET_AUTODIAL_UNATTENDED, ref connHandle, 0);
    }

    /// 掛斷當前連線
    public void Disconnect()
    {
        if (_connected)
            NativeMethods.RasHangUp(_currentConn.hrasconn);
    }

    // --- 唯讀屬性 ---
    public bool IsConnected => _connected;
    public string EntryName => _connected ? _currentConn.szEntryName : "";
    public string Duration => _connected ? FormatDuration(_stats.dwConnectionDuration) : "";
    public long BytesSent => _connected ? _stats.dwBytesXmited : 0;
    public long BytesReceived => _connected ? _stats.dwBytesRcved : 0;
    public string[] AllEntries => GetAllEntryNames();

    private string FormatDuration(int ms)
    {
        int s = ms / 1000;
        int h = s / 3600; s %= 3600;
        int m = s / 60; s %= 60;
        return $"{h}h {m}m {s}s";
    }

    private string[] GetAllEntryNames()
    {
        int count = 1, size = Marshal.SizeOf(typeof(RASENTRYNAME));
        var entries = new RASENTRYNAME[count];
        entries[0].dwSize = size;
        NativeMethods.RasEnumEntries(null, null, entries, ref size, out count);
        if (count > entries.Length)
        {
            entries = new RASENTRYNAME[count];
            for (int i = 0; i < count; i++) entries[i].dwSize = size;
            NativeMethods.RasEnumEntries(null, null, entries, ref size, out count);
        }
        return entries.Take(count).Select(e => e.szEntryName).ToArray();
    }
}

使用範例


var ras = new RASDisplay();
Console.WriteLine($"現有撥號項目: {string.Join(", ", ras.AllEntries)}");

if (!ras.IsConnected)
{
    int ret = ras.Connect("ADSL"); // "ADSL" 為撥號連線名稱
    Console.WriteLine(ret == 0 ? "撥號成功" : $"撥號失敗,錯誤碼: {ret}");
}
else
{
    Console.WriteLine($"已連線: {ras.EntryName}, 時長: {ras.Duration}");
    Console.WriteLine($"上傳: {ras.BytesSent / 1024} KB, 下載: {ras.BytesReceived / 1024} KB");
    // ras.Disconnect(); // 斷線
}

注意事項

  • 需以**管理員權限**運行,否則 `RasHangUp` 可能拒絕存取。
  • 撥號項目名稱需與「網路連線」中顯示完全一致(區分大小寫)。
  • `InternetDial` 為同步阻塞呼叫,UI 程式建議放入背景執行緒。
  • 現代系統建議使用 `DotRas` 開源庫,提供更完整的托管封裝與異步支援。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved