C#通过DotRas实现完美控制VPN和PPPoE(ADSL)
使用DotRas实现对VPN和PPPoE(ADSL)完美管理和控制
支持增、删、改、查、拨号(连接)、断开、查询实时连接状态、查询IP
完全基于Windows的RAS API操作,功能和效率并非百度到的那些使用CMD进行操作的例子可比的!!
无论VPN、PPPoE(ADSL)还是其他神马的拨号连接其实都是基于RAS的,DotRas就是基于Windows原生RAS API包装翻译成C#的库,有兴趣的童鞋可以自己研究研究
DotRas有多个版本,几乎每个不同Win大版本都有对应的RasAPI版本,为了最好的兼容新旧平台使用的是以WinXP API为基础编译的版本。也就是不兼容XP以下的系统,我想应该没谁还在用XP以下版本系统!
DotRas的库和源码可以到他的官网下载,为了方便某些上外网有问题滴童鞋本文在末尾会提供DotRas V1.3的源码
DotRas官网:http://dotras.codeplex.com/
为了防止某些童鞋实在弄不懂怎么用这几个类,特意做了个Demo
为了写这个Demo,可是耗费了我2个多小时........
不过Demo就是Demo,自然会有点BUG(影响不大),我也懒的修复了,反正童鞋们能看懂就行
对了对了,这Demo还加上的自动添加静态路由功能!!相关的静态路由管理类嘛,我会在下篇博文里发出来,或者各位童鞋可以直接到文尾下载Demo看
废话不说,直接看代码
RAS管理器基类
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using DotRas; namespace RBTI.Toolbox.RasConnect.Ras { abstract class RasManager : IEnumerable<RasEntry> { /// <summary> /// 所有活动Ras连接 /// </summary> public static IEnumerable<RasConnection> AllActiveRasConnections { get { return RasConnection.GetActiveConnections(); } } protected RasDeviceType DeciceType { get; private set; } /// <summary> /// 所有活动连接 /// </summary> public IEnumerable<RasConnection> AllActiveConnections { get { return RasConnection.GetActiveConnections().Where(c => c.Device.DeviceType == DeciceType); } } /// <summary> /// 电话簿路径 /// </summary> public string PhoneBookPath { get; private set; } /// <summary> /// 活动连接 /// </summary> public IEnumerable<RasConnection> ActiveConnections { get { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); return RasConnection.GetActiveConnections().Where(c => c.Device.DeviceType == DeciceType && usersPhoneBook.Entries.Contains(c.EntryName)); } } /// <summary> /// 获取实体 /// </summary> /// <param name="name">实体名称</param> /// <returns></returns> public RasEntry this[string name] { get { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); return usersPhoneBook.Entries[name]; } } /// <summary> /// Ras管理器 /// </summary> /// <param name="phoneBookPath">电话簿路径</param> public RasManager(string phoneBookPath, RasDeviceType deciceType) { PhoneBookPath = phoneBookPath; DeciceType = deciceType; } /// <summary> /// 设置DNS地址(设置首选和备用地址都为0.0.0.0时是自动获取) /// </summary> /// <param name="name">实体名称</param> /// <param name="dnsAddress">首选DNS地址</param> /// <param name="dnsAddressAlt">备用DNS地址</param> public void SettingDns(string name, IPAddress dnsAddress, IPAddress dnsAddressAlt) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); if (!usersPhoneBook.Entries.Contains(name)) return; RasEntry entry = usersPhoneBook.Entries[name]; entry.DnsAddress = dnsAddress; entry.DnsAddressAlt = dnsAddressAlt; entry.Update(); } /// <summary> /// 设置IP地址(设置为0.0.0.0时是自动获取) /// </summary> /// <param name="name">实体名称</param> /// <param name="ipAddress">IP地址</param> public void SettingIp(string name, IPAddress ipAddress) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); if (!usersPhoneBook.Entries.Contains(name)) return; RasEntry entry = usersPhoneBook.Entries[name]; entry.IPAddress = ipAddress; entry.Update(); } /// <summary> /// 设置验证信息(设置为null时是清空已保存的验证信息) /// </summary> /// <param name="name">实体名称</param> /// <param name="credentials">身份信息</param> public void SettingCredentials(string name, NetworkCredential credentials) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); if (!usersPhoneBook.Entries.Contains(name)) return; RasEntry entry = usersPhoneBook.Entries[name]; if (credentials == null) entry.ClearCredentials(); else entry.UpdateCredentials(credentials); } /// <summary> /// 设置是否为默认网关 /// </summary> /// <param name="name">实体名称</param> /// <param name="remoteDefaultGateway">是否为默认网关</param> public void SettingRemoteDefaultGateway(string name, bool remoteDefaultGateway) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); if (!usersPhoneBook.Entries.Contains(name)) return; RasEntry entry = usersPhoneBook.Entries[name]; entry.Options.RemoteDefaultGateway = remoteDefaultGateway; entry.Update(); } /// <summary> /// 删除 /// 在连接过程中删除不会自动断开,请先执行断开操作 /// </summary> /// <param name="name">实体名称</param> public void Remove(string name) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); if (usersPhoneBook.Entries.Contains(name)) usersPhoneBook.Entries.Remove(name); } public IEnumerator<RasEntry> GetEnumerator() { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); return usersPhoneBook.Entries.Where(c => c.Device.DeviceType == DeciceType).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); return usersPhoneBook.Entries.Where(c => c.Device.DeviceType == DeciceType).GetEnumerator(); } } }
VPN管理器类
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using DotRas; namespace RBTI.Toolbox.RasConnect.Ras { class VpnManager : RasManager { /// <summary> /// VPN管理器 /// </summary> /// <param name="phoneBookPath">电话簿路径</param> public VpnManager(string phoneBookPath) : base(phoneBookPath, RasDeviceType.Vpn) { } /// <summary> /// VPN管理器 /// </summary> /// <param name="phoneBookType">电话簿类型</param> public VpnManager(RasPhoneBookType phoneBookType) : this(RasPhoneBook.GetPhoneBookPath(phoneBookType)) { } /// <summary> /// VPN管理器 /// 打开全局用户电话簿 /// </summary> public VpnManager() : this(RasPhoneBookType.AllUsers) { } /// <summary> /// 添加 /// </summary> /// <param name="name">实体名称</param> /// <param name="serverAddress">服务器地址</param> /// <param name="remoteDefaultGateway">是否使用默认网关</param> /// <returns></returns> public RasEntry Add(string name, string serverAddress, bool remoteDefaultGateway) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); RasEntry entry = RasEntry.CreateVpnEntry( name, serverAddress, RasVpnStrategy.Default, RasDevice.GetDevices().First(o => o.DeviceType == RasDeviceType.Vpn)); entry.Options.RequireMSChap2 = true; entry.Options.RemoteDefaultGateway = remoteDefaultGateway; usersPhoneBook.Entries.Add(entry); return entry; } /// <summary> /// 更改 /// </summary> /// <param name="name">实体名称</param> /// <param name="serverAddress">服务器地址</param> /// <param name="remoteDefaultGateway">是否使用默认网关</param> public void Update(string name, string serverAddress, bool remoteDefaultGateway) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); if (!usersPhoneBook.Entries.Contains(name)) return; RasEntry entry = usersPhoneBook.Entries[name]; entry.PhoneNumber = serverAddress; entry.Options.RequireMSChap2 = true; entry.Options.RemoteDefaultGateway = remoteDefaultGateway; entry.Update(); } } }
拨号宽[PPPoE(ADSL)]管理器类
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using DotRas; namespace RBTI.Toolbox.RasConnect.Ras { class BroadbandManager : RasManager { /// <summary> /// PPPoE管理器 /// </summary> /// <param name="phoneBookPath">电话簿路径</param> public BroadbandManager(string phoneBookPath) : base(phoneBookPath, RasDeviceType.PPPoE) { } /// <summary> /// PPPoE管理器 /// </summary> /// <param name="phoneBookType">电话簿类型</param> public BroadbandManager(RasPhoneBookType phoneBookType) : this(RasPhoneBook.GetPhoneBookPath(phoneBookType)) { } /// <summary> /// PPPoE管理器 /// 打开全局用户电话簿 /// </summary> public BroadbandManager() : this(RasPhoneBookType.AllUsers) { } /// <summary> /// 添加宽带 /// </summary> /// <param name="name">宽带名称</param> /// <param name="remoteDefaultGateway">是否使用默认网关</param> /// <returns></returns> public RasEntry Add(string name,bool remoteDefaultGateway = true) { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(PhoneBookPath); RasEntry entry = RasEntry.CreateBroadbandEntry(name, RasDevice.GetDevices().First(o => o.DeviceType == RasDeviceType.PPPoE), remoteDefaultGateway); usersPhoneBook.Entries.Add(entry); return entry; } } }
RAS拨号连接类
为了程序结构清晰,拨号连接特意分开一个类
这个类支持对连接状态进行实时检测,包括断线检测
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ComponentModel; using System.Net; using System.Timers; using DotRas; namespace RBTI.Toolbox.RasConnect.Ras { class RasDialConnection { #region 字段 private RasDialer dialer; private RasHandle rasHandle; private RasConnection connection; private RasConnectionState state; private Timer timer; #endregion #region 事件 /// <summary> /// 拨号完成事件 /// </summary> public event EventHandler<DialCompletedEventArgs> DialCompleted; /// <summary> /// 状态修改事件 /// </summary> public event EventHandler<StateChangedEventArgs> StateChanged; #endregion #region 属性 /// <summary> /// 是否可以发布事件 /// 默认为True /// </summary> public bool CanRaiseEvents { get; set; } /// <summary> /// 同步对象 /// </summary> public ISynchronizeInvoke SynchronizingObject { get; set; } /// <summary> /// 实体名称 /// </summary> public string EntryName { get { return dialer.EntryName; } } /// <summary> /// IP地址 /// </summary> public IPAddress IPAddress { get; private set; } /// <summary> /// 服务器IP地址 /// </summary> public IPAddress ServerIPAddress { get; private set; } /// <summary> /// 实时状态 /// </summary> public RasConnectionState State { get { if (rasHandle == null || rasHandle.IsInvalid || rasHandle.IsClosed) return RasConnectionState.Disconnected; if (connection == null) return state; else { try { return connection.GetConnectionStatus().ConnectionState; } catch (InvalidHandleException) { return RasConnectionState.Disconnected; } } } } #endregion /// <summary> /// Ras连接 /// </summary> /// <param name="phoneBookPath">电话簿路径</param> /// <param name="entryName">实体名称</param> /// <param name="synchronizingObject">同步对象</param> public RasDialConnection(string phoneBookPath, string entryName, ISynchronizeInvoke synchronizingObject) { SynchronizingObject = synchronizingObject; CanRaiseEvents = true; timer = new Timer(100); timer.AutoReset = true; timer.Enabled = false; timer.Elapsed += Timer_Elapsed; dialer = new RasDialer(); dialer.Credentials = null; dialer.EapOptions = new RasEapOptions(false, false, false); dialer.HangUpPollingInterval = 0; dialer.Options = new RasDialOptions(false, false, false, false, false, false, false, false, false, false); //dialer.SynchronizingObject = synchronizingObject; dialer.EntryName = entryName; dialer.PhoneBookPath = phoneBookPath; dialer.StateChanged += Dialer_StateChanged; ; dialer.DialCompleted += Dialer_DialCompleted; } /// <summary> /// Ras连接 /// </summary> /// <param name="phoneBookType">电话簿类型</param> /// <param name="entryName">实体名称</param> /// <param name="synchronizingObject">同步对象</param> public RasDialConnection(RasPhoneBookType phoneBookType, string entryName, ISynchronizeInvoke synchronizingObject) : this(RasPhoneBook.GetPhoneBookPath(phoneBookType), entryName, synchronizingObject) { } /// <summary> /// Ras连接 /// 使用全局用户电话簿 /// </summary> /// <param name="entryName">实体名称</param> /// <param name="synchronizingObject"></param> public RasDialConnection(string entryName, ISynchronizeInvoke synchronizingObject) : this(RasPhoneBookType.AllUsers, entryName, synchronizingObject) { } /// <summary> /// Ras连接 /// </summary> /// <param name="phoneBookPath">电话簿路径</param> /// <param name="entryName">实体名称</param> public RasDialConnection(string phoneBookPath, string entryName) : this(phoneBookPath, entryName, null) { } /// <summary> /// Ras连接 /// </summary> /// <param name="phoneBookType">电话簿类型</param> /// <param name="entryName">实体名称</param> public RasDialConnection(RasPhoneBookType phoneBookType, string entryName) : this(phoneBookType, entryName, null) { } /// <summary> /// Ras连接 /// 使用全局用户电话簿 /// </summary> /// <param name="entryName">实体名称</param> public RasDialConnection(string entryName) : this(entryName, (ISynchronizeInvoke)null) { } protected void RaiseEvent<TEventArgs>(EventHandler<TEventArgs> method, TEventArgs e) where TEventArgs : EventArgs { if (method != null && this.CanRaiseEvents) { lock (method) { if (SynchronizingObject != null && SynchronizingObject.InvokeRequired) { SynchronizingObject.Invoke(method, new object[] { this, e }); } else { method(this, e); } } } } private void Timer_Elapsed(object sender, ElapsedEventArgs e) { timer.Enabled = false; if (State == RasConnectionState.Disconnected) { connection = null; rasHandle = null; IPAddress = null; ServerIPAddress = null; state = RasConnectionState.Disconnected; RaiseEvent(StateChanged, new StateChangedEventArgs(RasConnectionState.Disconnected)); } else timer.Enabled = true; } private void Dialer_DialCompleted(object sender, DialCompletedEventArgs e) { var connections = RasConnection.GetActiveConnections(); if (e.Connected && connections.Any(c => c.Handle == rasHandle)) { connection = connections.First(c => c.Handle == rasHandle); RasIPInfo ipAddresses = (RasIPInfo)connection.GetProjectionInfo(RasProjectionType.IP); IPAddress = ipAddresses.IPAddress; ServerIPAddress = ipAddresses.ServerIPAddress; timer.Enabled = true; } RaiseEvent(DialCompleted, e); } private void Dialer_StateChanged(object sender, DotRas.StateChangedEventArgs e) { state = e.State; RaiseEvent(StateChanged, new StateChangedEventArgs(e.ErrorCode, e.ErrorMessage, e.ExtendedErrorCode, e.State)); } /// <summary> /// 拨号 /// </summary> /// <param name="userName">用户名</param> /// <param name="password">密码</param> public void Dial(string userName, string password) { state = RasConnectionState.Disconnected; dialer.Credentials = new NetworkCredential(userName, password); rasHandle = dialer.DialAsync(); } /// <summary> /// 拨号 /// 使用已保存的验证信息 /// </summary> public void Dial() { RasPhoneBook usersPhoneBook = new RasPhoneBook(); usersPhoneBook.Open(dialer.PhoneBookPath); var entry = usersPhoneBook.Entries[dialer.EntryName]; var credentials = entry.GetCredentials(); state = RasConnectionState.Disconnected; if (credentials != null) dialer.Credentials = credentials; rasHandle = dialer.DialAsync(); } /// <summary> /// 断开连接 /// </summary> public void Disconnect() { if (State == RasConnectionState.Disconnected) return; try { timer.Enabled = false; if (dialer.IsBusy) { dialer.DialAsyncCancel(); } else { if (connection != null) connection.HangUp(); RaiseEvent(StateChanged, new StateChangedEventArgs(RasConnectionState.Disconnected)); } } finally { connection = null; rasHandle = null; IPAddress = null; ServerIPAddress = null; state = RasConnectionState.Disconnected; } } } }
连接状态改变事件参数类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DotRas; namespace RBTI.Toolbox.RasConnect.Ras { class StateChangedEventArgs : EventArgs { public RasConnectionState State { get; private set; } public int ErrorCode { get; private set; } public string ErrorMessage { get; private set; } public int ExtendedErrorCode { get; private set; } public StateChangedEventArgs(RasConnectionState state) { State = state; } public StateChangedEventArgs(int errorCode, string errorMessage, int extendedErrorCode, RasConnectionState state) { ErrorCode = ErrorCode = errorCode; ErrorMessage = errorMessage; ExtendedErrorCode = extendedErrorCode; State = state; } } }
以上的代码童鞋们能看懂就直接用,看不懂就到下面下个Demo程序源码看看吧
DotRas V1.3 库源码:
本文出自 小古Blog,转载时请注明出处及相应链接。
本文永久链接: http://blog.chdz1.com/?post=253
2条评论
win7 专业版 sp1,dotnet 4.0.30319,没有安装任何补丁。
运行 demon 内已经编译的程序出错。
See the end of this message for details on invoking
just-in-time (JIT) debugging instead of this dialog box.
************** Exception Text **************
System.IO.FileLoadException: Could not load file or assembly 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)
File name: 'System.Core, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, Retargetable=Yes'
at RBTI.Toolbox.RasConnect.FormStarting.FormMain_Load(Object sender, EventArgs e)
at System.Windows.Forms.Form.OnLoad(EventArgs e)
at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
at System.Windows.Forms.Control.CreateControl()
at System.Windows.Forms.Control.WmShowWindow(Message& m)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.Form.WmShowWindow(Message& m)
at System.Windows.Forms.Form.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
************** Loaded Assemblies **************
mscorlib
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 (RTMRel.030319-0100)
CodeBase: file:///C:/Windows/Microsoft.NET/Framework64/v4.0.30319/mscorlib.dll
----------------------------------------
RasConnect
Assembly Version: 1.0.0.19100
Win32 Version: 1.0.0.0
CodeBase: file:///C:/Users/Administrator/Desktop/Debug/RasConnect.exe
----------------------------------------
System.Windows.Forms
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Windows.Forms/v4.0_4.0.0.0__b77a5c561934e089/System.Windows.Forms.dll
----------------------------------------
System
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System/v4.0_4.0.0.0__b77a5c561934e089/System.dll
----------------------------------------
System.Drawing
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/System.Drawing/v4.0_4.0.0.0__b03f5f7f11d50a3a/System.Drawing.dll
----------------------------------------
Accessibility
Assembly Version: 4.0.0.0
Win32 Version: 4.0.30319.1 built by: RTMRel
CodeBase: file:///C:/Windows/Microsoft.Net/assembly/GAC_MSIL/Accessibility/v4.0_4.0.0.0__b03f5f7f11d50a3a/Accessibility.dll
----------------------------------------
System.Threading.Tasks
Assembly Version: 2.6.8.0
Win32 Version: 2.6.8.0
CodeBase: file:///C:/Users/Administrator/Desktop/Debug/System.Threading.Tasks.DLL
----------------------------------------
Microsoft.Threading.Tasks
Assembly Version: 1.0.12.0
Win32 Version: 1.0.168.0
CodeBase: file:///C:/Users/Administrator/Desktop/Debug/Microsoft.Threading.Tasks.DLL
----------------------------------------
************** JIT Debugging **************
To enable just-in-time (JIT) debugging, the .config file for this
application or computer (machine.config) must have the
jitDebugging value set in the system.windows.forms section.
The application must also be compiled with debugging
enabled.
For example:
<configuration>
<system.windows.forms jitDebugging="true" />
</configuration>
When JIT debugging is enabled, any unhandled exception
will be sent to the JIT debugger registered on the computer
rather than be handled by this dialog box.
好复杂的说。。。。