0

Darstellungsfehler von Koreanischen und anderen ausländischen Zeichensätzen in C# / Visual Studio beheben

Ich weiß, der Titel des Posts ist grausam, aber ich wusste das Problem nicht kürzer zu beschreiben. Aber kommen wir nun zur Sache. Im Rahmen der Entwicklung meines Cloud Downloaders stieß ich unter anderem auf folgendes Problem. Koreanische Zeichen wurden nur als blanke Kästchen dargestellt, obwohl Sie im Quelltext korrekt angezeigt wurden. Wer sich jetzt fragt, wo man die koreanischen Zeichen auf einer deutschen Tastatur herbekommt, sollte einfach mal bei Google Translate vorbeischauen. (Der nachfolgende Screenshot veranschaulicht das Problem noch mal. Zum vergrößern bitte das Bild anklicken.)

Koreanische Zeichen falsche Darstellung

Im Netz habe ich nur sehr wenige Lösungen gefunden. Und fast alle waren meiner Meinung nach eher suboptimal. Meistens wird nämlich folgender Weg vorgeschlagen:

Es wird empfohlen die Methode SetCompatibleTextRenderingDefault() mit dem Parameter true aufzurufen. Hierdurch werden Texte mit der auf  GDI+ basierenden Graphics-Methode gerendert, welche zu Zeiten des .Net-Frameworks 1.x genutzt wurde.
Jedoch entstehen hierbei einige “Nachteile”. Zum einen werden die  Abstände bei der Graphics-Methode anders berechnet, sodass ein bestehendes Layout “auseinanderfallen” kann und zum anderen werden nicht alle Controls korrekt gerendert. (Ab .Net 2.0 wurde die auf GDI basierende TextRenderer Klasse eingeführt.)

Bezogen auf unsere Testapplikation sähe das wie folgt aus:

In der Program.cs aktivieren wir das DefaultTextRendering.

 static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(true);
            Application.Run(new Form1());
        }
    }
}

Und in der Applikation erhalten wir folgendes Ergebnis:
Koreanische Zeichen setcompatibletextrendering(true)
Wie man sieht, wird lediglich das Label korrekt dargestellt. Die Groupbox rendert die Zeichen weiterhin falsch.

Der andere Weg

Eine andere, und meiner Meinung nach eine bessere Lösung, ist der Weg über das Ändern der Schriftart/Font-Property. Es gibt ein paar Schriftarten, die wesentlich mehr Zeichensätze enthalten, als die ganzen Standardschriftarten. Eine davon ist zum Beispiel Arial Unicode MS, welche mit dem Microsoft Office mitgeliefert wird, aber auch auf MacOSX und auch einzeln verfügbar ist.
Setzt man nun diese Schriftart für alle seine Controls, erhält man folgendes Ergebnis:

namespace KoreanCharacters
{
    public partial class Form1 : Form
    {
        // [...]

        private void Form1_Load(object sender, EventArgs e)
        {
            this.Font = new Font("Arial Unicode MS", 8.25f);
            foreach (Control con in GetAllControls(this))
                if (con.Font != null)
                    con.Font = new Font("Arial Unicode MS", 8.25f);

            this.Text = "안녕하세요";
            label1.Text = "안녕하세요";
            groupBox1.Text = "안녕하세요";
        }

        private static List<Control> GetAllControls(Control controlBase)
        {
            List<Control> controls = new List<Control>();
            foreach (Control con in controlBase.Controls)
            {
                controls.Add(con);
                List<Control> subControls = GetAllControls(con);
                controls.AddRange(subControls);
            }
            return controls;
        }
    }
}

Wie man sieht, wird nun auch der Text der Groupbox korrekt dargestellt. Sprich es werden sämtliche Controls, mit Ausnahme des Form-Text, korrekt dargestellt. Deshalb beschrieb ich diese Möglichkeit weiter oben auch nur als “bessere” und nicht als “perfekte” Lösung. Warum der Titel des Forms nicht korrekt gerendert wird, kann ich mir auch nicht erklären.

 

Ich hoffe ich konnte dem ein oder anderen von euch helfen. Standet ihr auch schon vor diesem Problem? Und wie habt ihr es gelöst? Kennt ihr vielleicht sogar eine bessere Lösung? Ich freue mich über jeden Kommentar!

Viele Grüße,
Raffi 

0

Staatstrojaner – bin auch ich betroffen?

Der sogenannte Staatstrojaner (auch Bundestrojaner) geht momentan fleißig durch sämtliche Medien und auch ich habe mich ein bisschen mit dem Thema beschäftigt. Jedoch möchte ich keine Analyse der Gesamtlage machen, noch auf 10 Seiten meine Meinung darüber kundtun, denn das haben schon genug andere getan.

Stattdessen habe ich mir mal ein wenig das Paper des CCC zum Staatstrojaner angeschaut und mir einen Überblick verschafft, was die Firma Digitask da so zustande gebracht hat.
Eigentlich wäre es das dann auch für mich gewesen, wenn ich nicht auf Youtube über ein Video gestolpert wäre, in dem jemand probiert per Screencast zu erklären, wie und wo man auf seinem PC nachschauen kann, ob man sich den Staatstrojaner eingefangen hat. Und das ganze in über 8 Minuten.
Da ich ein unheimliches Faible für Tools habe und am liebsten alles in ein kleines Tool umsetzen würde, habe ich mich daran gemacht, eines zur Erkennung des Staatstrojaners zu schreiben. Die nötigen Informationen dazu beruhen auf dem oben genannten Paper des CCC.

Mittlerweile ist dies zwar relativ unnötig, da die meisten Antivirenprogramm-Hersteller schon eine Erkennungsmethode in ihre Software implementiert haben, aber am Tag der Enthüllung des Trojaners machte das ganze noch Sinn. Somit war ich zwar nicht schnell genug, aber die Arbeit deshalb nicht zu veröffentlichen, wäre auch zu schade.
Von daher gibt es am Ende dieses Artikels, nebst eines kleinen Vorstellungsvideos, den sogenannten „StaatstrojanerCheck“ zum Download. Und da bei so etwas Transparenz eine große Rolle spielt, könnt ihr euch natürlich auch den dazugehörigen Quellcode herunterladen. Alles zusammen ist unter der CC-BY-Lizenz veröffentlicht, das heißt ihr könnt die Software weitergeben und weiterentwicklen, sofern irgendwo mein Name auftaucht. Über Verbesserungsvorschläge und Kritik freue ich mich immer!

Und nun viel Spaß beim Testen und bis bald, wenn es wieder heißt – „1, 2, 3 – Staatstrojaner, bist auch du dabei?“

Download: StaatstrojanerCheck 0.9 (Programm)

Download: StaatstrojanerCheck 0.9 (Source, C#.Net, VS 2008 Solution)

1

Bilder rotieren mit C# – Bitmap.RotateFlip vs.Graphics-Object

Auf der Suche nach einer Funktion zum Rotieren von Bildern in C# bin ich auf folgenden Beitrag auf dotnet-snippets.de gestoßen: Bilder rotieren mit C#.
Neben der eigentlich vorgestellten Methode wurden in den Kommentaren noch 2 andere Methoden vorgestellt und die Frage in den Raum geworfen, welche Methode davon die schnellste sei. Da dies auch mein Interesse weckte, habe ich kurzerhand eine kleine Test-Applikation geschrieben, die die verschiedenen vorgestellten Funktionen auf Herz und Nieren ihre Geschwindigkeit überprüfen sollte.

Nach einigen Testläufen wurde klar, dass die eigentliche Funktion nicht geeignet war, da in ihr jedes Mal ein neues Bitmap Objekt erstellt wurde, was Unmengen an Speicher frisst. Somit wurde folgende Funktion aus den Tests ausgeschlossen.

public Bitmap rotateImage(Bitmap bitmap, float angle)
{
     Bitmap returnBitmap = new Bitmap(bitmap.Width, bitmap.Height);
     Graphics graphics = Graphics.FromImage(returnBitmap);
     graphics.TranslateTransform((float)bitmap.Width / 2, (float)bitmap.Height / 2);
     graphics.RotateTransform(angle);
     graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2);
     graphics.DrawImage(bitmap, new Point(0, 0));
     return returnBitmap;
}

Im Rennen blieb jedoch folgende, leicht veränderte, ebenfalls dem Graphics-Object basierende Funktion,

public Bitmap rotateImageUsi(Bitmap bitmap, float angle)
{
    using (Graphics graphics = Graphics.FromImage(bitmap))
    {
        graphics.TranslateTransform((float)bitmap.Width / 2, (float)bitmap.Height / 2);
        graphics.RotateTransform(angle);
        graphics.TranslateTransform(-(float)bitmap.Width / 2, -(float)bitmap.Height / 2);
        graphics.DrawImage(bitmap, new Point(0, 0));
    }
    return bitmap;
}

sowie die Bitmap-Class eigene RotateFlip Funktion.

public Bitmap rotateInternalFunction(Bitmap bitmap)
{
    bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
    return bitmap;
}

Zum Testen der Geschwindigkeit habe ich folgendes, komprimiertes Foto vom letzten Wochenende genommen.

Steinberg in Goslar
(Anklicken um zum Bild in Originalgröße zu gelangen.)

Dann habe ich diese Bild mit beiden Funktionen jeweils 10, 100, 250, 500, 1000, 5000 und 10000 mal um 180° rotiert und die Zeit für Ausführung der Funktionen gemessen.

int[] testruns = new int[]{10,100,250,500,1000,5000,10000};
Bitmap bmp = (Bitmap)Bitmap.FromFile(pic);
string result = string.Empty;

foreach (int x in testruns)
{

    DateTime s2 = DateTime.Now;
    for (int i = 0; i < x; i++)
    {
        rotateImageUsi(bmp, 180f);
    }
    DateTime e2 = DateTime.Now;

    DateTime s3 = DateTime.Now;
    for (int i = 0; i < x; i++)
    {
        rotateInternalFunction(bmp);
    }
    DateTime e3 = DateTime.Now;

    TimeSpan t2 = new TimeSpan(e2.Ticks - s2.Ticks);
    TimeSpan t3 = new TimeSpan(e3.Ticks - s3.Ticks);

    result += "Durchläufe: " + x.ToString() + "\r\n" +
            t2.TotalMilliseconds.ToString() + "\r\n" +
            t3.TotalMilliseconds.ToString() + "\r\n\r\n==========================\r\n";
}

Dabei kam ich zu folgenden Ergebnissen:

 Testdurchläufe Zeit in ms – Graphics-Object Zeit in ms - Bitmap.RotateFlip
10 640 23
100 5327 167
250 10955 409
500 18641 758
1000 35375 1343
5000 232421 7270
10000 448424 9734

Ausführungsgeschwindigkeit - Bilder rotieren in C#

Getestet habe ich das ganze auf meinem Laptop mit einem Intel Core i3 M330 Prozessor, 4GB Arbeitsspeicher, .Net-Framework 4 unter Windows 7.
Betrachtet man die Ergebnisse, so lässt sich sagen, dass die RotateFlip-Variante wesentlich schneller ist, was vor allem bei der Bearbeitung  von vielen Bildern zu Buche schlägt.
Einen Grund gibt es dennoch auf die Graphics Variante zurückzugreifen: Der frei wählbare Winkel im Gegenteil zu RotateFlip.

Solltet ihr nun auch Lust bekommen haben, könnt ihr euch meine Testumgebung hier als Visual Studio Projekt herunterladen und eure eigenen Tests durchführen. Über Kommentare würde ich mich wie immer natürlich sehr freuen.

Viele Grüße,
Raffi

p.s.: Wenn euch das Testfoto gefällt, solltet ihr mal auf dem Foto-Blog meiner Freundin vorbeischauen.

1

Dlls in Visual Studio C# Projekte einbinden

Prolog:

Gestern erhielt ich folgende Mail von Christoph.

hallo webmaster,
ich möchte gerne meine webcam
in meinem C#-Projekt einbinden (auf deiner Seite gut beschrieben)
Ich habe die dll’s runtergeladen und
nun weiß ich nicht wie ich sie
unter Microsoft Visual Studios 2008 einbinden kann
würde mich auf Antwort freuen
Mit freundlichen Grüßen Christoph

Für all jene, die noch nicht so lange dabei sind – Christoph bezieht sich auf folgenden Artikel. Aber nun zu seinem Problem. Wie bindet man Dlls im Visual Studio in ein laufendes Projekt ein?

Lösung:

Die Lösung des Problems liegt näher als manch einer von euch denken mag. Zuerst macht ihr einen Rechtsklick auf den Eintrag “Verweise” im Projektmappen-Explorer und klickt in dem sich öffnenden Kontextmenü auf “Verweis hinzufügen”. (Bild 1)

Nun geht ihr auf den Reiter “Durchsuchen”, wählt die gewünschten (.Net kompatiblen) Dlls aus und bestätigt eure Auswahl mit einem Klick auf den Ok-Button. (Bild 2)

Im letzten Schritt könnt ihr noch die entsprechenden Using-Direktiven in eurem Quellcode hinzufügen um einen einfacheren Zugriff auf die Methoden der Dlls zu erlangen. (Bild 3)

Visual Studio - Verweise

Bild 1

Dlls auswählen

Bild 2

Using-Direktiven hinzufügen

Bild 3

 

 

 

 

 

 

 

Das war’s auch schon. Ich hoffe ich habe das Thema verständlich genug erklärt. Sollten dennoch Fragen aufgekommen sein, gilt das gleiche wie immer – ab in die Kommentare damit!

 

Viele Grüße,
Raffi

0

Benutzereingaben in C# speichern am Beispiel einer Textbox

Auf Wunsch eines Lesers gibt es heute mal eine Runde C# Basics. Nach dem ich ein kleines Snippet zum Umgang mit den AppSettings verbloggt hatte, wurde ich gefragt, wie man denn nun eigentlich mit Hilfe des Snippets Benutzereingaben abspeichern könnte. Gefragt, verbloggt. Hier kommt die Lösung.

Zur Verdeutlichung habe ich heute extra mal ein kleines C#-WinForms-Projekt im Visual Studio angelegt. Auf  das Form kommen jeweils ein Label und eine Textbox. Des Weiteren finden die oben schon erwähnten Funktionen zum Laden und Speichern von Werten in den AppSettings Verwendung.

WinForms Events - Laden und Speichern von Benutzereingaben in den AppSettingsWenn ihr das Projekt angelegt und die Controls hinzugefügt habt, sollte das ganze in etwa wie auf dem nebenstehenden Bild aussehen.

Als nächstes müsst ihr zwei EventHandler anlegen. Am einfachsten geht das über das “Ereignisse-Fenster”. (Das ist jenes, welches im nebenstehenden Bild rot umkringelt ist.)
Dort legt ihr einen EventHandler für das Load-, und einen für das FormClosing-Event an.
Abschließend müssen wir die beiden Methoden nur noch mit Leben füllen. Da der benötigte Code nicht all zu lang ist, gibt es folgende Erklärungen direkt am Quelltext.

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;
using System.Configuration;

namespace BenutzereingabenSpeichern
{
    public partial class FormMain : Form
    {
        public FormMain()
        {
            InitializeComponent();
        }

        public string getAppSetting(string key)
        {
            //Laden der AppSettings
            Configuration config = ConfigurationManager.
                                    OpenExeConfiguration(
                                    System.Reflection.Assembly.
                                    GetExecutingAssembly().Location);
            //Zurückgeben der dem Key zugehörigen Value
            return config.AppSettings.Settings[key].Value;
        }

        public void setAppSetting(string key, string value)
        {
            //Laden der AppSettings
            Configuration config = ConfigurationManager.
                                    OpenExeConfiguration(
                                    System.Reflection.Assembly.
                                    GetExecutingAssembly().Location);
            //Überprüfen ob Key existiert
            if (config.AppSettings.Settings[key] != null)
            {
                //Key existiert. Löschen des Keys zum "überschreiben"
                config.AppSettings.Settings.Remove(key);
            }
            //Anlegen eines neuen KeyValue-Paars
            config.AppSettings.Settings.Add(key, value);
            //Speichern der aktualisierten AppSettings
            config.Save(ConfigurationSaveMode.Modified);
        }

        private void FormMain_Load(object sender, EventArgs e)
        {
            try
            {
                //Laden der Eigenschaft aus den Settings
                textBoxEingabe.Text = getAppSetting("benutzerEingabe");
            }
            catch (Exception ee)
            {
                //Fehler abfangen. z.B. nicht vorhandene AppSettings
                textBoxEingabe.Text = ee.Message;
            }
        }

        private void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            //Speichern der Benutzereingabe
            setAppSetting("benutzerEingabe", textBoxEingabe.Text);
        }

    }
}

Ist doch ganz einfach, oder etwa nicht? Solltet ihr noch Fragen haben, schreibt mir einfach einen Kommentar. Wer mag kann sich das ganze Visual Studio Projekt zu diesem Artikel auch herunterladen.

Viele Grüße,
Raffi

weitere Seiten ... 1 2 3 4