Code zu Internet und Netzwerk in .NET

Laden von Dateien aus dem Internet

Zum Laden von Dateien aus dem Internet stehen im .NET Framework mehrere Möglichkeiten zur Verfügung. Eine sehr einfache Lösung findet sich mit der Klasse WebRequest, mittels derer eine Anfrage gestellt und die Antwort ausgewertet werden kann. Damit das nachstehende Beispiel funktioniert, müssen die Namensräume System.IO und System.Net importiert werden:

Public Function LoadTextFile(ByVal Url As String) As String
    Dim Request As WebRequest = WebRequest.Create(Url)
    Dim Response As WebResponse = Request.GetResponse()
    Dim Reader As New StreamReader(Response.GetResponseStream())
    LoadTextFile = Reader.ReadToEnd()
    Reader.Close()
    Response.Close()
    Return LoadTextFile
End Function
Herunterladen einer Datei aus dem Internet mit WebRequest und WebResponse.

Soll der Inhalt der geladenen Datei nicht direkt in der Anwendung weiterverarbeitet werden, sondern in eine Datei geschrieben werden, bietet sich die Verwendung von WebClient.DownloadFile an. Die hier demonstrierten Methoden arbeiten synchron, weshalb der Thread, in dem sie ausgeführt werden, blockiert wird. Deshalb ist es in Fällen, in denen asynchrones Verhalten gewünscht wird, sinnvoll, bei Verwendung von WebRequest die Methoden BeginGetResponse und EndGetResponse zu benutzen oder das Herunterladen in einen eigenen Thread zu verfrachten.

Christoph Schneegans [MVP] hat festgestellt, daß die beschriebene Methode nur bei Ressourcen funktioniert, die UTF-8-codiert sind. Bei anders codierten Ressourcen kann man unter .NET 2.0 das WebRequest-Objekt in den Typ HttpWebRequest umwandeln und über dessen Eigenschaft CharSet den Zeichensatz bestimmen, mit dem die Ressource codiert ist, um diesen anschließend beim Lesen des Antwortstroms anzugeben. Allerdings ist der Wert dieser Eigenschaft bei .NET 1.1 anscheinend immer eine leere Zeichenfolge.

Ermitteln der eigenen IP-Adresse

Folgender Code ermittelt die IP-Adresse des Systems, auf dem die Anwendung, die den Code enthält, ausgeführt wird. Es müssen die Namensräume System.Windows.Forms und System.Net importiert werden, damit der Code kompiliert werden kann:

Console.WriteLine("IP-Adresse(n) des Computers:")
For Each Address As IPAddress In Dns.Resolve(SystemInformation.ComputerName).AddressList
    Console.WriteLine(Address.ToString())
Next Address
Ermitteln der eigenen IP-Adressen.

Wiederverwenden von Internet-Explorer-Fenstern

Manchmal ist es erforderlich, daß die Navigation innerhalb eines oder mehrerer bestehender Fenster des Webbrowsers stattfindet. Eine allgemeine Lösung für alle Webbrowser existiert aufgrund unterschiedlicher Konfigurationsmöglichkeiten der einzelnen Webbrowser nicht. Ist jedoch nur der Internet Explorer im Einsatz, kann zu diesem Zweck die im folgenden Listing angegebene Funktion ReplaceInternetExplorerUrl benutzt werden. Bei Aufruf der Funktion wird wahlweise in einem oder mehreren Fenstern, in denen eine Ressource mit einer bestimmten Adresse angezeigt wird, zu einer anderen Adresse navigiert und die Anzahl der wiederverwendeten Fenster zurückgegeben. Um das Beispiel kompilieren zu können, ist es erforderlich, im Projekt einen Verweis auf Microsoft Internet Controls (Datei SHDocVw.dll) aufzunehmen und den Namensraum SHDocVw zu importieren:


' Requires a reference to "Microsoft Internet Controls" ("SHDocVw.dll").
Public Function ReplaceInternetExplorerUrl( _
    ByVal OldUrl As String, _
    ByVal NewUrl As String, _
    Optional ByVal FirstInstanceOnly As Boolean = False _
) As Integer
    Dim ReplacedCount As Integer
    For Each IE As InternetExplorer In New ShellWindows()
        If IE.LocationURL = OldUrl Then
            IE.Navigate(NewUrl)
            ReplacedCount += 1
            If FirstInstanceOnly Then
                Exit For
            End If
        End If
    Next IE
    Return ReplacedCount
End Function
Navigieren zu einer bestimmten Website in bestehenden Internet Explorer-Fenstern.

Herstellen und Beenden der Standardinternetverbindung

.NET 1.1 bietet keine Unterstützung zum Wählen von Internetverbindungen auf dem lokalen Rechner. Derartige Funktionalität läßt sich jedoch leicht mittels der Plattformfunktionen InternetAutodial und InternetAutodialHangup aus der WinInet.dll auch in .NET-basierenden Anwendungen nutzen. Die im nachstehenden Listing angegebene Klasse InternetDial verfügt über gemeinsame Methoden zum Herstellen und Beenden der Standardinternetverbindung und stellt alle Möglichkeiten, welche die dahinterliegenden Plattformfunktionen bieten, zur Verfügung. Soll etwa die Standardverbindung unter Anzeige des Einwahldialogs hergestellt werden, kann der Aufruf InternetDialer.Dialup(InternetDialer.AutoDialOptions.ForceOnline) benutzt werden:

Public Class InternetDialer
    Public Declare Function InternetAutodial Lib "wininet.dll" ( _
        ByVal dwFlags As Int32, _
        ByVal hwndParent As IntPtr _
    ) As Boolean

    Private Const INTERNET_AUTODIAL_FORCE_ONLINE As Int32 = &H1
    Private Const INTERNET_AUTODIAL_FORCE_UNATTENDED As Int32 = &H2
    Private Const INTERNET_AUTODIAL_FAILIFSECURITYCHECK As Int32 = &H4
    Private Const INTERNET_AUTODIAL_OVERRIDE_NET_PRESENT As Int32 = &H8

    Private Declare Function InternetAutodialHangup Lib "wininet.dll" ( _
        ByVal dwReserved As Int32 _
    ) As Boolean

    Public Enum AutoDialOptions
        ForceOnline = INTERNET_AUTODIAL_FORCE_ONLINE
        ForceUnattended = INTERNET_AUTODIAL_FORCE_UNATTENDED
        FailIfSecurityCheck = INTERNET_AUTODIAL_FAILIFSECURITYCHECK
        OverrideNetPresent = INTERNET_AUTODIAL_OVERRIDE_NET_PRESENT
    End Enum

    Public Shared Sub Dialup(ByVal Options As AutoDialOptions)
        Dialup(Options, IntPtr.Zero)
    End Sub

    Public Shared Sub Dialup( _
        ByVal Options As AutoDialOptions, _
        ByVal Parent As Control _
    )
        Dialup(Options, Parent.Handle)
    End Sub

    Public Shared Sub Dialup( _
        ByVal Options As AutoDialOptions, _
        ByVal hwndParent As IntPtr _
    )
        If Not InternetAutodial(Options, hwndParent) Then
            Throw _
                New ApplicationException( _
                    "Error dialling the default internet connection." _
                )
        End If
    End Sub

    Public Shared Sub Hangup()
        If Not InternetAutodialHangup(0) Then
            Throw _
                New ApplicationException( _
                    "Error disconnecting internet connection." _
                )
        End If
    End Sub
End Class
Herstellen und Beenden der Standardinternetverbindung mittels der Funktionen InternetAutodial und InternetAutodialHangup.

Laden einer kennwortgeschützten Ressource unter Weitergabe von Cookies

Mitglieder von Google AdSense können Berichte über den durch Werbung erzielten Umsatz mit einem Webbrowser abrufen. Zu diesem Zweck erfolgt eine POST-Authentifizierung durch Übermittlung der Zugangsdaten über ein Webformular. Das serverseitig laufende Anmeldungsskript gibt einen Set-Cookie-Kopfeintrag zurück. Dieses Cookie muß bei weiteren Anfragen weitergegeben werden. Während Webbrowser das automatisch erledigen, muß dies bei der Implementierung einer automatischen Berichtsabfrage berücksichtigt werden.

Die beiden im Folgenden präsentierten Ansätze wurden auf den Spezialfall von Google-AdSense-Berichten zugeschnitten, sodaß Codierungen fest im Code angegeben wurden. Zudem wurde auf den Aufruf des Abmeldeskripts verzichtet. Damit die Codebeispiele kompiliert werden können, muß der Namensraum System.Net importiert werden. Das folgende Beispiel benötigt zudem den Namensraum System.Text:

Dim Client As New WebClient()
Client.Headers.Add("Content-Type", "application/x-www-form-urlencoded")
Client.UploadData( _
    "https://www.google.com/adsense/login.do", _
    "POST", _
    Encoding.UTF8.GetBytes("username=<user name>&password=<password>") _
)
Client.Headers.Clear()
Client.Headers.Add("Cookie", Client.ResponseHeaders("Set-Cookie"))
Dim Data() As Byte = _
    Client.DownloadData("https://www.google.com/adsense/report/overview")
Client.Dispose()
MsgBox(Encoding.UTF8.GetString(Data))
Herunterladen eines Google-AdSense-Berichts mit manueller Weitergabe der Cookies.

Eine weitere Lösung läßt sich mit der Klasse CookieContainer in Verbindung mit der Klasse HttpWebRequest finden:

Dim Request As HttpWebRequest = _
    DirectCast( _
        HttpWebRequest.Create("https://www.google.com/adsense/login.do"), _
        HttpWebRequest _
    )
Dim Cookies As New CookieContainer()
With Request
    .CookieContainer = Cookies
    .Method = "POST"
    .ContentType = "application/x-www-form-urlencoded"
End With
Dim Writer As New StreamWriter(Request.GetRequestStream())
Writer.Write("username=<user name>&password=<password>")
Writer.Close()
Request.GetResponse().Close()
Request = _
    DirectCast( _
        WebRequest.Create("https://www.google.com/adsense/report/overview"), _
        HttpWebRequest _
    )
Request.CookieContainer = Cookies
Dim Reader As New StreamReader(Request.GetResponse().GetResponseStream())
MsgBox(Reader.ReadToEnd())
Reader.Close()
Herunterladen eines Google-AdSense-Berichts unter Zuhilfenahme der Klasse CookieContainer.

Auswahl sinnvoller Dateinamen für heruntergeladene Dateien

Will man in einem Webbrowser eine Datei herunterladen oder die gerade angezeigte Webseite speichern, schlägt der Webbrowser einen mehr oder weniger sinnvollen Dateinamen für die lokale Datei vor. Wenn die Adresse der herunterzuladenden Ressource bereits einen sinnvollen Dateinamen enthält, kann dieser auch für die lokale Datei herangezogen werden. Schwieriger ist die Wahl eines passenden Dateinamens bei Adressen wie https://www.example.org/reports/2007/ und https://www.example.org/reports/?id=982385.

Mögliche Ansatzpunkte für die Auswahl eines sinnvollen Dateinamens:

Die im nächsten Listing angegebene Methode DownloadFile ist als Lösungsskizze zu verstehen. Plausibilitätsprüfungen für Parameter und ermittelte Werte wurden teilweise weggelassen. Die Methode geht bei der Auswahl eines sinnvollen Dateinamens für die heruntergeladene Datei folgendermaßen vor:

Imports Microsoft.Win32
Imports System.IO
Imports System.Net

⋮

Public Function DownloadFile( _
    ByVal Uri As String, _
    ByVal DestinationDirectory As String, _
    Optional ByVal PreferContentDisposition As Boolean = False _
) As String
    Dim Request As HttpWebRequest = _
        DirectCast(WebRequest.Create(Uri), HttpWebRequest)
    Dim Response As WebResponse = Request.GetResponse()
    Dim ResponseStream As Stream = Response.GetResponseStream()
    Dim FileName As String = Nothing
    If PreferContentDisposition Then
        Dim ContentDisposition As String = _
            Response.Headers("Content-Disposition")
        If ContentDisposition IsNot Nothing Then
            Dim Parts() As String = Split(ContentDisposition, ";")
            If Parts.Length > 0 Then
                For i As Integer = 1 To Parts.Length
                    Dim DispositionParam As String = Parts(i)
                    Dim ParamParts() As String = _
                        Split(DispositionParam, "=")
                    Dim Param As String = ParamParts(0)
                    Dim ParamValue As String = ParamParts(1)
                    If Param Is "filename" Then
                        FileName = Path.GetFileName(ParamValue)
                        Exit For
                    End If
                Next i
            End If
        End If
    End If
    If FileName Is Nothing Then
        Dim FileUri As New Uri(Uri)
        FileName = _
            Replace( _
                FileUri.Segments(FileUri.Segments.Length - 1), _
                "/", _
                String.Empty _
            )
        FileName = Path.GetFileNameWithoutExtension(FileName)
        If Len(FileName) = 0 Then
            FileName = Replace(FileUri.Host, ".", "_")
        End If
        Dim Key As RegistryKey = _
            Registry.ClassesRoot.OpenSubKey( _
                "MIME\Database\Content Type\" & Response.ContentType _
            )
        If Key IsNot Nothing Then
            Dim Extension As String = _
                DirectCast(Key.GetValue("Extension"), String)
            If Extension IsNot Nothing Then
                FileName = FileName & Extension
            End If
        End If
    End If
    FileName = Path.Combine(DestinationDirectory, FileName)

    ' Download data and write file.
    Using Reader As New BinaryReader(ResponseStream)
        Using Writer As New BinaryWriter( _
            New FileStream(FileName, FileMode.CreateNew) _
        )

            ' Read blocks of 4 KB.
            Const BlockSize As Integer = 4096
            Dim Buffer() As Byte
            Do
                Buffer = Reader.ReadBytes(BlockSize)
                Writer.Write(Buffer)
            Loop While Buffer.Length > 0
        End Using
    End Using
    Response.Close()
    Return FileName
End Function
Herunterladen einer Ressource aus dem Internet mit Auswahl eines sinnvollen Dateinamens.