Imports System.IO
Imports System.Threading
Imports System.Threading.Tasks
Imports System.Diagnostics
Imports System.Globalization
Public Class Form1
Dim speedOptionsint As Integer = 5
Private cts As CancellationTokenSource
Private Sub BTCheckProxyStart_Click(sender As Object, e As EventArgs) Handles BTCheckProxyStart.Click
' Absicherung: Prüfen, ob Kontrollen existieren und Text vorhanden ist
If PLL Is Nothing OrElse PLLChecked Is Nothing OrElse PLLSpeedChecked Is Nothing OrElse PLLConsole Is Nothing Then
MessageBox.Show("Steuerelemente (PLL, PLLChecked, PLLSpeedChecked, PLLConsole) wurden nicht gefunden.", "Initialisierungsfehler", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End If
If String.IsNullOrWhiteSpace(PLL.Text) Then
AppendConsole("[FEHLER] Keine Proxies in PLL eingetragen.")
Return
End If
' UI vorbereiten
Try
PLLChecked.Items.Clear()
PLLSpeedChecked.Items.Clear()
PLLConsole.Clear()
BTCheckProxyStart.Enabled = False
BTCheckProxyStop.Enabled = True
Catch ex As Exception
MessageBox.Show($"Fehler beim Zurücksetzen der Benutzeroberfläche: {ex.Message}", "UI Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error)
Return
End Try
' Dateipfade definieren mit robuster Exception-Behandlung für Berechtigungen
Dim scriptDir As String
Try
scriptDir = AppDomain.CurrentDomain.BaseDirectory
Catch ex As Exception
scriptDir = Path.GetTempPath()
End Try
Dim workingFile As String = Path.Combine(scriptDir, "Working.txt")
Dim deadFile As String = Path.Combine(scriptDir, "Dead.txt")
Dim fastFile As String = Path.Combine(scriptDir, "Fast.txt")
' Alte Dateien vor Start sicher bereinigen
Try
If File.Exists(workingFile) Then File.Delete(workingFile)
If File.Exists(deadFile) Then File.Delete(deadFile)
If File.Exists(fastFile) Then File.Delete(fastFile)
Catch ex As Exception
AppendConsole($"[WARNUNG] Konnte alte Textdateien nicht löschen: {ex.Message}")
End Try
' Lock-Objekte für thread-sicheres Schreiben in Textdateien
Dim fileLock As New Object()
' Proxies zeilenweise einlesen und bereinigen
Dim proxies As List(Of String)
Try
proxies = PLL.Lines.
Where(Function(l) Not String.IsNullOrWhiteSpace(l)).
Select(Function(l) l.Trim()).
Distinct().
ToList()
Catch ex As Exception
AppendConsole($"[FEHLER] Konnte Proxy-Liste nicht einlesen: {ex.Message}")
BTCheckProxyStart.Enabled = True
BTCheckProxyStop.Enabled = False
Return
End Try
If proxies.Count = 0 Then
AppendConsole("[FEHLER] Nach Bereinigung sind keine gültigen Proxies übrig.")
BTCheckProxyStart.Enabled = True
BTCheckProxyStop.Enabled = False
Return
End If
AppendConsole($"Starte Test für {proxies.Count} Proxies mit max. 50 Threads...")
' CancellationToken für den Stop-Button initialisieren
cts = New CancellationTokenSource()
Dim token = cts.Token
' Hintergrund-Task starten, damit die GUI flüssig bleibt
Task.Run(Sub()
Dim parallelOptions As New ParallelOptions() With {
.MaxDegreeOfParallelism = 50,
.CancellationToken = token
}
Try
Parallel.ForEach(proxies, parallelOptions, Sub(proxy)
' Abfrage auf Abbruch prüfen
token.ThrowIfCancellationRequested()
Dim sw = Stopwatch.StartNew()
Dim isSuccess As Boolean = False
Dim latency As Long = 0
Try
Dim psi As New ProcessStartInfo()
psi.FileName = "curl.exe"
psi.Arguments = "--proxy """ & proxy & """ --connect-timeout 5 --max-time 8 -s

-o NUL"
psi.CreateNoWindow = True
psi.UseShellExecute = False
Using p As Process = Process.Start(psi)
If p IsNot Nothing Then
p.WaitForExit(9000)
sw.Stop()
If p.HasExited AndAlso p.ExitCode = 0 Then
isSuccess = True
latency = sw.ElapsedMilliseconds
End If
End If
End Using
Catch ex As OperationCanceledException
Throw
Catch ex As Exception
isSuccess = False
End Try
' In Dateien schreiben und UI aktualisieren mit try/catch im Lock
SyncLock fileLock
Try
If isSuccess Then
File.AppendAllText(workingFile, $"{proxy}|{latency}" & Environment.NewLine)
AppendConsole($"[OK ] {proxy} {latency}ms")
AddToListV(proxy, $"{latency} ms")
Else
File.AppendAllText(deadFile, proxy & Environment.NewLine)
AppendConsole($"[BAD] {proxy}")
AddToListV(proxy, "Dead")
End If
Catch ioEx As IOException
AppendConsole($"[IO-FEHLER] Konnte Ergebnis für {proxy} nicht schreiben: {ioEx.Message}")
Catch writeEx As Exception
AppendConsole($"[FEHLER] Schreiben fehlgeschlagen für {proxy}: {writeEx.Message}")
End Try
End SyncLock
End Sub)
Catch ex As OperationCanceledException
AppendConsole("[INFO] Der Test wurde vom Benutzer abgebrochen.")
Catch aggEx As AggregateException
For Each innerEx In aggEx.InnerExceptions
If TypeOf innerEx Is OperationCanceledException Then
AppendConsole("[INFO] Der Test wurde vom Benutzer abgebrochen.")
Else
AppendConsole($"[PARALLEL-FEHLER] {innerEx.Message}")
End If
Next
Catch ex As Exception
AppendConsole($"[KRITISCHER FEHLER] {ex.Message}")
End Try
' Fast.txt generieren mit robuster Fehlerbehandlung und Liste für den Speed-Check sichern
Dim sortedProxiesList As New List(Of String)()
Try
If File.Exists(workingFile) Then
Dim workingLines = File.ReadAllLines(workingFile)
Dim sortedProxies = workingLines.
Where(Function(line) Not String.IsNullOrWhiteSpace(line) AndAlso line.Contains("|")).
Select(Function(line)
Try
Dim parts = line.Split("|"c)
If parts.Length >= 2 Then
Dim parsedMs As Integer
If Integer.TryParse(parts(1), parsedMs) Then
Return New With {.Valid = True, .Proxy = parts(0), .Ms = parsedMs}
End If
End If
Catch
End Try
Return New With {.Valid = False, .Proxy = "", .Ms = 0}
End Function).
Where(Function(x) x.Valid).
OrderBy(Function(x) x.Ms).
ToList()
sortedProxiesList = sortedProxies.Select(Function(x) x.Proxy).ToList()
Dim fastFileLines = sortedProxies.Select(Function(x) $"{x.Proxy} [{x.Ms} ms]").ToArray()
File.WriteAllLines(fastFile, fastFileLines)
End If
Catch ex As Exception
AppendConsole($"[FEHLER beim Erstellen der Fast.txt]: {ex.Message}")
End Try
' --- GESCHWINDIGKEITSTEST FÜR PLLSpeedChecked ---
If sortedProxiesList.Count > 0 AndAlso Not token.IsCancellationRequested Then
AppendConsole($"[SPEED] Starte Geschwindigkeitstest für {sortedProxiesList.Count} funktionierende Proxies...")
Dim speedOptions As New ParallelOptions() With {
.MaxDegreeOfParallelism = speedOptionsint,
.CancellationToken = token
}
Try
Parallel.ForEach(sortedProxiesList, speedOptions, Sub(proxy)
token.ThrowIfCancellationRequested()
Dim downloadSpeedStr As String = "Fehler"
Try
Dim psi As New ProcessStartInfo()
psi.FileName = "curl.exe"
psi.Arguments = "--proxy """ & proxy & """ --insecure --connect-timeout 5 --max-time 15 -s -L

-o NUL --write-out ""%{speed_download}"""
psi.CreateNoWindow = True
psi.UseShellExecute = False
psi.RedirectStandardOutput = True
psi.RedirectStandardError = True
Using p As Process = Process.Start(psi)
If p IsNot Nothing Then
Dim output As String = p.StandardOutput.ReadToEnd()
p.WaitForExit(16000)
If p.HasExited AndAlso Not String.IsNullOrWhiteSpace(output) Then
Dim bytesPerSec As Double = 0
output = output.Trim().Replace(".", CultureInfo.InvariantCulture.NumberFormat.NumberDe cimalSeparator)
If Double.TryParse(output, NumberStyles.Any, CultureInfo.InvariantCulture, bytesPerSec) Then
If bytesPerSec > 0 Then
Dim kbps As Double = bytesPerSec / 1024
If kbps > 1024 Then
downloadSpeedStr = $"{Math.Round(kbps / 1024, 2)} MB/s"
Else
downloadSpeedStr = $"{Math.Round(kbps, 2)} KB/s"
End If
End If
End If
End If
End If
End Using
Catch ex As OperationCanceledException
Throw
Catch
downloadSpeedStr = "Timeout/Error"
End Try
AddToListSpeed(proxy, downloadSpeedStr)
End Sub)
Catch ex As OperationCanceledException
AppendConsole("[INFO] Geschwindigkeitstest wurde abgebrochen.")
Catch ex As Exception
AppendConsole($"[SPEED-FEHLER] {ex.Message}")
End Try
End If
' --- PLLSpeedChecked: Filterung (< 1 MB/s entfernen), Sortieren und Fast.txt aktualisieren ---
Try
If Not Me.IsDisposed Then
Me.Invoke(Sub()
If PLLSpeedChecked IsNot Nothing AndAlso PLLSpeedChecked.Items.Count > 0 Then
Dim itemsList = PLLSpeedChecked.Items.Cast(Of ListViewItem)().ToList()
' 1. Alle Proxies mit unter 1 MB/s (oder Fehler/Timeout) herausfiltern
itemsList = itemsList.Where(Function(x)
Dim speedBytes As Double = ParseSpeedToBytes(x.SubItems(1).Text)
Return speedBytes >= (1024 * 1024)
End Function).ToList()
' 2. Übrig gebliebene nach Geschwindigkeit absteigend sortieren
itemsList.Sort(Function(x, y)
Dim speedX As Double = ParseSpeedToBytes(x.SubItems(1).Text)
Dim speedY As Double = ParseSpeedToBytes(y.SubItems(1).Text)
Return speedY.CompareTo(speedX)
End Function)
' 3. ListView aktualisieren
PLLSpeedChecked.BeginUpdate()
PLLSpeedChecked.Items.Clear()
PLLSpeedChecked.Items.AddRange(itemsList.ToArray() )
PLLSpeedChecked.EndUpdate()
' 4. Fast.txt mit den gefilterten und sortierten Speed-Ergebnissen überschreiben
Try
Dim speedFileLines = itemsList.Select(Function(item) $"{item.Text} [{item.SubItems(1).Text}]").ToArray()
File.WriteAllLines(fastFile, speedFileLines)
Catch exWrite As Exception
AppendConsole($"[FEHLER beim Aktualisieren der Fast.txt]: {exWrite.Message}")
End Try
End If
End Sub)
End If
Catch ex As Exception
AppendConsole($"[FEHLER beim Filtern und Sortieren der Speed-Liste]: {ex.Message}")
End Try
' UI-Elemente nach Beendigung wieder sicher aktivieren
Try
If Not Me.IsDisposed Then
Me.Invoke(Sub()
If BTCheckProxyStart IsNot Nothing Then BTCheckProxyStart.Enabled = True
If BTCheckProxyStop IsNot Nothing Then BTCheckProxyStop.Enabled = False
AppendConsole("Fertig.")
End Sub)
End If
Catch
End Try
End Sub, token)
End Sub
Private Sub BTCheckProxyStop_Click(sender As Object, e As EventArgs) Handles BTCheckProxyStop.Click
Try
If cts IsNot Nothing Then
cts.Cancel()
AppendConsole("[INFO] Stopp-Signal gesendet...")
End If
Catch ex As Exception
AppendConsole($"[FEHLER beim Stoppen]: {ex.Message}")
End Try
End Sub
' Thread-sichere Methode für die Konsole mit Null-Prüfung
Private Sub AppendConsole(message As String)
Try
If Me.IsDisposed Then Return
If Me.InvokeRequired Then
Me.Invoke(New Action(Of String)(AddressOf AppendConsole), message)
Else
If PLLConsole IsNot Nothing Then
PLLConsole.AppendText(message & Environment.NewLine)
PLLConsole.ScrollToCaret()
End If
End If
Catch
End Try
End Sub
' Thread-sichere Methode für die ListView (PLLChecked) mit Null-Prüfung
Private Sub AddToListV(proxy As String, status As String)
Try
If Me.IsDisposed Then Return
If Me.InvokeRequired Then
Me.Invoke(Sub() AddToListV(proxy, status))
Else
If PLLChecked IsNot Nothing Then
Dim item As New ListViewItem(proxy)
item.SubItems.Add(status)
PLLChecked.Items.Add(item)
End If
End If
Catch
End Try
End Sub
' Thread-sichere Methode für die Speed-ListView (PLLSpeedChecked) mit Null-Prüfung
Private Sub AddToListSpeed(proxy As String, speed As String)
Try
If Me.IsDisposed Then Return
If Me.InvokeRequired Then
Me.Invoke(Sub() AddToListSpeed(proxy, speed))
Else
If PLLSpeedChecked IsNot Nothing Then
Dim item As New ListViewItem(proxy)
item.SubItems.Add(speed)
PLLSpeedChecked.Items.Add(item)
End If
End If
Catch
End Try
End Sub
' Hilfsfunktion zum Parsen der formatierten Speed-Strings in Bytes für die Sortierung
Private Function ParseSpeedToBytes(speedStr As String) As Double
Try
If String.IsNullOrWhiteSpace(speedStr) OrElse speedStr.Contains("Error") OrElse speedStr.Contains("Timeout") Then
Return -1
End If
Dim parts = speedStr.Trim().Split(" "c)
If parts.Length >= 2 Then
Dim val As Double
If Double.TryParse(parts(0), NumberStyles.Any, CultureInfo.InvariantCulture, val) Then
If parts(1).ToUpper().Contains("MB") Then
Return val * 1024 * 1024
ElseIf parts(1).ToUpper().Contains("KB") Then
Return val * 1024
End If
Return val
End If
End If
Catch
End Try
Return -1
End Function
Private Sub MaxDegreeTB_Scroll(sender As Object, e As EventArgs) Handles MaxDegreeTB.Scroll
speedOptionsint = MaxDegreeTB.Value
End Sub
End Class