【问题标题】:The WebBrowser control won't work with Proxies from a text fileWebBrowser 控件不适用于文本文件中的代理
【发布时间】:2018-12-30 10:02:01
【问题描述】:

我对 Visual Basic 不太了解,对此我深表歉意,但我对 Java 和其他一些编程语言有基本的了解,所以我并不完全是新手。
我想制作一个程序,该程序使用从文本文件加载的代理连接到网站,但是当我调试它时,它给了我一堆不同的错误消息,例如:

  • HTTP 错误 503。服务不可用
  • 找不到网页

等等

这是我将代理加载到列表框中的代码:

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    OpenFileDialog1.ShowDialog()
    streamer = IO.File.OpenText(OpenFileDialog1.FileName)
    Dim mystring() As String = streamer.ReadToEnd.Split(vbNewLine)

    ListBox1.Items.AddRange(mystring)
 End Sub

这是使用代理的代码:

Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
    Button2.Enabled = False
    Button1.Enabled = False

    For Each item In ListBox1.Items
        UseProxy(item.ToString)
        Label4.Text = item
        WebBrowser1.Navigate(TextBox3.Text)
        pause(10000)
    Next
End Sub

这是使用代理:

Private Sub UseProxy(ByVal strProxy As String)
    Const INTERNET_OPTION_PROXY As Integer = 38
    Const INTERNET_OPEN_TYPE_PROXY As Integer = 3

    Dim struct_IPI As Struct_INTERNET_PROXY_INFO
    struct_IPI.dwAccessType = INTERNET_OPEN_TYPE_PROXY
    struct_IPI.proxy = Marshal.StringToHGlobalAnsi(strProxy)
    struct_IPI.proxyBypass = Marshal.StringToHGlobalAnsi("local")

    Dim intptrStruct As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(struct_IPI))

    Marshal.StructureToPtr(struct_IPI, intptrStruct, True)
    Dim iReturn As Boolean = InternetSetOption(IntPtr.Zero, INTERNET_OPTION_PROXY, intptrStruct, System.Runtime.InteropServices.Marshal.SizeOf(struct_IPI))
End Sub

我在按钮 2 click sub 的末尾也收到错误提示

实际上有时当我关闭程序时,我在按钮结束时在 Visual Studio 中出现错误2 click sub (Next)

System.InvalidOperationException: '此枚举器绑定的列表已被修改。只有在列表不变的情况下才能使用枚举器。'

【问题讨论】:

  • 您需要使用InternetSetOption 来传播与代理设置相关的注册表值已更改的(系统)消息。使用Registry Class 修改这些注册表设置。 (ProxyServer [string] = "Proxy:Port") 和 ProxyEnable [dword]
  • 您能详细说明一下吗?对不起,我没有写 useproxy 代码。
  • 您需要设置这些注册表值,然后InternetSetOption 必须生成一个“广播”以告知这些设置已更改,并生成一个“建议”,感兴趣的应用程序应该刷新他们对此事的了解.例如将INTERNET_OPTION_SETTINGS_CHANGED = 39 消息和INTERNET_OPTION_REFRESH = 37 消息发送到hInternet = IntPtr.Zero。如果您需要示例,请告诉我。
  • 是的,我想要一个例子,对不起

标签: vb.net winforms winapi webbrowser-control proxies


【解决方案1】:

我认为这是在系统范围内更改系统代理设置的最直接的方法。
有时,每个连接设置更可取。请参阅本案例中的链接文档。

使用Registry class 在 Internet 设置子项中设置所需的值:
Software\Microsoft\Windows\CurrentVersion\Internet Settings

然后,使用InternetSetOptionINTERNET_OPTION_SETTINGS_CHANGEDINTERNET_OPTION_REFRESH Flags 执行更改的系统通知:
(这些是标志,但在这种情况下不应组合使用)
如果使用 INTERNET_OPTION_PER_CONNECTION_OPTION 修改每个连接的设置,还必须调用INTERNET_OPTION_REFRESH

InternetSetOption 的声明和一个辅助函数。

Imports Microsoft.Win32
Imports System.Runtime.InteropServices
Imports System.Security.AccessControl

<SecurityCritical>
<DllImport("wininet.dll", SetLastError:=True, CharSet:=CharSet.Auto)>
Friend Shared Function InternetSetOption(hInternet As IntPtr, dwOption As Integer, lpBuffer As IntPtr, dwBufferLength As Integer) As Boolean
End Function
Friend Const INTERNET_OPTION_SETTINGS_CHANGED As Integer = 39
Friend Const INTERNET_OPTION_REFRESH As Integer = 37

<SecuritySafeCritical>
Private Sub InternetSetProxy(ProxyAddress As String, ProxyPort As Integer, UseProxy As Boolean)
    Dim keyName As String = "Software\Microsoft\Windows\CurrentVersion\Internet Settings"
    Dim KeyValue As Object = ProxyAddress + ":" + ProxyPort.ToString()

    Dim InternetSettingsKey As RegistryKey =
                Registry.CurrentUser.OpenSubKey(keyName,
                RegistryKeyPermissionCheck.ReadWriteSubTree,
                RegistryRights.WriteKey)

    InternetSettingsKey.SetValue("ProxyServer", KeyValue, RegistryValueKind.String)
    InternetSettingsKey.SetValue("ProxyEnable", If(UseProxy, 1, 0), RegistryValueKind.DWord)
    InternetSettingsKey.Close()

    InternetSetOption(IntPtr.Zero, INTERNET_OPTION_SETTINGS_CHANGED, IntPtr.Zero, 0)
    InternetSetOption(IntPtr.Zero, INTERNET_OPTION_REFRESH, IntPtr.Zero, 0)

    InternetSettingsKey.Dispose()
End Sub

您可以使用 Internet Explorer 对其进行测试。
如果您使用Fiddler,请使用这些参数调用辅助方法以将其用作默认系统代理:

InternetSetProxy("127.0.0.1", 8888, True)

要禁用代理设置,只需将UseProxy 参数设置为False

InternetSetProxy("127.0.0.1", 8888, False)

照常浏览互联网地址:

WebBrowser1.Navigate("https://www.google.com")

关于 .Net WebBrowser 控件的说明。
WebBrowser 控件使用当前缓存(在 Internet Explorer 设置中指定)以预定义的行为加载访问过的资源。
如果您启用了代理但您仍然可以看到网站的(首页)页面,这取决于此默认行为。
如果您尝试单击某个链接,则无法访问此新链接,并且消息:

代理服务器没有响应

将展示。
这种结果不是一成不变的,取决于缓存设置。

编辑:
一种让WebBrowser 控制Navigate 一个或多个Internet 地址的方法,对每个地址使用不同的代理服务器:

WebBrowser DocumentCompleted 事件添加一个事件处理程序。
当第一次调用WebBrowser.Navigate() 方法时,事件处理程序将循环遍历代理列表(从ListBox 控件Items 列表中读取)。

此代码假定代理列表由以下字符串组成 包括每个代理的地址和端口。 (例如。 "127.0.0.1:8888" 提琴手)。

这里,和 OP 示例一样,所有代理的 Internet 地址都是相同的(WaitDocumentsComplete() 方法的参数),但当然可以从另一个列表中读取。

DocumentCompleted Handler 和辅助方法NavigateNextProxy() 都被标记为异步,因此整个过程中 UI 不会被阻塞。

可以使用WebBrowser.Stop() 方法或将ProxyIndex 字段设置为负值随时停止此过程。
如果进程在其自然完成之前被中断,则有必要显式删除处理程序,使用:

RemoveHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler

Event Handler 声明以及 Wrapper 和 Helper 函数:

Protected Friend DocumentCompletedHandler As WebBrowserDocumentCompletedEventHandler

Private Sub WaitDocumentsComplete(ByVal Address As String, ByVal TimeOutSeconds As Integer)
    DocumentCompletedHandler =
        Async Sub(s, e)
            If WebBrowser1.ReadyState = WebBrowserReadyState.Complete AndAlso ProxyIndex > -1 Then
                If (Not WebBrowser1.IsBusy) Then
                    Dim ProxyParts() As String = ListBox1.GetItemText(ListBox1.Items(ProxyIndex)).Split(":"c)
                    Await NavigateNextProxy(Address, ProxyParts(0), CInt(ProxyParts(1)), TimeOutSeconds)
                    ProxyIndex += 1
                    If ProxyIndex > ProxyIndexMaxValue Then
                        ProxyIndex = -1
                        RemoveHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler
                    End If
                End If
            End If
        End Sub
    AddHandler WebBrowser1.DocumentCompleted, DocumentCompletedHandler
End Sub

Protected Async Function NavigateNextProxy(Address As String, ByVal ProxyAddr As String, ProxyPort As Integer, TimeOutSeconds As Integer) As Task
    InternetSetProxy(ProxyAddr, ProxyPort, True)
    Await Task.Delay(TimeSpan.FromSeconds(TimeOutSeconds))
    If WebBrowser1.IsBusy Then WebBrowser1.Stop()
    WebBrowser1.Navigate(Address)
End Function

Button.Click()事件,设置List中第一个和最后一个Proxy的索引,每个navigation之间的超时值(以秒为单位),并调用包装方法WaitDocumentsComplete(),这将初始化@ 987654355@ 事件处理程序。

要开始该过程,请使用WebBrowser1.Navigate("") 初始化WebBrowser 控件

抑制WebBrowser 脚本错误对话框也很重要, 否则会影响整个过程。

Private ProxyIndex As Integer = -1
Private ProxyIndexMaxValue As Integer = 0

Private Sub btnForm2_Click(sender As Object, e As EventArgs) Handles btnForm2.Click
    ProxyIndex = 0
    ProxyIndexMaxValue = ListBox1.Items.Count - 1
    Dim BrowsingTimeoutSeconds As Integer = 3
    Dim Address As String = "https://www.google.com"
    WaitDocumentsComplete(Address, BrowsingTimeoutSeconds)
    WebBrowser1.ScriptErrorsSuppressed = True
    WebBrowser1.Navigate("")
End Sub

【讨论】:

  • 不错的答案!虽然为什么Application.DoEvents() 打电话?
  • @Visual Vincent 好吧,如果您使用的是WebBrowser 控件,那么您使用的是具有消息队列的表单。我们可能也必须处理该消息。我们引起了广播,我们不尽力列出来吗?太粗鲁了:)
  • 当然,但是消息泵/队列会自行处理。你不应该强迫它。这是一个While 循环,始终检查消息。
  • @Visual Vincent 是的,但它可能会变得孤立。显然,OP 需要执行多个连接,更改当前的代理地址。由于他最终可能会在闭环中这样做,因此我尝试暂停。如果您认为在这种情况下没有用,我可以将其删除。但我认为它可能会起作用(在这里)。
  • 有趣且完全有效的假设。我不知道,因为我没有尝试过,也不知道实现的具体细节(我以前从未尝试过使用代理)。我只是对电话感到好奇。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-30
  • 2017-02-15
  • 1970-01-01
相关资源
最近更新 更多