404Checkr – Links automatisch auf Gültigkeit testen
Dieser Artikel ist wieder einmal einer kleinen Eigenkreation gewidmet. Nachfolgend soll es um ein kleines Tool von mir gehen, welche ich, wie in den meisten Fällen, aus der Not heraus geschaffen habe. So war ich letztens auf der Suche nach einer Möglichkeit eine große Liste an Links zu überprüfen und tote Links möglichst einfach zu erkennen.
Die Problemstellung
Spätestens ab 20-30 Links macht das per Hand einfach keinen Spaß mehr und ist mehr als zeitintensiv. Deshalb habe ich ein kleines Programm in C# geschrieben, mit welchem beliebig viele Links auf ihre Gültigkeit überprüft werden können. Das Tool habe ich auf den Namen 404Checkr getauft, wobei sich 404 auf den HTTP-Statuscode 404 bezieht, welcher besagt, dass eine Seite/Datei nicht aufgefunden werden konnte.
Was bietet der 404Checkr
Der 404Checkr kann eine beliebig große Liste an Links verarbeiten. Diese kann entweder von Hand oder per Copy und Paste eingegeben werden. Danach kann ausgewählt werden, mit wie vielen Threads analysiert werden soll. Das heißt praktisch, wie viele Seiten parallel überprüft werden sollen. Der Idealwert hierfür steht immer in Abhängigkeit mit der Internetanbindung.
Weiter kann eingestellt werden, nach wie vielen Sekunden ein Timeout-Fehler erhoben wird. Dies kann nützlich sein, wenn man nur Seiten als “ok” eingestuft haben möchte, die innerhalb einer bestimmten Zeit Antworten. Je nach Anwendungszweck mag eine Seite zwar technisch erreichbar sein, jedoch praktisch unbrauchbar, wenn sie erst nach 15-20 Sekunden antwortet.
Sind diese Einstellungen getroffen, kann die Analyse der Links mit einem Klick gestartet werden. Den aktuellen Fortschritt der Überprüfung hat man anhand eines Ladebalkens sowie einer textbasierten Ausgabe jederzeit im Überblick.
Wenn die Überprüfung abgeschlossen ist, können die Ergebnisse entweder als schlichtes .txt-Dokument oder als .csv-Datei exportiert werden.
Download und Hinweise
Natürlich könnt ihr die Software kostenlos downloaden und benutzen. Solltet ihr sie jedoch kommerziell (also zum Beispiel in eurer Firma), so würde ich mich über eine Kontaktaufnahme oder eine kleine Spende per Paypal freuen.
Download: 404Checkr (v. 1.0.0.0)
Screencast – 404Checkr in Aktion
Quellcode und Entwicklerinfos
Im Gegenteil zu manchen meiner anderen Projekte bin ich diesmal so frei, euch den Quellcode zur Verfügung zu stellen. Wer möchte, kann gerne neue Funktion schreiben oder noch ein bisschen an der Performance arbeiten. Solltet ihr tatsächlich etwas Neues aus dem Projekt aufbauen, würde ich mich über einen Kommentar oder eine Mail freuen. (Man sieht ja immer gerne, was andere noch so für Ideen haben, auf die man selbst nicht gekommen ist. Zudem entstehen manchmal auch wirklich spannende Diskussionen aus solchen Situationen heraus.)
Noch zwei Hinweise, bevor es den Downloadlink für das Visual Studio Projekt gibt.
- Ich sagte, ihr könnt gerne etwas Neues aus dem bestehenden Projekt schaffen. Was ich nicht so gerne sehe, ist, wenn ihr lediglich meinen Namen aus dem Projekt entfernt, euer Logo hinzufügt und das Programm dann als “eure Idee/Erfindung” verkauft. Ich sage das an dieser Stelle bewusst, da ich in letzter Zeit öfter mal derartige Anfragen bekomme.
- Erwartet nicht zu viel. Das Programm ist quasi zwischen zwei Kaffeetassen entstanden. Wenn ihr euch im Code verlauft, kann ich dafür nicht haftbar gemacht werden. ;)
Download: 404Checkr (v. 1.0.0.0) – Visual Studio 2012 Solution
Standard-Browser in C# auslesen
Nachfolgend soll es darum gehen, wie man den Pfad zum Standard-Browser unter Windows in C# herausfindet. Dies macht zum Beispiel immer dann Sinn, wenn man eine Datei mittels Process.Start() im Standard-Browser öffnen möchte.
Zwar könnte man davon ausgehen, dass jeder Windowsnutzer auch den Internet Explorer installiert hat und man somit einfach diesen zum Anzeigen von Dateien benutzen sollte, jedoch erhöht es nicht unbedingt das Vertrauen des Users in die eigene Applikation. So wäre zumindest ich als Benutzer meines Programms davon genervt, wenn dieser immer den Internet Explorer anstelle des Firefox, welchen ich als Standard-Browser festgelegt habe, starten würde.
Nun gibt es zwei Möglichkeiten den Standard-Browser in C# zu öffnen. Beide haben Ihre Vor- und Nachteile.
Variante 1 – Process.Start() mit URL als Startpfad
Die einfachste Variante den Standard-Browser zu starten, ist es einfach mittels der Process-Klasse einen neuen Prozess zu starten. Als Startpfad für den Process wird die zu öffnende Url übergeben.
string url = "http://google.de"; Process.Start(url);
Diese zwei Zeilen reichen aus, um die angegebene Url im Standard-Browser zu öffnen. Problematisch wird es jedoch, wenn in der Variable “url” keine gültige Url steht. In diesem Fall kann es zu einer Exception kommen, da das .NET-Framework nicht unbedingt weiß, mit welcher Anwendung der übergebene Parameter gestartet werden soll.
Dies kann zum Beispiel sein, wenn ihr keinen Einfluss auf den Url-Parameter habt und, egal was dort drin steht, die Url oder Datei im Standard-Browser öffnen wollt. Hierfür ist Variante 2 geeignet.
Variante 2 – Den Standard-Browser in C# auslesen
Um in jedem Fall den Standard-Browser zu öffnen und diesen im Zweifelsfall entscheiden zu lassen, ob er die Url/Datei öffnen oder anzeigen kann, ist es nötig den Pfad des Standard-Browser zu kennen. Hierzu könnt ihr folgende kleine Funktion benutzen, die den Pfad des Standard-Browsers aus der Registry ausliest.
private static string GetStandardBrowserPath()
{
string browserPath = string.Empty;
RegistryKey browserKey = null;
try
{
//Überprüfe den Pfad für Windows XP
browserKey = Registry.ClassesRoot.OpenSubKey(@"HTTPshellopencommand", false);
//Wenn kein XP Pfad gefunden wurde, überprüfe den Pfad für Windows Vista und neuer
if (browserKey == null)
{
browserKey = Registry.CurrentUser.OpenSubKey(@"SoftwareMicrosoftWindowsShellAssociationsUrlAssociationshttp", false); ;
}
//Wenn ein Pfad ein gefunden wurde, schneide eventuelle Parameter am Pfad ab
if (browserKey != null)
{
//Entferne Hochkommata
browserPath = (browserKey.GetValue(null) as string).ToLower().Replace(""", "");
//Schneide evtl. Parameter ab
if (!browserPath.EndsWith("exe"))
{
browserPath = browserPath.Substring(0, browserPath.LastIndexOf(".exe") + 4);
}
//Schließe Registry-Schlüssel
browserKey.Close();
}
}
catch
{
//Wenn kein Standard-Browser gefunden wurde, gebe leeren Pfad zurück
return string.Empty;
}
//Gebe Browser-Pfad zurück
return browserPath;
}
Diese Funktion wird nun wieder mittels der Process.Start()-Funktion genutzt. Die Url wird dieses mal jedoch als Parameter und nicht als Stardpfad übergeben.
string url = "http://google.de";
string browserPath = GetStandardBrowserPath();
if (string.IsNullOrEmpty(browserPath))
{
MessageBox.Show("No standard browser found!");
}
else
{
Process.Start(browserPath, url);
}
Fazit
Das war es auch schon. Wenn ihr also sicher seid, dass die Urls in einem validen Format vorliegen, so solltet ihr Variante 1 nutzen. Wenn ihr dies nicht sicherstellen könnt oder vielleicht Dateien in eurem eigenen Format oder mit eurer eigenen Dateiendung öffnen wollt, so ist Variante 2 geeignet. Bei Variante 2 sollte jedoch bedacht werden, dass eure Applikation, je nach User-Kontext, in dem Sie ausgeführt wird, explizit Administrationsrechte benötigt!
Wie geht ihr mit dem “Problem” um? Habt ihr auch eine Lösung, die auf der Registry beruht oder geht ihr vielleicht einen ganz anderen Weg?
Videos in C# erstellen
Heute gibt es mal wieder ein bisschen C# Code. Es geht darum, wie man Videodatein in C# aus einzelnen Bildern bzw. Bitmaps erstellen kann. Mittels der AForge Library, welche ich schon in dem C# Webcam Tutorial verwendet habe, geht dies relativ einfach.
Vorbereitungen
Für das nachfolgende Tutorial braucht ihr die AForge.Video.FFMPEG.dll Library und die dazugehörigen FFMPEG Librarys. Alles beides findet ihr unter folgendem Link. Wählt auf der nachfolgenden Downloadseite die .zip-Datei aus, welche “(libs only)” im Dateinamen hat.
Im nächsten Schritt öffnet ihr euer Visual Studio oder eine Alternative Entwcklungsumgebung mit der ihr eurer Programm schreiben wollt. (Für das nachfolgende Tutorial beziehe ich mich auf as Visual Studio.)
Projekt erstellen
Legt nun ein neues Projekt, ich habe mich für ein WinForms-Projekt entschieden, und wartet bis das Visual Studio das Projekt erstellt hat. Entpackt nun das zuvor heruntergeladene AForge-Paket. In dem Ordner Release des Archivs findet ihr sämtliche AForge Librarys. Für dieses Tutorial benötigen wir jedoch nur die AForge.Video.FFMPEG Library. Kopiert diese in den Ordner eures Visual Studio Projekts. Fügt nun eine Referenz in eurem Projekt auf die AForge.Video.FFMPEG Library hinzu. (Wer nicht weiß, wie man Referenzen hinzufügt, kann dies hier nachlesen.)
Bevor wir weitermachen können, müssen noch zwei weitere Einstellungen an eurem Projekt vorgenommen werden. Zum einen muss gegebenenfalls die Zielversion .NET-Framework angepasst werden. Diese darf maximal 3.5 betragen, da die AForge-Library nicht mit neuren Version (4.0, 4.5) kompatibel ist. Dies geht in den Properties eures Projekts. (Siehe Screenshot, links)
Im zweiten Schritt müsst ihr ggf. die Zielarchitektur eures Projektes umstellen. Standardmäßig dürfe diese auf “Any CPU” stehen. Hier müsst ihr explizit x86 angeben. Diese Einstellung könnt ihr ebenfalls über die Properties eures Projekts vornehmen. (Siehe Screenshot, rechts)
Kurzer Zwischenstand – ihr solltet nun ein Projekt angelegt haben, eine Referenz auf die AForge.Video.FFMPEG-Library hinzugefügt haben, die Zielplattform auf x86 und das Zielframework auf höchsten 3.5 eingestellt haben. Wenn dem so ist, kann es weitergehen.
Im nächsten Schritt brauchen wir noch ein paar weitere Dlls aus dem AForge Archiv. Ihr findet diese im AForge Archiv in dem Ordner “Externals\ffmpeg”. Alle Dateien aus diesem Ordner müsst ihr in den Ausgabeorder eures Visual Studio Projekts kopieren. (Dies müsste nach Umstellung der Zielplattform nun “EuerProjektOrdner\bin\x86\Debug” sein.)
Codebasis schaffen
Wenn ihr das nun auch erledigt habt, kann es mit dem Code losgehen. Als erstes solltet ihr die AForge Library per using-Direktive einbinden.
using AForge.Video.FFMPEG;
Im nächsten Schritt legen wir eine Methode an, in der den Code zur Videoerstellung in C# schreiben wollen. Ich habe hierfür einen Button auf der Form der Anwendung angelegt und werde nachfolgend dessen Button-Click-Methode verwenden.
private void buttonCreateVideo_Click(object sender, EventArgs e)
{
}
VideoFileWriter erstellen
Hinweis: Der nun folgende Code gehört ausschließlich in diese Methode. Ich werden den Code Stück für Stück aufbauen und erklären. Wer nur nach der Lösung sucht, sollte nun einfach ganz an das Ende des Artikel scrollen.
Als erstes legen wir zwei Variablen für die Breite und Höhe des Videos an. Hiermit geben wir die Auflösung des Videos an. Danach erstellen wir eine Instanz des VideoFileWriters und öffnen eine neue Videodatei mittels dessen open()-Methode.
int width = 640;
int height = 480;
VideoFileWriter writer = new VideoFileWriter();
writer.Open("code-bude_test_video.avi", width, height, 25, VideoCodec.MPEG4, 1000000);
Noch eine kurze Erklärung der open()-Methode. Der vierte Parameter gibt die Frames/Sekunde des Videos an, der fünfte den Codec und der sechste die Bitrate des Videos in Bit! Die hier angegebene Rate von 1000000 entspricht einer Videobitrate von 1 Mbit. Je höher die Bitrate, umso größer die Video und umso besser die Qualität.
Videoframes erstellen und schreiben
Nun können wir beliebig viele Frames in form von Bitmap-Grafiken wegschreiben. Hierzu dient dient der nachfolgende Code. Der Modulo-Operator (%) sowie die Zufallszahlen dienen nur dazu, ein wenig Bewegung in unser Testvideo zu bekommen. Dank der Framerate von 25 FPS, die wir oben beim Öffnen des VideoFileWriters angegeben haben, wissen wir, dass 25 Grafiken 1 Sekunde Video ergeben. Da wir mit den nachfolgenden Schleifen 375 Grafiken erstellen bzw. wegschreiben, wird unser Video also 15 Sekunden lang.
Bitmap image = new Bitmap(width, height);
Graphics g = Graphics.FromImage(image);
g.FillRectangle(Brushes.Black, 0, 0, width, height);
Brush[] brushList = new Brush[] { Brushes.Green, Brushes.Red, Brushes.Yellow, Brushes.Pink, Brushes.LimeGreen };
Random rnd = new Random();
for (int i = 0; i < 250; i++)
{
int rndTmp = rnd.Next(1, 3);
Application.DoEvents();
g.FillRectangle(brushList[i%5], (i % width)*2, (i % height) * 0.5f, i%30, i%30);
g.FillRectangle(brushList[i % 5], (i % width)*2, (i % height)*2, i % 30, i % 30);
g.FillRectangle(brushList[i % 5], (i % width) * 0.5f, (i % height) *2, i % 30, i % 30);
g.Save();
writer.WriteVideoFrame(image);
}
g.DrawString("(c) 2013 by code-bude.net", new System.Drawing.Font("Calibri", 30), Brushes.White, 80, 240);
g.Save();
for (int i = 0; i < 125; i++)
{
writer.WriteVideoFrame(image);
}
Video speichern
Im letzten Schritt müssen wir den VideoFileWriter noch schließen.
writer.Close();
Das war es auch schon. Die paar gezeigten Zeilen Code reichen vollkommen aus, um ein eigenes Video in C# zu erstellen.
Download & Codeübersicht
Nachfolgend gibt es noch einmal den komplette Code der Form1.cs-Datei, sowie einen Downloadlink zum Visual Studio Projekt.
Download: VideoFileWriter Beispiel Projekt
using AForge.Video.FFMPEG;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace VideoWriter
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void buttonCreateVideo_Click(object sender, EventArgs e)
{
int width = 640;
int height = 480;
VideoFileWriter writer = new VideoFileWriter();
writer.Open("code-bude_test_video.avi", width, height, 25, VideoCodec.MPEG4, 1000000);
Bitmap image = new Bitmap(width, height);
Graphics g = Graphics.FromImage(image);
g.FillRectangle(Brushes.Black, 0, 0, width, height);
Brush[] brushList = new Brush[] { Brushes.Green, Brushes.Red, Brushes.Yellow, Brushes.Pink, Brushes.LimeGreen };
Random rnd = new Random();
for (int i = 0; i < 250; i++)
{
int rndTmp = rnd.Next(1, 3);
Application.DoEvents();
g.FillRectangle(brushList[i%5], (i % width)*2, (i % height) * 0.5f, i%30, i%30);
g.FillRectangle(brushList[i % 5], (i % width)*2, (i % height)*2, i % 30, i % 30);
g.FillRectangle(brushList[i % 5], (i % width) * 0.5f, (i % height) *2, i % 30, i % 30);
g.Save();
writer.WriteVideoFrame(image);
}
g.DrawString("(c) 2013 by code-bude.net", new System.Drawing.Font("Calibri", 30), Brushes.White, 80, 240);
g.Save();
for (int i = 0; i < 125; i++)
{
writer.WriteVideoFrame(image);
}
writer.Close();
}
}
}
3 Tipps um HttpWebRequests zu beschleunigen
Wer mit dem .NET-Framework arbeitet und ab und an HttpWebRequest benutzt, wird vielleicht schon einmal über das Phänomen gestolpert sein, dass Abfragen mittels der HttpWebRequest Klasse, je nach Fall, ziemlich langsam sein können. Vor allem, wenn man HttpWebrequest im Rahmen von Threading benutzt und probiert möglichst schnell Dateien darüber zu laden, wird die HttpWebRequest-Klasse schnell zur Spaßbremse.
Mag man Anfangs noch die Probleme an der Internetverbindung vermuten, merkt man meist spätestens nach einem Geschwindigkeitsvergleich mit wget oder Curl, dass das “Problem” bei der .NET-Implementierung liegen muss.
Im nachfolgenden Artikel möchte ich auf 3 Punkte eingehen, die euch helfen, das letzte aus der HttpWebRequests-Klasse herauszuholen.
1. DefaultConnectionLimit erhöhen
Alle Instanzen der HttpWebRequest klasse richten sich nach gewissen Vorgaben des ServicepointManager. Eine der Eigenschaften des ServicepointManager ist das “DefaultConnectionLimit”. Standardmäßig ist das DefaultConnectionLimit auf 2 eingestellt, was heißt, dass nie mehr als 2 gleichzeitige Verbinden existieren können. Vor allem wenn man mittels Threading mehrere Requests gleichzeitig abfertigen will, entwickelt sich dies zur absoluten Bremse. In diesem Fall ist das DefaultConnectionLimit mit Bedacht und entsprechend der Kapazität, der zur Verfügung stehenden Verbindung, hochzusetzen.
ServicePointManager.DefaultConnectionLimit = connectionLimit;
Um den optimalen Wert zu finden, sollten gegebenenfalls Test mit unterschiedlichen Werten gemacht werden.
2. ProxyEinstellungen richtig vornehmen
Eine weitere Bremse können die Proxy-Server Einstellungen sein. Sucht man nach Problemen mit HttpWebRequest, so findet man häufiger die Problembeschreibung, dass nur die erste Abfrage sehr lange dauert und alle weiteren Abfragen relativ schnell gehen. Das liegt daran, dass die erste Abfrage nach der Erstellung einer Instanz nach Proxyeinstellungen sucht. Diese Suche nach dem System-Proxy kann gut und gerne zwischen 2-8 Sekunden in Anspruch nehmen. Hierfür gibt es zwei Lösungen.
Es wird kein Proxy benötigt
Sollte kein Proxy benötigt werden, so kann dieser explizit null gesetzt werden, sodass nicht nach dem systemweiten Standardproxy gesucht wird.
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://mywebsite.com");
req.Proxy = null;
Es wird ein Proxy benötigt
Wenn ein Proxy-Server benötigt wird und dieser bekannt ist (das sollte er dem Programmierer im Normalfall sein), so kann er manuell gesetzt werden. Auch so kann die langsame Suche nach einem Proxy umgangen werden.
string proxyUrl = "proxy.myproxy.com";
int proxyPort = 8080;
WebProxy myProxy = new WebProxy(proxyUrl, proxyPort);
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create("http://mywebsite.com");
req.Proxy = myProxy;
3. Expect100Continue deaktivieren
Der letzte Tipp kann noch mal ein Paar Sekunden bringen. Genauer gesagt ca. 1/3 Sekunde pro Abfrage. Je nach Anzahl der Abfragen macht das aber schon etwas aus. In der Standardkonfiguration wird ein Continue(100) Status vor der eigentlichen Abfrage vorweg gesendet. Ist der Server bereit/willig unsere Abfrage zu verarbeiten, so bekommt der Client dies als Antwort und schickt die eigentliche Anfrage los. Das Absenden des Continue(100) und das Warten und Erhalten der Antwort darauf benötigt logischerweise Zeit.
Gehen wir davon aus, dass wir den Server kennen, können wir uns die Continue(100) Anfrage schenken und getrost deaktivieren. Hinzu kommt, dass es immer noch einige Server(-Software) gibt, die mit Continie(100) Anfragen nicht klar kommt. Wer mehr darüber wissen will, kann hier nachlesen.
ServicePointManager.Expect100Continue = false;
Das war es auch schon. Ich hoffe ihr könnt mit den Tipps etwas anfangen. Solltet ihr noch weitere Möglichkeiten kennen, Abfragen mittels HttpWebRequest zu beschleunigen, so schreibt mir einfach einen Kommentar.
Visual Studio Webservice Client mit BasicAuth (für SAP)
Auch mit SAP lassen sich Webservices aufsetzen. Sogar WSDL-Dateien kann man sich in SAP generieren lassen. Das kam mir gerade recht, als ich die Tage einen Webservice in SAP schreiben sollte, welcher von einem .NET-Programm angesteuert werden soll.
In .NET lassen sich Webservices mittels ihrer WSDL-Datei nämlich sehr angenehm als Webreference einfügen. Das Visual Studio generiert dann eine sogenannte Proxy-Klasse daraus und über diese lässt sich der Webservice dann sehr einfach anprogrammieren.
Soweit in der Theorie. In der Praxis sieht das so aus, dass ich in SAP festlegen musste, wie sich die Nutzer gegenüber dem Webservice authentifizieren sollen. Hier hatte ich mich BasicAuth entschieden. Also eine einfach Authentifizierung mittels Benutzername und Passwort.
Leider hat das Visual Studio das beim Generieren der Proxyklasse nicht so ganz drauf. So bekommt man die freundliche Fehlermeldung
“Die HTTP-Anforderung ist beim Clientauthentifizierungsschema “Anonymous” nicht autorisiert. Vom Server wurde der Authentifizierungsheader “Basic realm=”SAP Web Application Server XYZ”" empfangen.”
entgegen geworfen, wenn man einfach ein Objekt der Proxy-Klasse erstellt und eine Methode aufruft.
SAPwebservice.YS_TEST_SERVICE sapClient = new SAPwebservice.YS_TEST_SERVICE();
sapClient.ClientCredentials.UserName.UserName = "mein_benutzername";
sapClient.ClientCredentials.UserName.Password = "mein_passwort";
int result = sapClient.TestMethode("1234", "Testdaten.Testdaten.Testdaten.");
Dabei hatte ich in weiser Voraussicht sogar schon meinen Benutzernamen und mein Passwort mit übergeben. Und was nun?
Um der generierten Proxyklasse beizubringen, dass Sie BasicAuth als Authentifizierung gegenüber dem SAP-Webservice nutzen soll, ist es nötig, folgende Parameter an den Konstruktor der Klasse zu übergeben.
using System.ServiceModel;
[...]
//Binding von Hand rekonfiguriert für SAP-Basic Auth
BasicHttpBinding basicAuthBinding = new BasicHttpBinding(BasicHttpSecurityMode.TransportCredentialOnly);
basicAuthBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Basic;
EndpointAddress basicAuthEndpoint = new EndpointAddress("http://mein_sap_server:mein_port/sap/bc/srt/rfc/sap/mein_webservice?sap-client=mein_mandant&wsdl=1.1");
SAPwebservice.YS_TEST_SERVICE sapClient = new SAPwebservice.YS_TEST_SERVICE(basicAuthBinding, basicAuthEndpoint);
Erstellt man das Binding von Hand, was eigentlich auch so klappen sollte, wenn man ein Objekt der Proxy-Klasse erstellt, so kann die Authentifizierungsmethode angeben. Auf diesem Weg hat man die Chance, dem Proxy zu vermitteln, dass er doch auch die ebenfalls von uns gesetzten Credentials (Nutzername/Passwort) benutzt.
So klappt es dann auch mit dem SAP-Webservice mit BasicAuth und dem .NET/C# Webservice Client.


