0

Standard-Browser in C# auslesen

csharp_standard_browserNachfolgend 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?

0

Videos in C# erstellen

videos in c# erstellenHeute 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.)

visual_studio_zielframework_einstellenBevor 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)

visual_studio_zielplattform_einstellenIm 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();
        }
    }
}
0

3 Tipps um HttpWebRequests zu beschleunigen

HttpWebRequest LogoWer 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.

0

Visual Studio Webservice Client mit BasicAuth (für SAP)

SAP Webservice with .NET Webservice Client and BasicAuthAuch 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.

1

Alias für using-Direktiven und Namespaces in C#

Jeder, der schon das ein oder andere Programm in C# geschrieben hat, wird das Problem vermutlich kennen. Man bindet die ein und die andere Library ein. Teilweise auch Librarys, die ähnliche Zwecke erfüllen. Das kann gut gehen, muss es aber nicht.

Schnell bekommt man eine Fehlermeldung nach dem Schema: “XYZ ist ein mehrdeutiger Verweis und kann von LibraryA.XYZ oder LibraryB.XYZ sein.

Doch was nun? Am Beispiel der AForge Library möchte ich das Problem einmal exemplarisch lösen. Als Beispiel nehmen wir an, wir hätten bisher folgende beide Referenzen gesetzt und die using-Direktiven geschrieben.

 using System.Drawing;
 using AForge.Imaging;
 

Verwenden wir nun die Klasse “Image” in unserem Programm, so erhalten wir folgende Fehlermeldung, die daraus resultiert, dass es sowohl im Namespace System.Drawing als auch in AForge.Drawing eine Klasse namens “Image” gibt.

visual_studio_namespace_alias

Nun könnte man abwägen, auf die Elemente wessen Namespace wir öfter zugreifen und dann die using-Direktive des seltener verwendeten Namespace entfernen. Schließlich könnte man ja auch unter Angabe des Namespaces direkt auf dessen Elemente zugreifen.

 Image img = AForge.Imaging.Image.FromFile(imagePath);
 

Das ist jedoch je nach Anzahl der Aufrufe als auch nach der Länge/Verschachtelung des Namespaces mehr als umständlich. Viel einfacher und in meinen Augen auch eleganter ist es, einen Alias für einen Namespace zu vergeben. Das Setzen eines Alias lässt sich direkt in der using-Direktive erledigen. Anhand unseres Beispiels könnten wir die AForge using-Direktive wie folgt umschreiben.

 using System.Drawing;
 using AF = AForge.Imaging;
 

Nun können wir nachfolgend in unserem Programm anstelle von “AForge.Imaging” einfach immer “AF” einschreiben. Das löst zum einen das Problem der mehrdeutigen Verweise und zum anderen hilft es auch, schnell und einfach zwischen den Referenzen zu unterscheiden.

 Image img = AF.Image.FromFile(imagePath);