查看文章 |
VB写WebBrowser捕捉信息(3)
2008-06-16 10:54
第三部分 自动拨号、自动挂断以及自动处理中途掉线一个出色的“自动上网机器人”程序应能按照既定的时间准时开始拨号、并当所需任务已完成后立即挂断。而且仅做到这些还不够,它还应在发出拨号指令后跟踪拨 号操作是否真的成功、上网速度如何、是否需要挂断后重新拨号、自动浏览过程中是否出现掉线、以及最终的挂断操作是否真的成功完成,等等。 因此,“机器人”程序应定时检查在线状况,以保证浏览时一定在在线状态、浏览完毕后一定不在在线状态。同时还要检查浏览进度,当浏览速度过慢时尝试挂断后重新拨号。 本部分讨论了实现“自动拨号”、“检查在线状况”、以及“自动挂断”这三个功能的若干方法,比较了诸方法各自的优劣,并总结给出了使用建议。本部分的示例程序将这三个功能的诸方法集成在一起,以便于大家对比使用(见下图)。
1. 自动拨号方法1A:使用rnaui.dll rnaui.dll是微软的“拨号网络用户接口”程序集,一般在“\Windows\System”目录下。其中的RnaDial程序用于启动拨号。该程序可在命令行执行(在“开始”->“运行”中键入): rundll32.exe rnaui.dll,RnaDial <拨号网络连接名> 其中的“RnaDial”和“<拨号网络连接名>”是区分大小写的。 但由于上述命令仅启动拨号窗口而未立即开始拨号,因此在程序中使用时还应再 送出模拟“回车”的按键:
方法1B:使用wininet.dll wininet.dll是微软的Internet扩充函数集,一般在“\Windows\System”目录下。其中的 InternetAutodial、InternetAutodialHangup和InternetGetConnectedState三个函数分别可 完成自动拨号、自动挂断和判断在线状态等任务。InternetAutodial的定义为:
若将第一个参数(dwFlags)的值设为2,该函数无需用户干预就可自动拨号。但使用该函数有一个前提:即必须将“Internet 属性”->“连接”设成“始终拨打默认连接”(见下图)。
用InternetAutodial函数自动拨号的情况可参见下图。从图中可以看出,该方法可自动重试多次。具体的重试次数在默认连接的“设置”->“高级”中定义:
方法1C:使用RAS RAS 是微软的远程访问服务(Remote Access Service)API集合。其中的 API函数RasDial可完成拨号任务。但由于该函数在使用上略显复杂而不太常用,故示例程序中未采纳。 自动拨号方法小结:rnaui方法使用起来最简单,又由于它不一定非要使用默认连接,因此也最灵活。但这种灵活恰恰又给它带来了弱点,即如果不提供连接 名,该方法不会自动调用默认连接。此外,这种方法还有两个最大的缺点:一是仅拨号一次,若出现占线或没有响应等情况时不会自动重试;二是调用程序不容易得 到拨号是否成功的返回值。相比之下,wininet方法虽仅能拨打默认连接(无默认连接时,使用第一个连接),但它可多次试拨,并且 InternetAutodial函数等待拨号成功或所有试拨结束以便给调用程序返回拨号是否成功的值,因此,在“自动上网机器人”的环境中 wininet方法是最适宜的。 2. 检查在线状况方法2A:wininet方法 若InternetGetConnectedState函数返回True,则为在线状态。该方法最大的缺点是:若当前连接不是用wininet方法建立的,则返回值可能不准确。 方法2B:查找窗口法 拨号连接成功后,下图所示的窗口一定存在(不管它是最小化在任务栏的最右端,或是开启为下图所示的状态):
用FindWindow API函数找到该窗口即意味着当前在线。此外,查找窗口法的另一个用处是查找“重新连接”窗口:当中途掉线时,操作系统往往会询问你是否重新连接,找到该窗口并发出模拟“回车”按键即可实现再拨号。 查找窗口法的缺点是:由于找窗口时需要提供窗口标题,因此即使使用的是默认连接也必须事先知道默认连接名。 方法2C:RAS 方法 先用RasEnumConnections函数返回整个RAS集合,再用RasGetConnectStatus函数判断第一个 RAS连接的状态。RAS方法的最大优点是:不管当前连接是否是用wininet建立的,RAS 方法均可对在线状态做出正确判断。 方法2D:注册表法 在线时,注册表的“\HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services \RemoteAccess”处有键值“Remote Connection”,且其值不为零;不在线时,该处无“Remote Connection”键值(当本次系统启动后从未拨号成功时),或者其值为零(表明曾拨号成功,但现在已断掉)。 检查在线状况之方法小结:由于wininet方法的局限性,一般我们应避免使用之;查找窗口法是可靠的,只是要知道连接名;因此我们推荐使用RAS 方法和注册表法。 3. 自动挂断方法3A:wininet法 使用InternetAutodialHangup函数。同样地,若当前连接不是用wininet方法建立的,则返回值可能不准确(即不能成功挂断)。 方法3B:窗口查找法 找到图九所示的窗口,然后用ShowWindow API函数使之成为当前窗口,最后发出模拟<Alt>+C的按键操作(从图九中可以看出,<Alt>+C是“断开连接”按键的快捷方式)。 方法3C:RAS 法 用RasHangUp函数执行挂断。不管用何种方法建立的连接,RAS 法均能可靠地完成任务。 自动挂断方法小结:相比之下,窗口查找法和RAS 法是可以信赖的。 4. 本部分总结综上所述,对于“自动拨号”、“检查在线状况”、以及“自动挂断”的各种方法,我们推荐“1A-2C-3C” 组合。当然各方法可综合使用(如加入2D、3B等),以确保万无一失。在具体编程时还应注意:拨号后判断结果,如不成功应重新拨号;任务进行过程中定时检 查在线状态,出现掉线后应及时处理;最后的挂断操作后应再查在线状态,以确保挂断成功。 下面是实例程序的完整代码。源代码中的全局定义已按照wininet、RAS、注册表等进行分类,各具体方法也均按序排列,以便于大家挑选使用。该程序的执行情况在本部分的开始处已给出(图六)。 ' 程序三:自动拨号、自动挂断以及自动处理中途掉线' Option Explicit ' 有关 wininet 的全局定义 Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED = 2 Private Const INTERNET_CONNECTION_MODEM = 1 Private Declare Function InternetAutodial Lib "wininet.dll" _ (ByVal dwFlags As Long, ByVal dwReserved As Long) As Long Private Declare Function InternetAutodialHangup Lib _ "wininet.dll" (ByVal dwReserved As Long) As Long Private Declare Function InternetGetConnectedState Lib _ "wininet.dll" (ByRef lpdwFlags As Long, ByVal _ dwReserved As Long) As Long ' 有关“窗口查找”的全局定义 Private Declare Function FindWindow Lib "user32" _ Alias "FindWindowA" (ByVal lpClassName As String, _ ByVal lpWindowName As String) As Long Private Declare Function ShowWindow Lib "user32" _ (ByVal hwnd As Long, ByVal nCmdShow As Long) As Long Private Const SW_SHOW = 5 ' 有关 RAS 的全局定义 Private Const RASCS_DONE = &H2000& Private Const RAS_MaxEntryName = 256 Private Const RAS_MaxDeviceType = 16 Private Const RAS_MaxDeviceName = 128 Private Type RASCONN dwSize As Long hRasConn As Long szEntryName(RAS_MaxEntryName) As Byte szDeviceType(RAS_MaxDeviceType) As Byte szDeviceName(RAS_MaxDeviceName) As Byte End Type Private Type RASCONNSTATUS dwSize As Long RasConnState As Long dwError As Long szDeviceType(RAS_MaxDeviceType) As Byte szDeviceName(RAS_MaxDeviceName) As Byte End Type Private Ras_Buf(255) As RASCONN Private Ras_Status As RASCONNSTATUS Private lpcb As Long Private lpcConnections As Long Private Declare Function RasEnumConnections Lib _ "rasapi32.dll" Alias "RasEnumConnectionsA" (lprasconn _ As Any, lpcb As Long, lpcConnections As Long) As Long Private Declare Function RasGetConnectStatus Lib _ "rasapi32.dll" Alias "RasGetConnectStatusA" (ByVal _ hRasConn As Long, lpRASCONNSTATUS As Any) As Long Private Declare Function RasHangUp Lib "rasapi32.dll" _ Alias "RasHangUpA" (ByVal hRasConn As Long) As Long ' 有关“注册表”的全局定义 Private Const HKEY_LOCAL_MACHINE = &H80000002 Private Declare Function RegOpenKey Lib "advapi32.dll" Alias _ "RegOpenKeyA" (ByVal hKey As Long, ByVal lpSubKey As _ String, phkResult As Long) As Long Private Declare Function RegQueryValueEx Lib "advapi32.dll" _ Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal _ lpValueName As String, ByVal lpReserved As Long, lpType _ As Long, lpData As Any, lpcbData As Long) As Long Private Declare Function RegCloseKey Lib "advapi32.dll" _ (ByVal hKey As Long) As Long Dim ret As Long '自动拨号 Private Sub wininet拨号测试_Click() If InternetAutodial(INTERNET_AUTODIAL_FORCE_UNATTENDED, 0) _ Then MsgBox "已连接(wininet法)" End Sub Private Sub rnaui拨号测试_Click() ret = Shell("rundll32.exe rnaui.dll,RnaDial " + Text1, 1): DoEvents SendKeys "{enter}", True: DoEvents End Sub '检查是否断线 Private Sub wininet方法_Click() ' wininet法检查是否断线 If InternetGetConnectedState(INTERNET_CONNECTION_MODEM, 0) Then MsgBox "在线." Else MsgBox "当前未连接。" End If End Sub Private Sub 查找窗口法_Click() ' 查找窗口法检查是否断线 ret = FindWindow("#32770", "重新连接") If ret <> 0 Then Call ShowWindow(ret, SW_SHOW) SendKeys "{enter}", True: Exit Sub End If ret = FindWindow("#32770", "连接到 The95963") If ret <> 0 Then MsgBox "在线." Else MsgBox "当前未连接。" End If End Sub Private Sub RAS方法_Click() ' RAS方法检查是否断线 Ras_Buf(0).dwSize = Len(Ras_Buf(0)) + 1 lpcb = 256 * Ras_Buf(0).dwSize ret = RasEnumConnections(Ras_Buf(0), lpcb, lpcConnections) If ret Then MsgBox "出错!": Exit Sub End If Ras_Status.dwSize = Len(Ras_Status) + 2 ret = RasGetConnectStatus(Ras_Buf(0).hRasConn, Ras_Status) If ret = 0 And Ras_Status.RasConnState = RASCS_DONE Then MsgBox "在线." Else MsgBox "当前未连接。" End If End Sub Private Sub 注册表法_Click() ' 注册表法检查是否断线 Dim SubKey As String, ValueName As String Dim Data As Long, Result As Long SubKey = "System\CurrentControlSet\Services\RemoteAccess" ret = RegOpenKey(HKEY_LOCAL_MACHINE, SubKey, Result) If ret = 0& Then ValueName = "Remote Connection" ret = RegQueryValueEx(Result, ValueName, 0&, 0&, ByVal Data, 0&) ret = RegQueryValueEx(Result, ValueName, 0&, 0&, Data, Len(Data)) If ret = 0& And Data <> 0 Then MsgBox "在线!" Else MsgBox "当前未连接。" End If RegCloseKey (Result) End If End Sub '自动挂断 Private Sub wininet法_Click() ' wininet法自动挂断 If InternetAutodialHangup(0) Then MsgBox "已挂断(wininet法)" End Sub Private Sub 窗口查找法_Click() ' 窗口查找法自动挂断 ret = FindWindow("#32770", "连接到 The95963") If ret <> 0 Then Call ShowWindow(ret, SW_SHOW) SendKeys "%c", True MsgBox "已挂断(窗口查找法)" End If End Sub Private Sub RAS法_Click() ' RAS法自动挂断 Ras_Buf(0).dwSize = Len(Ras_Buf(0)) + 1 lpcb = 256 * Ras_Buf(0).dwSize ret = RasEnumConnections(Ras_Buf(0), lpcb, lpcConnections) If ret Then MsgBox "出错!": Exit Sub End If Ras_Status.dwSize = Len(Ras_Status) + 2 ret = RasGetConnectStatus(Ras_Buf(0).hRasConn, Ras_Status) If ret = 0 And Ras_Status.RasConnState = RASCS_DONE Then If RasHangUp(Ras_Buf(0).hRasConn) = 0 Then _ MsgBox "已挂断(RAS法)" End If End Sub |



