Openthinclient und OpenVPN

In diesem Beitrag zeige ich heute wie man mit Openthinclient und OpenVPN Thin Clients auch außerhalb des eigen Netzes betreiben kann (Telearbeitsplatz/Homeoffice).

Zunächst wird vorausgesetzt, dass ein Windows Active Directory Server im eigenen Netzwerk betrieben wird. Dieser Windows Server wird zur Authentifizierung der Nutzer gegenüber dem OpenVPN Server benötigt. So können am Ende Benutzer mit ihrem Windows Benutzer und deren Passwort einen VPN Tunnel aufbauen.

Als erstes fangen wir mit der Installation einenes Linux Servers an. Ich nehme in meinen Fall eine Ubuntu Distribution. Danach fangen wir mit der Installation von Openthinclient an. Dazu wird folgendes Kommando auf dem Ubuntu Server ausgeführt um die Installationsdatei herunterzuladen:

wget http://archive.openthinclient.org/openthinclient/installer/otc-manager_unix_2019_1_3_with_JRE.sh

Nach dem herunterladen der der Installationsdatei wird diese mit dem folgenden Befehl ausführbar gemacht:

sudo chmod +x ./otc-manager_unix_2019_1_3_with_JRE.sh

Danach wird mit folgenden Befehl der Installationsprozess gestartet. Auf die nachfolgenden Installationsprozess gehe ich nicht näher ein. Aber weitere Information findet man auf der Homepage von Openthinclient https://www.openthinclient.com.

sudo ./otc-manager_unix_2019_1_3_with_JRE.sh

Um die Installation abzuschließen wird man dazu aufgefordert auf der Weboberfläche des Openthinclient Servers aufzurufen. Beispiel: http://XXX.XXX.XXX.XXX:8080

Nach der Installation von Openthinclient beginnen wir mit der Konfiguration des OpenVPN Servers auf unserer Ubuntu Maschine.

Wenn der Server hinter einem Router steht, muss die IP-Adresse des Servers fest vergeben werden. Zudem muss eine Portfreigabe für den VPN Dienst (Standartmäßig UDP 1194) freigegeben werden und es sollte eine Dynamischer DNS Dienst konfiguriert werden, wenn der Router keine statische IP-Adresse vom Provider zugewiesen bekommen hat. Des weiteren muss noch eine statische Route im Router eingetragen werden, damit die OpenVPN Clients auch mit dem internen Netzwerk kommunizieren können. Der OpenVPN Server erstellt ein eigenes Netzwerk, wo sich die verbunden OpenVPN Clients sich drin befinden (10.8.0.0/24). Genau dieses Netzwerk muss als statische Route im Router eingetragen werden, damit Clients oder Server auf das 10.8.0.0/24 Netzwerk zugreifen können.

So könnte die statische Route aussehen: 10.8.0.0 255.255.255.0 192.168.0.6

Die orange markierte IP-Adresse ist die Adresse des Ubuntu Servers wo sich der openthinclient server und der openvpn server drauf befinden.

Nach dem konfigurieren des Routers wird folgender Befehl auf der Konsole des Ubuntu Server ausgeführt um die Installationsdatei für OpenVPN herunterzuladen:

sudo wget https://git.io/vpn -O openvpn-install.sh

Nach dem herunterladen der der Installationsdatei wird diese mit dem folgenden Befehl ausführbar gemacht:

sudo chmod +x ./openvpn-install.sh

Danach wird mit folgenden Befehl der Installationsprozess gestartet:

sudo ./openvpn-install.sh

Nun werden folgende Einstellungen im Installationsskript getroffen:

  • Auf welche IP-Adresse soll der OpenVPN sich binden: Externe IP-Adresse deines Routers oder man verwendet eine Dynamische DNS Adresse. Wenn man die Externe IP-Adresse des Routers verwendet, wird diese alle 24h Stunden vom Internetprovider geändert und man kann den VPN Zugang nicht mehr nutzen. Dagegen hilft eine Dynamischer DNS Dienst, der egal welche Externe IP-Adresse man hat, immer auf die selbe Dynamische DNS Adresse zeigt.
  • Auf welchem Protokoll sollen VPN Verbindungen laufen: [1] Standard Einstellung mit Enter bestätigen.
  • Auf welchen Port soll der OpenVPN Server horchen: [1] Standard Einstellung mit Enter bestätigen.
  • Welcher DNS Dienst soll verwendet werden: [1] Standard Einstellung mit Enter bestätigen.
  • Name der Client Konfigurationsdatei: clients

Am Ende der Konfiguration wird die Client-Konfigurationsdatei im Ordner /root/clients.ovpn abgelegt.

Jetzt bearbeiten wir die server.conf. Dazu werden folgende Befehle auf dem Terminal des Ubuntu Servers ausgeführt:

sudo bash -c 'echo duplicate-cn >> /etc/openvpn/server/server.conf'
sudo bash -c 'echo /usr/lib/openvpn/openvpn-auth-ldap.so /etc/openvpn/auth/auth-ldap.conf >> /etc/openvpn/server/server.conf'
sudo bash -c 'echo push "route 192.168.0.0 255.255.255.0" >> /etc/openvpn/server/server.conf'

Detaillierte Informationen über die Befehle
  • Der erste Befehl dient dazu, dass unsere cilent.ovpn Konfigurationsdatei gleichzeitig für mehrere Thin Clients eingesetzt werden kann.
  • Der zweite Befehl dient dazu, dass der OpenVPN Server über das LDAP Protokoll ein Verbindung zum Windows Active Directory Server aufbaut um die Benutzernamen und Passwörter der Nutzer abzugleichen, die zum Aufbauen der VPN Verbindung ihre Benutzernamen und Passwort angeben müssen.
  • Der dritte Befehl dient dazu, dass die OpenVPN Clients eine Route des internen Netzwerks beigebracht bekommen. Die Netzwerkadresse und die Netzwerkmaske kann natürlich eine andere wie in diesem Beispiel sein.

Nun installieren wir das LDAP Modul für OpenVPN. Dieses Modul ermöglicht die Verbindung zu einem Microsoft Active Directory Server. Dazu wird folgender Befehl ausgeführt:

sudo apt install openvpn-auth-ldap

Als nächstes erstellen wir einen neuen Benutzer auf unserem Active Directory Server. Dazu wird das Tool „Active Directory-Benuter und -Computer“ aufgerufen und es wird ein einfacher Benutzer angelegt. Dieser Benutzer dient nachher dazu eine Verbindung zwischen dem OpenVPN Server und dem Active Directory Server aufzubauen. Bei dem Erstellen ist es egal in welcher OU (Organistationseinheit) sich dieser befindet.

Beim Erstellen des Benutzers muss man darauf achten, dass das Kennwort nie ablaufen darf und das der Benutzer sein Kennwort auch nicht ändern darf.

Nachdem Erstellen des neuen Benutzers werden „Erweiterte Features“ unter Ansicht aktiviert:

Nun wird der distinguishedName von dem neu erstellten Benutzer kopiert.

Wieder auf unserer Linux Maschine angelangt, erstellen wir nun eine auth-ldap.conf Datei, die der OpenVPN Server braucht um eine Verbindung zum Active Directory herzustellen. Folgende Befehle werden nun eingegeben:

sudo mkdir /etc/openvpn/auth
sudo touch /etc/openvpn/auth/auth-ldap.conf
sudo nano /etc/openvpn/auth/auth-ldap.conf 

Im Texteditor nano werden nun folgende Zeilen in die Datei eingefügt:

<LDAP>
         URL               ldap://XXX.XXX.XXX.XXX:389
         BindDN            "CN=openvpn-ldap,OU=IT-Abteilung,OU=Benutzer,DC=zuhause,DC=local"
         Password          "[email protected]$worD1234"
         Timeout           15
         TLSEnable         no
         FollowReferrals   yes
 </LDAP>
  
 <Authorization>
         BaseDN            "OU=Benutzer,DC=zuhause,DC=local"
         SearchFilter      "(sAMAccountName=%u)"
         RequireGroup      false
 </Authorization> 
  • URL: Dies ist die IP-Adresse eures Active Directory Servers. Dabei die X durch die Nummer der IP-Adresse ersetzen.
  • BindDN: Hier wird der kopierte distinguishedName des zuvor neu erstellten Benutzer eingefügt.
  • Password: Passwort des zuvor neu erstellten Benutzers.
  • BaseDN: Benutzer die sich in der angegeben OU (Organisationseinheit) oder darunter befinden, sind nachher dazu berechtigt einen VPN Tunnel mit ihrem Benutzernamen und Passwort aufzubauen.

Detaillierte Informationen über die anderen Befehle
  • Timeout: Zeit nachdem die Verbindungsversuche abgebrochen werden, wenn der Active Directory Server nicht erreicht wird.
  • TLSEnable: Soll die Verbindung zwischen OpenVPN und dem Active Directory Server verschlüsselt werden. In meinem Fall nicht also „no“.
  • FollowRefferals: Wenn man z.B. mehrere Domains (dc=tree1,dc=example,dc=com and dc=tree2,dc=example,dc=com) in einer Gesamtstruktur betreibt, die sich untereinander vertrauen, und man nun versucht Benutzer in tree1 zu suchen, die aber auf tree2 liegen, dann wird der LDAP Server auf tree1 ein LDAP Refferal herausgeben. Damit kann man auch auf tree2 LDAP‘s Server zugreifen. Um diesen Refferal zu folgen muss diese Funktion mit „yes“ aktiviert werden.
  • SearchFilter: Dieser Beschreibt die Abfrage die der OpenVPN Server mit dem zuvor neu erstellten Benutzer auf der BaseDN ausführt. In diesem Fall werden alle Benutzer, die sich in der BaseDN befinden zurückgegeben.
  • RequireGroup: Um auf Benutzer und alle Unterverzeichnisse zuzugreifen muss der Benutzer, der für LDAP verwendet wird, ein Teil dieser Gruppe sein. In meinen Fall habe ich diese Funktion mit „false“ deaktiviert.

Nun kann der nano Editor mit Strg+O die Datei abspeichern und man schließt danach den Editor mit Strg+X.

Am Ende wird der OpenVPN Service neugestartet um alle Änderungen wirksam zu machen. Dazu folgenden Befehl in das Terminal eingeben:

sudo service openvpn restart 

Nun erstellen wir eine neue Anwendung im Openthinclient Manager. Dieser wird über folgende Webadresse geöffnet http://XXX.XXX.XXX.XXX:8080 Dabei X-Zeichen durch die IP-Adresse des Ubuntu-Servers austauschen. Diese Anwendung bekommt den Namen: „OpenVPN“ und ist vom Typ: „OpenVPN Client“. In den Anwendungseinstellungen wird als Konfiguratiosdatei “/clients.ovpn” angegeben und beim Eingabefeld “Nutzername und Passwort” wird “Benutzer fragen” ausgewählt.

Jetzt wird die zuvor erstellte „clients.ovpn“ Datei in den Openthinclient Server hochgeladen, damit diese Dateien an alle Thin Clients ausgeteilt werden. Um die Dateien hochzuladen öffnen wir das Menü „Dateibrowser“ im Openthinclient Manger und laden die Dateien im Folgenden Verzeichnis hoch: nfs/root/custom/rootfs

Nun werden die erstellte OpenVPN Anwendung einem Thin Client zugewiesen, der außerhalb des eigenen Netzwerkes eingesetzt werden soll (Homeoffice). Openthinclient verteilt das Openthinclient OS im eigenen Netzwerk via PXE. Dies ist über das Internet aber nicht möglich und deswegen muss auf den Thin Clients, die außerhalb des eigenen Netzwerkes agieren, lokal das Betriebssystem installiert werden. Dazu wird eine Weitere Anwendung vom Typ „localboot“ erstellt und den erforderlichen Thin Clients zugeordnet.

Nun ist alles eingerichtet und der Thin Client kann außerhalb des eigen Netzwerkes eingesetzt werden 😉.

System Apps aus Windows Image entfernen

In diesem Beitrag werde ich zeigen, wie man System Apps aus einem Windows Image entfernen kann.

Zuerst lädt man sich das Windows Media Creation Tool herunter und erstellt damit ein Windows Insallationsimage (ISO-Datei).

Danach lädt man sich noch ImgBurn (ImgBurn Portable Version) (Tool zum Erstellen und Verwalten von ISO-Dateien) herunter.

Als drittes Programm müssen wir das Windows ADK herunterladen.

Als erstes beginnt man mit dem erstellen der ISO-Datei mit dem Windows Media Creation Tool. Danach kann man mit einem Doppelklick die ISO-Datei in Windows 10 einfach in ein virtuelles Laufwerk einlegen lassen. Alle Daten die auf der ISO werden in einen Order auf den Desktop kopiert.

Im zweiten Schritt installieren wir das Windows ADK (Windows Assessment and Deployment Kit) installiert. Bei der Installationsroutine gibt man an, dass man das Windows Assessment and Deployment Kit auf diesen Computer installieren möchte. Danach muss man die Lizenzvereinbarung akzeptieren.

Danach wählen sie folgende Features zur Installation aus:

  • Bereitstellungstools

Nun klickt man auf “Installieren” um die Features zu installieren.

Nun öffnet man im Startmenü den Ordner “Windows Kits” und führt das “Umgebung für Bereitstellungs- und Imageerstellungstool” mit Administratorrechten aus. Danach werden folgende Befehle in die Kommandozeile eingefügt:

cd %userprofile%\Desktop\Windows\sources

mkdir ..\mount

dism /Get-WimInfo /WimFile:install.esd

In der install.esd sind meist mehrere Versionen von Windows enthalten. Beispielhafte Ausgabe:

Index : 1
Name : Windows 10 Education
Description : Windows 10 Education
Size : 15,746,232,078 bytes

Index : 2
Name : Windows 10 Education N
Description : Windows 10 Education N
Size : 13,492,266,506 bytes

Nun entscheidet man sich für eine Version und wandelt die install.esd in eine install.wim um, die nur die ausgewählte Version enthält.

dism /export-image /SourceImageFile:install.esd /SourceIndex:5 /DestinationImageFile:install.wim /Compress:max /CheckIntegrity 

Nun mounten wir die neu erstellte install.wim um die darin enthaltenden Apps zu löschen.

dism /mount-wim /wimfile:install.wim /mountdir:..\mount /index:1 

Als nächstes wird mit dem nachfolgenden Befehl eine Textdatei auf dem Desktop erstellt. Darin stehen dann alle vorinstallierten Apps drin die in der install.wim enthalten sind. Hier ein kurzer Ausschnitt:

Anzeigename: Microsoft.BingWeather
Version: 4.25.20211.0
Architektur: neutral
Ressourcen-ID: ~
PackageName: Microsoft.BingWeather_4.25.20211.0_neutral_~_8wekyb3d8bbwe
Regionen:

 dism /image:..\mount /Get-ProvisionedAppxPackages > %userprofile%\Desktop\apps.txt 

Nun beginnt der lustige Part 😉 Man kann jetzt mit den PackageName der einzelen Apps und dem nachfolgenden Befehl jede einzelne App aus der install.wim entfernen. In diesem Beispiel wird mit dem Befehl die Windows Weather App entfernt.

dism /image:..\mount /Remove-ProvisionedAppxPackage /PackageName:Microsoft.BingWeather_4.25.20211.0_neutral_~_8wekyb3d8bbwe

Nachdem alle gewünschten App entfernt worden sind können wir die install.wim unmounten, die install.esd löschen und den leeren mount Ordner löschen.

dism /unmount-wim /mountdir:..\mount /commit

del /f install.esd 

rmdir /q /s ..\mount

Die Kommandozeile kann nun geschlossen werden und wir öffnen nun ImgBurn und klicken auf “Create image file from file/folders

Nun wählt man als Source unseren Ordner auf dem Desktop aus  und klicken danach auf das “+ Symbol“.

Als Destination lassen wir uns auf dem Desktop eine ISO-Datei erstellen.

Damit die erstellte ISO-Datei auch bootfähig wird wechseln wir in den Tab “Advanced” und in die Kategorie “Bootable Disk“. Nun wird er Haken bei “Make Image Bootable” gesetzt.

Im Feld “Boot Image” wird folgende Datei hinzugefügt:

C:\Users\Gero\Desktop\Windows\boot\etfsboot.com

Die Zahl im Feld “Sectors To Load” wird auf 8 gesetzt.

Am Ende drückt man auf dem im Bild Orange umrandeten Button, um das erstellen der ISO-Datei zu beginnen.

Alle weiteren Fenster werden immer mit YES oder OK bestätigt und Fertig!!! 😉

Webmininggraph für Coinimp

In diesem Beitrag zeige ich euch wie man in seiner Webseite mit Coinimp und Chart.js einen Webmininggraph für seine Besucher anbieten kann.

Standardmäßig bietet Coinimp schon einen Webminier an. Dieser hat aber keine grafische Oberfläche womit der Besucher auf der Webseite interagieren könnte.

Mit einer zusätzlichen grafischen Oberfläche und dem “Start/Stop Mining” Knopf kann der Benutzer nachvollziehen wie viel Leistung er dem Webseitenbetreiber zu Verfügung stellen möchte und er kann entscheiden ob er überhaupt Minen möchte.

Ihr braucht auch einen FTP Zugang oder Online File Manager für eure Webseite.

Die “my-mining-page.php” und die “my-mining-page.js” braucht ihr um den Webminergraph auf eurer Seite anzuzeigen. So könnt ihr am Ende die my-mining-page.php als <iframe> in eure gewünschte Seite einpflegen.

Download: my-mining-page.zip

Um den Webmininggraph auf seiner Webseite hinzuzufügen, muss man sich zuerst bei Coinimp registrieren.

Danach fügt man eine neue Seite hinzu:

Danach klickt man auf das “Desktop” Symbol um den Webminercode zu generieren. Nun muss nur der “Site-Key” kopiert werden. Dieser ist in dem Bild teilweise geschwärzt.

In der my-mining-page.php muss nun in der Zeile 137 der zuvor kopierte Site-Key eingefügt werden.

Nun muss man per FTP oder per Online File Manager die my-mining-page.php und my-mining-page.js in das Stammverzeichnis des Webservers geladen werden (htdocs).

Zuletzt kann mit einem <iframe> Element die my-mining-page.php in die gewünschte Seite eingepflegt werden. Nur die src-Adresse muss geändert werden.

<iframe src="https://DeineWebseite.de/my-mining-page.php" style="height:300px; width:100%; border:none;"></iframe>

C# Emgu CV Gesichtserkennung

In diesem Beitrag zeige ich euch wie man in Visual Studio mit C# und Emgu CV eine Gesichtserkennungssoftware programmieren kann.

Dieses Programm kann nicht nur Gesichter erkennen, sondern es kann auch Gesichter wiedererkennen. Dieses wird oftmals verwechselt, aber es sind zwei unterschiedliche Dinge. So können am Ende mit dem Programm Gesichter erkannt werden und mit einem Namen versehen werden, damit sie danach vom Programm wiedererkannt werden.

Wenn ihr keine Zeit oder Lust habt das ganze Projekt nach zu programmieren, dann könnt ihr das fertige Projekt hier herunterladen: Win_FaceDetection.zip

Wichtig!!! Die Emgu.CV.UI.dll und Emgu.CV.World.dll müssen beim Öffnen und Bearbeiten des Projektes als Verweise neu hinzugefügt werden.

Als erstes müssen wir die neuste Version des Emgu CV Projekts als ZIP-Datei herunterladen:

Emgu CV

Als nächstes entpacken wir die ZIP-Datei auf dem Desktop. Nun erstellen wir ein neues Projekt in Visual Studio. Nach dem Erstellen des Projektes müssen nun einige Dateien von dem Emgu CV Projekt in unser Projekt hinzugefügt werden. Alle Dateien die in den nachfolgenden Schritten hinzugefügt werden, befinden sich in dem entpacktem Emgu CV Projekt auf dem Desktop.

Als erstes werden zwei DLL-Dateien als Verweise in unser Projekt mit aufgenommen.

  • bin\Emgu.CV.UI.dll
  • bin\Emgu.CV.World.dll

Um die DLLs als Verweise hinzuzufügen einfach auf “Projekt” > “Verweise hinzufügen…” klicken.

Als nächstes werden weitere DLLs und eine Klasse aus dem Emgu CV Projekt in unser Projekt hinzugefügt. Folgende DLLs werden hinzugefügt:

  • \libs\x86\concrt140.dll
  • \libs\x86\cvextern.dll
  • \libs\x86\msvcp140.dll
  • \libs\x86\opencv_ffmpeg341.dll
  • \libs\x86\vcruntime140.dll
  • \Emgu.CV.Contrib\Face\Recognizer.cs

Um Elemente in ein Projekt hinzuzufügen einfach auf “Projekt” >  “Vorhandenes Element hinzufügen…” klicken.

Als letztes wird noch eine XML-Datei zum Projekt hinzugefügt. Diese dient als Datenbank, um nachher Gesichter zu erkennen.

  • \opencv\data\haarcascades\haarcascade_frontalface_default.xml

Für diese Datei erstellen wir einen neuen Ordner im Projektmappen-Explorer. Dazu einfach auf  “Projekt” >  “Neuer Ordner” klicken. Der Name des Ordners lautet: “data“. In diesem Ordner wird nun die “haarcascade_frontalface_default.xml” hinzugefügt. Alle bei allen hinzugefügten Dateien (außer den Verweisen) muss die Eigenschaft “In Ausgabeverzeichnis kopieren” auf “Immer kopieren” eingestellt werden.

So sieht es aus wenn alle Dateien zum Projekt hinzugefügt wurden:

Um die Klasse “Recognizer.cs” und deren Methoden verwenden zu können, muss der “namespace” der Klasse geändert werden. Als Namespace wird dein Projektname eigetragen. So wird der Code in der Recognizer.cs von:

namespace EmguCVFace
{
    /// <summary>
    /// Face Recognizer
    /// </summary>
    public abstract class FaceRecognizer : UnmanagedObject
    {

in

namespace EmguCVFace
{
    /// <summary>
    /// Face Recognizer
    /// </summary>
    public abstract class FaceRecognizer : UnmanagedObject
    {

geändert. Als letzte Vorbereitung müsst ihr in eurem Projektordner einen neuen Ordner erstellen.

  • Win_FaceDetection\Win_FaceDetection\bin\Debug\Face Collection

In diesem Ordner erstellt ihr ein Testdokument mit dem Namen: “Datenbank“. Nun kann mit dem eigentlichen Programmieren begonnen werden ;). Ich zeige nun meinen ganzen Quellcode den ihr auch gerne in euren Projekt Optimieren und Weiterentwickeln könnt.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using System.IO;

namespace Win_FaceDetection
{
    public partial class frmFaceDetection : Form
    {

        Image<Bgr, byte> img;
        VideoCapture vid;
        int indexofSelectedFace = 0;
        FaceRecognizer recognizer = new EigenFaceRecognizer(80, double.PositiveInfinity);
        Bitmap[] detectedFaces;
        string[] faceNames;
        int[] faceIDs;

        public frmFaceDetection()
        {
            InitializeComponent();

            stopToolStripMenuItem.Enabled = false;
            btnZurück.Enabled = false;
            btnVor.Enabled = false;
            btnVerwerfen.Enabled = false;
            btnHinzufügen.Enabled = false;
        }  
        
        private void drawText(Image<Bgr, Byte> img, Rectangle rect, string text)
        {
            Font font = new Font("Arial", 12, FontStyle.Bold);
            Graphics g = Graphics.FromImage(img.Bitmap);

            int tWidth = (int)g.MeasureString(text, font).Width;
            int x;
            if (tWidth >= rect.Width)
                x = rect.Left - ((tWidth - rect.Width) / 2);
            else
                x = (rect.Width / 2) - (tWidth / 2) + rect.Left;

            g.DrawString(text, font, Brushes.Red, new PointF(x, rect.Top - 18));
        }

        public void GesichterErkennen()
        {
            Rectangle[] faceRectangles = null;
            detectedFaces = null;
            string facePath = Path.GetFullPath(@"data/haarcascade_frontalface_default.xml");
            CascadeClassifier classifierFace = new CascadeClassifier(facePath);
            var imgGray = img.Convert<Gray, byte>().Clone();
            faceRectangles = classifierFace.DetectMultiScale(imgGray, 1.1, 4);
            detectedFaces = new Bitmap[faceRectangles.Length];

            for (int i = 0; i < faceRectangles.Length; i++)
            {
                img.Draw(faceRectangles[i], new Bgr(255, 0, 0), 1);
                imgGray.ROI = faceRectangles[i];
                var detectedFace = imgGray.Copy();
                detectedFaces[i] = detectedFace.Resize(200, 200, Inter.Linear).ToBitmap();
            }
            pictureBoxInput.Image = img.ToBitmap();
        }

        public void GesichterWiedererkennen()
        {
            Rectangle[] faceRectangles = null;
            string facePath = Path.GetFullPath(@"data/haarcascade_frontalface_default.xml");
            CascadeClassifier classifierFace = new CascadeClassifier(facePath);
            var imgGray = img.Convert<Gray, byte>().Clone();
            faceRectangles = classifierFace.DetectMultiScale(imgGray, 1.1, 4);

            for (int i = 0; i < faceRectangles.Length; i++)
            {
                int foundfaceID = 0;
                imgGray.ROI = faceRectangles[i];
                var detectedFace = imgGray.Copy();
                detectedFace = detectedFace.Resize(200, 200, Inter.Linear);
                var result = recognizer.Predict(detectedFace);
                /*Die Dictance ist ein Wert, der angibt wie viel Ähnlichkeit das
                  aktuelle Gesicht mit allen anderen Gesichtern in der Datenbank
                  hat. Je kleiner der Wert desto ähnlicher ist das aktuelle Gesicht
                  zu einem anderem Gesicht in der Datenbank.*/
                var distance = result.Distance;
                foundfaceID = result.Label;

                if (distance < 3000)
                {
                    string foundfaceName = "";
                    for (int j = 0; j < faceIDs.Length; j++)
                    {
                        if (faceIDs[j] == foundfaceID)
                        {
                            foundfaceName = faceNames[j];
                            break;
                        }
                    }
                    /*Methode drawText wird aufgerufen um den Namen des 
                    wiedererkannten Gesichts in die PictureBox zu schreiben.*/
                    drawText(img, faceRectangles[i], foundfaceName);
                    img.Draw(faceRectangles[i], new Bgr(0, 255, 0), 1);
                }
            }
            pictureBoxInput2.Image = img.ToBitmap();
        }

        private void öffnenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "Image files (*.jpg, *.jpeg, *.jpe, *.jfif, *.png)" +
                " | *.jpg; *.jpeg; *.jpe; *.jfif; *.png";
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                img = new Image<Bgr, byte>(ofd.FileName);
                int index = tabControl1.SelectedIndex;
                
                //"Wiedererkennen" Tab im Fokus
                if (index == 1)
                {
                    pictureBoxInput2.Image = img.ToBitmap();
                    Trainieren();
                    GesichterWiedererkennen();
                }
                //"Erkennen" Tab im Fokus
                else
                {
                    pictureBoxInput.Image = img.ToBitmap();
                    GesichterErkennen();
                }
            }  
        }

        private void startToolStripMenuItem_Click(object sender, EventArgs e)
        {

            startToolStripMenuItem.Enabled = false;
            öffnenToolStripMenuItem.Enabled = false;
            stopToolStripMenuItem.Enabled = true;

            try
            {
                vid = new VideoCapture();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }

            int index = tabControl1.SelectedIndex;
            //"Wiedererkennen" Tab im Fokus
            if(index == 1)
            {
                Trainieren();
                Application.Idle += ProcessFrameWiedererkennen;
            }
            //"Erkennen" Tab im Fokus
            else
            {
                Application.Idle += ProcessFrameErkennen;
            } 
        }

        private void ProcessFrameWiedererkennen(object sender, EventArgs e)
        {
            img = vid.QueryFrame().ToImage<Bgr, byte>();
            GesichterWiedererkennen();
        }

        private void ProcessFrameErkennen(object sender, EventArgs e)
        {
            img = vid.QueryFrame().ToImage<Bgr, byte>();
            GesichterErkennen();
        }

        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            startToolStripMenuItem.Enabled = true;
            öffnenToolStripMenuItem.Enabled = true;
            stopToolStripMenuItem.Enabled = false;
            Application.Idle -= ProcessFrameWiedererkennen;
            Application.Idle -= ProcessFrameErkennen;
            vid.Dispose();         
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            /*Wenn man bei einer laufenden Videoaufnahme den Tab wechselt, 
              wird die Videoaufnahme gestoppt.*/
            stopToolStripMenuItem.PerformClick();
        }

        private void btnExtrahieren_Click(object sender, EventArgs e)
        {
            if (pictureBoxInput.Image != null)
            {
                btnExtrahieren.Enabled = false;
                btnVerwerfen.Enabled = true;
                btnHinzufügen.Enabled = true;
                //Wenn Videostream noch läuft, wird dieser angehalten.
                stopToolStripMenuItem.PerformClick();
                /*Erstes erkanntes Gesicht wird in 
                  die kleine PictureBox geladen.*/
                if (detectedFaces.Length > 0)
                {
                    if (detectedFaces.Length >= 1)
                    {
                        btnVor.Enabled = true;
                    }
                    pictureBoxFace.Image = detectedFaces[0];
                }
            }    
        }

        private void btnVerwerfen_Click(object sender, EventArgs e)
        {
            /*Verworfenes Bild aus dem Bitmap Array entfernen und 
              das nächste Bild einblenden lassen.*/
            detectedFaces = detectedFaces.Skip(1).ToArray();
            textBoxName.Text = "";
            if (detectedFaces.Length-1 > 0)
            {              
                pictureBoxFace.Image = detectedFaces[0];
            }
            else if (detectedFaces.Length-1 == 0)
            {
                btnVor.Enabled = false;
                pictureBoxFace.Image = detectedFaces[0];
            }
            else
            {
                btnExtrahieren.Enabled = true;
                btnVerwerfen.Enabled = false;
                btnHinzufügen.Enabled = false;
                pictureBoxFace.Image = null;
                pictureBoxInput.Image = null;
            }
            
        }

        private void btnHinzufügen_Click(object sender, EventArgs e)
        {
            if (textBoxName.Text == "")
            {
                MessageBox.Show("Keinen Namen vergeben.");
            }
            else
            {
                //Abspeichern des ausgewählten Gesichts
                Random rand = new Random(DateTime.Now.Millisecond);
                int random = Math.Abs(rand.Next()*1000);
                pictureBoxFace.Image.Save(@"Face Collection\Face_" + 
                    (textBoxName.Text) +"_"+ random + ".bmp");
                textBoxName.Text = "";

                /*Abgespeichertes Bild aus dem Bitmap Array entfernen und 
                  das nächste Bild einblenden lassen.*/
                detectedFaces = detectedFaces.Skip(1).ToArray();
                if (detectedFaces.Length - 1 > 0)
                {
                    pictureBoxFace.Image = detectedFaces[0];
                }
                else if (detectedFaces.Length - 1 == 0)
                {
                    btnVor.Enabled = false;
                    pictureBoxFace.Image = detectedFaces[0];
                }
                else
                {
                    btnExtrahieren.Enabled = true;
                    btnVerwerfen.Enabled = false;
                    btnHinzufügen.Enabled = false;
                    pictureBoxFace.Image = null;
                    pictureBoxInput.Image = null;
                }
                
            }
        }

        private void btnZurück_Click(object sender, EventArgs e)
        {      
            indexofSelectedFace--;
            if (indexofSelectedFace == 0)
            {
                btnZurück.Enabled = false;
                btnHinzufügen.Enabled = true;
                btnVerwerfen.Enabled = true;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
            else
            {
                btnVor.Enabled = true;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
        }

        private void btnVor_Click(object sender, EventArgs e)
        {           
            indexofSelectedFace++;
            if (indexofSelectedFace == detectedFaces.Length-1)
            {
                btnVor.Enabled = false;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }
            else
            {
                btnZurück.Enabled = true;
                btnHinzufügen.Enabled = false;
                btnVerwerfen.Enabled = false;
                pictureBoxFace.Image = detectedFaces[indexofSelectedFace];
            }  
        }

        private void Trainieren()
        {
            string[] filePaths = Directory.GetFiles(@"Face Collection\", "*.bmp");
            var faceImages = new Image<Gray, byte>[filePaths.Count()];
            faceNames = new string[filePaths.Count()];
            faceIDs = new int[filePaths.Count()];
            if (filePaths.Count() == 0)
            {
                MessageBox.Show("Keine Bilder in der Datenbank vorhanden");
            }
            else
            {
                for (int i = 0; i < filePaths.Count(); i++)
                {
                    var faceImage = new Image<Gray, byte>(filePaths[i]);
                    string faceName = filePaths[i].Split('_')[1];

                    char[] charsToTrim = { '.', 'b', 'm', 'p' };
                    int faceID = Convert.ToInt32(filePaths[i].Split('_')[2].Trim(charsToTrim));

                    faceImages[i] = faceImage;
                    faceNames[i] = faceName;
                    faceIDs[i] = faceID;
                }
                /*Der EigenFaceRecognizer wird mit allen Bildern gefüttert 
                  die im Ordner "Face Collection" sind. Datenbank als Textdatei wird auch 
                  erstellt und danach wieder eingelesen.*/
                recognizer.Train(faceImages, faceIDs);
                recognizer.Write(@"Face Collection\database.txt");
                recognizer.Read(@"Face Collection\database.txt");
                MessageBox.Show("Algorithmus wurde mit den gespeicherten Bildern trainiert.");
            }
        }

    }
}

Visual Studio Offlineinstaller erstellen

In diesen Beitrag erkläre ich, wie man einen Offlineinstaller für Visual Studio 2017 erstellt.

Der Vorteil einer Offlineinstallation von Visual Studio ist die spezielle Auswahl der Pakete, die installiert werden sollen. So kann viel Speicherplatz eingespart werden.

Zuerst laden wir uns den Webinstaller von der Visual Studio 2019 Community Version herunter. Visual Studio 2019 Community

Im Download Ordner wird nun ein neuer Ordner mit dem Namen “vs2019” erstellt. Danach wird die Eingabeaufforderung mit Administratorrechten (cmd.exe) gestartet und es wird in den Download Ordner navigiert. Nun kann man folgenden Befehl eingeben um den Offlineinstaller zu erstellen.

vs_community.exe --layout "%userprofile%\Downloads\vs2019" --add Microsoft.VisualStudio.Workload.ManagedDesktop --lang de-DE

Parameter –layout: Dieser Parameter gibt an in welchen Order die Offlinedateien heruntergeladen werden sollen.

Parameter –add: Gibt die Pakete an, die heruntergeladen werden sollen. Liste aller Installationpakete.

Parameter –lang: Gibt an in welcher Sprache die Installationspakete heruntergeladen werden.

Liste der Sprachen:

Language-codeLanguage
cs-CZCzech
de-DEGerman
en-USEnglish
es-ESSpanish
fr-FRFrench
it-ITItalian
ja-JPJapanese
ko-KRKorean
pl-PLPolish
pt-BRPortuguese – Brazil
ru-RURussian
tr-TRTurkish
zh-CNChinese – Simplified
zh-TWChinese – Traditional

Nach dem herunterladen aller Pakete ist, alles bereit für die Offlineinstallation. Im Ordner “vs2017” befindet sich der Ordner “certificates“. In diesem Ordner befinden sich einige Zertifikate, die vor dem Ausführen der vs_setup.exe installiert werden müssen. Dazu werden alle Zertifikate markiert und mit dem Tastendruck “Enter” Installiet. Nun klickt solange auf den “Weiter” Knopf, bis alle Zertifikate installiert sind.

Danach kann man die vs_stup.exe ausführen um mit der Installation zu beginnen.

C# Beenden von Anwendung verhindern

In diesen Beitrag werde ich eine Anwendung programmieren, die das ungewollte schließen von Anwendungen verhindern soll.

Mechanismen um ungewolltes schließen der Anwendung zu verhindern:

  • Die Anwendung wird in Vollbildmodus gestartet
  • Anwendung verfügt über keine Statusleiste (kein Rahmen um das Fenster)
  • Alt+F4 und andere Tastenkombinationen werden außer Kraft gesetzt.
  • Beim Ausführen der Anwendung wird der Explorer beendet.
  • Strg+Alt+Entf um den Taskmanager aufzurufen wird verhindert.

Das fertige Projekt kann auch sofort Runtergeladen werden:

Win_Fullscreen.zip

!!!Wichtig: Um die Anwendung zu schließen einfach die Tasten Strg+Shift+Alt+Q gleichzeitig drücken.

Nachdem man ein neues Projekt erstellt hat, bearbeiten wir nun die Eigenschaften der Form, um den Rahmen und die Statusleiste der Form zu entfernen. Dazu wird die Eigenschaft FormBorderStyle auf None gesetzt.

Im nächsten Schritt werden wir dafür sorgen, dass die Anwendung sich im Vollbildmodus startet. Dazu wird die Eigenschaft WindowState auf Maximized gesetzt.

Nun brauchen wir noch 3 Events von der Form. Wir brauchen das Load, FormClosing und KeyDown Event. Jedes Event wird mit einem Doppelklick in den Code implementiert.

Einige Bibliotheken werden noch gebraucht:

using System.Diagnostics;

Nun widmen wir uns den Code zu, der im Load Event ausgeführt werden soll.

private void frmFullscreen_Load(object sender, EventArgs e)
        {
            //Verhindern, dass man den Task Manager bedienen kann. Beim Öffnen wird dieser in den Hintergrund gelegt. 
            ProcessStartInfo psii = new ProcessStartInfo(System.IO.Path.Combine(Environment.SystemDirectory, "taskmgr.exe"));
            psii.RedirectStandardOutput = false;
            psii.WindowStyle = ProcessWindowStyle.Hidden;
            psii.UseShellExecute = true;
            var processTaskMgr = Process.Start(psii);
            this.SendToBack();

            //Explorer abschalten. Startmenü erscheint nach dem beenden von explorer.exe auch nicht mehr. 
            var psi = new ProcessStartInfo("taskkill", "/IM explorer.exe /f");
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            Process.Start(psi);
        }

Im KeyDown Event wird folgender Code hinzugefügt:

private void frmFullscreen_KeyDown(object sender, KeyEventArgs e)
        {
            //Mit der Tastenkombination Strg+Shift+Alt+Q kann diese Anwendung geschlossen werden.
            if (e.KeyCode == Keys.Q && e.Control && e.Shift && e.Alt)
            {
                tastenkombinationerkannt = true;
                this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            }
        }

Im FormClosing Event wird dann folgender Code hinzugefügt:

private void frmFullscreen_FormClosing(object sender, FormClosingEventArgs e)
        {
            /*Daran hindern, dass die Form geschlossen werden kann. Außer man klickt auf den "Herunterfahren" Button 
oder die Tastenkombination Strg+Shift+Alt+Q wird ausgeführt.*/
            if (tastenkombinationerkannt == true)
            {
                //Explorer wieder einschalten. Startmenü erscheint nach dem start von explorer.exe auch wieder. 
                Process process = new Process();
                process.StartInfo.FileName = "C:\\Windows\\explorer.exe";
                process.Start();

                //Taskmanager freigeben
                var psi = new ProcessStartInfo("taskkill", "/IM Taskmgr.exe /f");
                psi.CreateNoWindow = true;
                psi.UseShellExecute = false;
                Process.Start(psi);
            }
            else
            {
                e.Cancel = true;
            }
        }

Der komplette Quellcode:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Diagnostics;

namespace Win_Fullscreen
{
    public partial class frmFullscreen : Form
    {
        public bool tastenkombinationerkannt = false;
        public frmFullscreen()
        {
            InitializeComponent();
        }

        private void frmFullscreen_Load(object sender, EventArgs e)
        {
            //Verhindern, dass man den Task Manager bedienen kann. Beim Öffnen wird dieser in den Hintergrund gelegt. 
            ProcessStartInfo psii = new ProcessStartInfo(System.IO.Path.Combine(Environment.SystemDirectory, "taskmgr.exe"));
            psii.RedirectStandardOutput = false;
            psii.WindowStyle = ProcessWindowStyle.Hidden;
            psii.UseShellExecute = true;
            var processTaskMgr = Process.Start(psii);
            this.SendToBack();

            //Explorer abschalten. Startmenü erscheint nach dem beenden von explorer.exe auch nicht mehr. 
            var psi = new ProcessStartInfo("taskkill", "/IM explorer.exe /f");
            psi.CreateNoWindow = true;
            psi.UseShellExecute = false;
            Process.Start(psi);
        }

        private void frmFullscreen_KeyDown(object sender, KeyEventArgs e)
        {
            //Mit der Tastenkombination Strg+Shift+Alt+Q kann diese Anwendung geschlossen werden.
            if (e.KeyCode == Keys.Q && e.Control && e.Shift && e.Alt)
            {
                tastenkombinationerkannt = true;
                this.FormBorderStyle = FormBorderStyle.FixedToolWindow;
            }
        }

        private void frmFullscreen_FormClosing(object sender, FormClosingEventArgs e)
        {
            /*Daran hindern, dass die Form geschlossen werden kann. Außer man klickt auf den "Herunterfahren" Button 
oder die Tastenkombination Strg+Shift+Alt+Q wird ausgeführt.*/
            if (tastenkombinationerkannt == true)
            {
                //Explorer wieder einschalten. Startmenü erscheint nach dem start von explorer.exe auch wieder. 
                Process process = new Process();
                process.StartInfo.FileName = "C:\\Windows\\explorer.exe";
                process.Start();

                //Taskmanager freigeben
                var psi = new ProcessStartInfo("taskkill", "/IM Taskmgr.exe /f");
                psi.CreateNoWindow = true;
                psi.UseShellExecute = false;
                Process.Start(psi);
            }
            else
            {
                e.Cancel = true;
            }
        }
    }
}

C# Maus bewegen lassen

In diesen Beitrag zeige ich euch, wie man in C# und mit der Programmierumgebung Visual Studio ein kleines Programm schreiben kann, um die Maus auf eine gewünschte Position bewegen zu lassen. Dazu braucht man grundlegende Kenntnisse wie man mit Visual Studio arbeitet.

Wer das fertige Projekt runterladen möchte kann dies auch tun: Win_MoveMouse.zip

Der wichtigste Part im Code ist das Initialisieren und Importieren aus der user32.dll.

[DllImport("user32.dll")] 
public static extern void mouse_event(int dwFlags, int dx, int dy); 
private const int MOUSEEVENTF_LEFTDOWN = 0x02; 
private const int MOUSEEVENTF_LEFTUP = 0x04; 
private const int MOUSEEVENTF_RIGHTDOWN = 0x08; 
private const int MOUSEEVENTF_RIGHTUP = 0x10;

Um die Maus dann zu bewegen muss man nun nur noch ein Objekt vom Typen Cursor erstellen und ihm einen Punkt geben um den Mauszeiger zu der gewünschten Position bewegen zu lassen.

this.Cursor = new Cursor(Cursor.Current.Handle);
Cursor.Position = new Point(x, y);

Um  ein Mausklick auszuführen muss man folgenden Codeschnipsel verwenden.

mouse_event(MOUSEEVENTF_RIGHTDOWN, x, y);
mouse_event(MOUSEEVENTF_RIGHTUP, x, y);

Das erste Argument beschreibt welcher Button auf der Maus gedrückt oder losgelassen werden soll werden.
Das zweite und dritte Argument dient dazu auf welcher X- und Y-Position der folgende Mausklick ausgeführt erden soll.

Hier sieht man den ganzen Quellcode:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using System.Runtime.InteropServices;


namespace Win_MoveMouse
{
    public partial class frmMoveMouse : Form
    {
        public frmMoveMouse()
        {
            InitializeComponent();
        }
        //Inizalisierung um Mausklicks durchzuführen
        [DllImport("user32.dll")]
        public static extern void mouse_event(int dwFlags, int dx, int dy);
        private const int MOUSEEVENTF_LEFTDOWN = 0x02;
        private const int MOUSEEVENTF_LEFTUP = 0x04;
        private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
        private const int MOUSEEVENTF_RIGHTUP = 0x10;

        private void btnBewegen_Click(object sender, EventArgs e)
        {
            int x;
            int y;
            int BildschirmHoehe;
            int BildschirmBreite;
            this.Cursor = new Cursor(Cursor.Current.Handle);

            //Werte in der TextBoxen von den X und Y-Koordinaten in Variablen speichern.
            x = Convert.ToInt32(txtXKoordinate.Text);
            y = Convert.ToInt32(txtYKoordinate.Text);

            //Bildschirmhöhe und Bildschirmbreite erkennen und in Variablen speichern.
            BildschirmHoehe = Screen.PrimaryScreen.Bounds.Height;
            BildschirmBreite = Screen.PrimaryScreen.Bounds.Width;

            if (x < 0 || x > BildschirmBreite)
            {
                MessageBox.Show("X Koordinate darf nicht kleiner als 0 oder größer als " + BildschirmBreite + " sein.");
            }
            else if (y < 0 || y > BildschirmHoehe)
            {
                MessageBox.Show("Y Koordinate darf nicht kleiner als 0 oder größer als " + BildschirmHoehe + " sein.");
            }
            else
            {
                //Cursor wird an die die zuvor eingetragenen X- und Y-Koordinaten verschoben.
                Cursor.Position = new Point(x, y);
                mouse_event(MOUSEEVENTF_RIGHTDOWN, x, y);
                mouse_event(MOUSEEVENTF_RIGHTUP, x, y);
            }
            
        }

        private void btnBeenden_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }
    }
}

Eigene Dateien in Windows Image einfügen

In diesem Beitrag werde ich zeigen, wie man ein Windows Image (ISO-Datei) mit eigenen Daten ausstatten kann, die dann nach der Installation auf dem Desktop erscheinen.

Zuerst lädt man sich das Windows Media Creation Tool herunter und erstellt damit ein Windows Insallationsimage (ISO-Datei).

Danach lädt man sich noch ImgBurn (ImgBurn Portable Version) (Tool zum Erstellen und Verwalten von ISO-Dateien) herunter.

Nach dem Erfolgreichen erstellen der ISO-Datei mit dem Windows Media Creation Tool kann man mit einem Doppelklick die ISO-Datei in Windows 10 einfach in ein virtuelles Laufwerk einlegen lassen. Alle Daten auf der ISO werden in einen Ordner auf den Desktop kopiert.

Danach wind im “source” Ordner geöffnet und folgende Ordnerstruktur wird erstellt:

C:\Users\Gero\Desktop\Windows\sources\$OEM$\$1\Users\Public\Desktop

In dem Ordner “Desktop” können nun die gewünschten Dateien eingefügt werden, die nach der Installation mit diesem Installationsimage auf dem Desktop erscheinen sollen.

Jetzt öffnen wir ImgBurn und klicken auf “Create image file from file/folders

Nun wählt man als Source unseren Ordner auf dem Desktop aus  und klicken danach auf das “+ Symbol“.

Als Destination lassen wir uns auf dem Desktop eine ISO-Datei erstellen.

Damit die erstellte ISO-Datei auch bootfähig wird wechseln wir in den Tab “Advanced” und in die Kategorie “Bootable Disk“. Nun wird er Haken bei “Make Image Bootable” gesetzt.

Im Feld “Boot Image” wird folgende Datei hinzugefügt:

C:\Users\Gero\Desktop\Windows\boot\etfsboot.com

Die Zahl im Feld “Sectors To Load” wird auf 8 gesetzt.

Am Ende drückt man auf dem im Bild Orange umrandeten Button, um das erstellen der ISO-Datei zu beginnen.

Alle weiteren Fenster werden immer mit YES oder OK bestätigt und Fertig!!! 😉

Windows installieren via PXE

In diesem Beitrag erkläre ich wie man einen PXE Server aufsetzt um damit eine Windows 10 Installation über das Netzwerk zu verteilen.

Am Ende erstellen wir mehrere ISO-Dateien, die Windows PE inne haben und nacheinander alle Installationsdateien von einer Windows 10 ISO-Datei auf den Zielrechner kopieren. Die Übertragung der ISO-Dateien findet über PXE statt.

Wer keine Lust oder Zeit hat alles aufzusetzen und zu erstellen, der kann sich das fertige Projekt einfach herunterladen TFTPD.zip (ca. 5,8GB groß).

Benötigte Programme und Dateien:

Im ersten Schritt wird ein Windowsinstallationsmedium mit dem Windows Media Creation Tool erstellt. Dazu lässt man sich eine ISO-Datei vom Programm erstellen.

Im zweiten Schritt installieren wir das Windows ADK (Windows Assessment and Deployment Kit) installiert. Bei der Installationsroutine gibt man an, dass man das Windows Assessment and Deployment Kit auf diesen Computer installieren möchte. Danach muss man die Lizenzvereinbarung akzeptieren.

Danach wählen sie folgende Features zur Installation aus:

  • Bereitstellungstools

Nun klickt man auf “Installieren” um die Features zu installieren.

Danach wird das Windows PE Add-on für das ADK installiert.

Danach öffnen wir die von dem Windows Media Creation Tool erstellte ISO-Installationdatei von Windows 10 mit einem Doppelklick und wir kopieren alle Dateien auf der ISO-Datei und schieben diese Dateien in einen Ordner auf den Desktop.

Da bei der Übertragung der Installationsdateien über den PXE Server eine ISO-Datei nicht mehr als ca. 1GB groß sein darf, muss die relativ große install.wim oder install.esd, die sich im source Ordner befindet,  aufgeteilt werden. Dazu benutzen wir das Programm HJSplit um diese Datei zu stückeln. In HJSplit klickt man auf den Button “Split” und wählt die “Input File” aus (install.win oder install.esd). Die “Split file size” wird auf 1000 Mbytes gestellt und der Vorgang wird gestartet.

Nach dem Zerstückeln löscht man die Ursprungsdatei. In den meisten Fällen hat man nun vier separate Dateien erhalten (hängt von der Größe der Ursprungsdatei ab).

Auf dem Desktop erstellen wir nun vier neue Ordner:

  • cd1
    • win10
      • install.wim.001 oder install.esd.001
  • cd2
    • win10
      • install.wim.002 oder install.esd.002
  • cd3
    • win10
      • install.wim.003 oder install.esd.003
  • cd4
    • win10
      • boot
      • efi
      • sources
        • install.win.004 oder install.esd.004
      • support
      • autorun.inf
      • bootmgr.efi

Im Ordner “win10” des Ordners “cd4” liegen dann alle anderen Installationsdateien, die noch übrig sind, inklusive des letzten Stücks der install.wim oder install.esd die sich noch im Ordner “source” befindet.

Damit das Windows PE beim Starten auf dem Clientcomputer nachher weiß was es mit den zerstückelten install.wim/esd Dateien und den anderen Installationsdateien für die Windows 10 Installation machen soll, müssen wir für jede der insgesamt 4 ISO-Dateien eine kleines Script schreiben, damit alle Dateien auf den Zielrechner wieder richtig zusammengesetzt werden und alle Installationsdateien auf der Zielfestplatte kopiert werden.

Für CD 1 erstellen wir die Datei diskpart.txt (diskpart.txt) mit folgenden Inhalt:

select disk 0
clean
create partition primary
select partition 1
active
format fs=ntfs quick
assign
exit

Des Weiteren wird noch die Datei startnet.cmd mit folgenden Inhalt erstellt:

wpeinit
@echo off
ping -n 10 127.0.0.1 > NUL
diskpart /s diskpart.txt
ping -n 5 127.0.0.1 > NUL
xcopy "X:\Program Files\win10" c:\win10\sources\ /e /h /y
cls
color 79
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
echo !!                                                !!
echo !! Nach dem Neustart waehle "Windows 10 CD2" aus. !!
echo !!                                                !!
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ping -n 5 127.0.0.1 > NUL
exit

Für CD 2 die Datei startnet.cmd mit folgenden Inhalt erstellt:

wpeinit
@echo off
ping -n 10 127.0.0.1 > NUL
diskpart /s diskpart.txt
ping -n 5 127.0.0.1 > NUL
xcopy "X:\Program Files\win10" c:\win10\sources\ /e /h /y
cls
color 79
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 
echo !!                                                !!
echo !! Nach dem Neustart waehle "Windows 10 CD3" aus. !!
echo !!                                                !!
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ping -n 5 127.0.0.1 > NUL
exit

Für CD 3 die Datei startnet.cmd mit folgenden Inhalt erstellt:

wpeinit
@echo off
ping -n 10 127.0.0.1 > NUL
xcopy "X:\Program Files\win10" c:\win10\sources\ /e /h /y
cls
color 79
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
echo !!                                                !!
echo !! Nach dem Neustart waehle "Windows 10 CD4" aus. !!
echo !!                                                !!
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ping -n 5 127.0.0.1 > NUL
exit

Für CD 4 die Datei startnet.cmd mit folgenden Inhalt erstellt:

Wichtig!!! In Zeile 7 Dateiendung gegebenenfalls auf esd ändern.

wpeinit
@echo off
ping -n 10 127.0.0.1 > NUL
xcopy "X:\Program Files\win10" c:\win10\ /e /h /y
ping -n 5 127.0.0.1 > NUL
cd /d c:\win10\sources
copy /b install.wim.001+install.wim.002+install.wim.003+install.wim.004 install.wim
ping -n 5 127.0.0.1 > NUL
cd /d c:\win10
ping -n 5 127.0.0.1 > NUL
start setup.exe
:loop
cls
color 79
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
echo !!                                           !!
echo !! Bei der Installation bitte nicht          !!
echo !! das Laufwerk formatieren oder loeschen.   !!
echo !!                                           !!
echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
ping -n 7 127.0.0.1 > NUL
pause > NUL
goto loop

Das Endresultat sollte ungefähr so aussehen:

Nun da alle Vorbereitungen getroffen wurden, kann mit dem Erstellen der Windows PE ISOs begonnen werden. Dazu wird im Startmenü die Verknüpfung “Umgebung für Bereitstellungs- und Imageerstellungstools” mit Administratorrechten ausgeführt.

!!! Dieser Vorgang der nun folgt wird für die nächsten drei CDs wiederholt !!! Dabei werden die Dateien aus den jeweiligen Ordnern entnommen.

  • cd1
  • cd2
  • cd3
  • cd4

Nun öffnet sich ein Konsolenfenster und wir geben folgende Befehle hintereinander ein:

copype amd64 C:\WinPE_amd64

Im Ordner C:\WinPE_amd64\media\Boot muss nun die Datei bootfix.bin gelöscht werden. Damit wird man beim Booten des Windows PE Images am Clientrechner nicht mehr dazu aufgefordert eine beliebe Taste zu drücken um mit dem Booten des Betriebsystems fortzufahen.

Danach genben sie diesen Befehl ein:

Wichtig!!! Alle Explorerfenster vorher schließen.

Dism /Mount-Image /ImageFile:"C:\WinPE_amd64\media\sources\boot.wim" /index:1 /MountDir:"C:\WinPE_amd64\mount"

Damit wird das Bootimage (boot.wim) von Windows PE zum Bearbeiten freigeben. Nun ist das Bootimage eingebunden.

In “C:\WinPE_amd64\media\mount\Program Files” wird der Ordner “win10” aus dem Ordner “cd1” kopiert.

Die Dateien (diskpart.txt nur für cd1) und startnet.cmd aus dem Ordner “cd1” werden in den Ordner “C:\WinPE_amd64\media\mount\Windows\System32” kopiert und gegebenenfalls ersetzt.

Danach genben sie diesen Befehl ein:

Wichtig!!! Alle Explorerfenster vorher schließen.

Dism /Unmount-Image /MountDir:"C:\WinPE_amd64\mount" /commit

Das Bootimage wird nun mit den Veränderungen wieder geschlossen.

Danach wird eine ISO-Datei aus den Windows PE Dateien erstellt.

MakeWinPEMedia /ISO C:\WinPE_amd64 C:\WinPE_amd64\Windows_10_CD1.iso

Nun kann die Windows_10_CD1.iso auf den Desktop verschoben werden und der Ordner “C:\WinPE_amd64” kann gelöscht werden.

!!! Der ganze Vorgang wird für die anderen drei CDs wiederholt !!!

Nachdem alle vier ISO-Dateien erstellt wurden kann mit dem Erstellen des PXE Servers begonnen werden. Der PXE Server (TinyPXE) hat einen eigenen proxyDHCP Server und kann somit auch in einem Netzwerk mit bestehenden DHCP Server verwendet werden.

Nun wird auf dem Desktop des Server-PCs der Ordner “TFTPD” erstellt. In diesen Ordner kommen alle Dateien des am Anfang heruntergeladenen TFTPD32 Servers hinein.

Ordnerstruktur des PXE Servers:

  • Desktop
    • TFTPD
      • root
        • boot
          • Windows_10_CD1.iso
          • Windows_10_CD2.iso
          • Windows_10_CD3.iso
          • Windows_10_CD4.iso
      • config.ini
      • filter.sample
      • licensing.txt
      • nics.sample
      • pxesrv.exe

In den “root” Ordner werden nun alle Dateien von grub4dos eingefügt. Im “root” Ordner wird ein weiterer Ordner mit dem Namen “boot” erstellt und alle zuvor erstellten ISO-Dateien werden dort hineinkopiert.

Nun editieren wir die menu.lst von grub4dos.

Wichtig!!! In Zeile 14, 21 und 28 Dateiendung gegebenenfalls auf esd ändern.

# This is a sample menu.lst file. You should make some changes to it.
# The old install method of booting via the stage-files has been removed.
# Please install GRLDR boot strap code to MBR with the bootlace.com
# utility under DOS/Win9x or Linux.

color blue/white light-blue/light-gray black/white black/white

default 0
timeout 10




iftitle [if exist (hd0,0)/win10/sources/install.esd.003] Windows 10 CD4 (Alle Daten gehen bei der Installation verloren)
map --mem /boot/Windows_10_CD4.iso (0xff)
map --hook
chainloader (0xff)
boot


iftitle [if exist (hd0,0)/win10/sources/install.esd.002] Windows 10 CD3 (Alle Daten gehen bei der Installation verloren)
map --mem /boot/Windows_10_CD3.iso (0xff)
map --hook
chainloader (0xff)
boot


iftitle [if exist (hd0,0)/win10/sources/install.esd.001] Windows 10 CD2 (Alle Daten gehen bei der Installation verloren)
map --mem /boot/Windows_10_CD2.iso (0xff)
map --hook
chainloader (0xff)
boot


title Windows 10 CD1 (Alle Daten gehen bei der Installation verloren)
map --mem /boot/Windows_10_CD1.iso (0xff)
map --hook
chainloader (0xff)
boot

!!! Antivirenprogramm deaktivieren. Behindert oft die Kommunikation zwischen Server und Client !!!

Nun startet man den PXE Server indem man auf pxesrv.exe klickt. Danach klickt man auf den Button “Online” um den Server zu aktivieren.

Fertig nun müsst du am Clientrechner nur noch im BIOS Netzwerkboot an die oberste stelle der Bootreihenfolge Stellen und Windows wird über das Netzwerk installiert. 😉

Kostenloses SSL Zertifikat

In dem heutigen Betrag  erkläre ich, wie man mit Cloudflare.com seine eigene Domain/Webseite mit einem kostenlosen SSL Zertifikat ausstatten kann.

SSL Zertifikate werden gebraucht, damit Webseiten von Internet Browsern als sicher anerkannt werden. Nachdem man sich registriert hat oder man sich mit seiner E-Mail Adresse und Passwort angemeldet hat, kann man seine Domain in Cloudflare hinzufügen.

Nachdem man auf “Add Site” geklickt hat, wird man auf die nächste Seite weitergeleitet. Dort klickt man auf den Button “Next“.

Auf der nächsten Seite wird man dazu aufgefordert ein Paket auszuwählen. In unserem Fall wählen wir das kostenlose Paket aus und bestätigen dies mit einem Klick auf den Button “Confirm Plan“.

Auf der nächsten Seite werden ihn nun alle DNS Records von ihrer Domain angezeigt. Dies kann in den meisten Fällen mit einem Klick auf “Continue” bestätigt werden.

Auf der darauf folgenden Seite wird man darauf hingewiesen die Nameserver seiner Domaine zu ändern. Nach dem Ändern der Nameserver seiner Domain klickt man wieder auf “Continue“. Nun kann es einige Zeit dauern bis Cloudflare deine Domain in ihr System aufnimmt. Dieser Prozess kann bis zu 24 Stunden dauern. Danach sollte eure Domain durch ein SSL Zertifikat von Cloudflare gesichert sein.

Optional: Wer sein Nameserver in Freenom ändern möchte, kann siche einen früheren Betrag von mir anschauen.