Showing posts with label C#. Show all posts
Showing posts with label C#. Show all posts

Tuesday, June 17, 2014

C# - Server / Client


Heute möchte ich euch kurz die Kommunikation zwischen zwei Computern über TCP/IP vorstellen.
Wir werden in diesem Beispiel einen Client und einen Server verwenden.
Der Server ist aber so aufgebaut, dass er mehrere Clients gleichzeitig bearbeiten kann.


Beginnen wir mit dem Aufbau des Servers.

Server Programm:


Wir erstellen eine neue Consolen Anwendung.



Als erstes erstellen wir eine Funktion StartServer();



Start Server












Da der Server auf die locale (eigene) IP Adresse hören soll, habe ich dafür eine eigene Funktion erstellt.



















In der Funktion StartServer() erstellen wir einen neuen TcpListener und rufen die Funktion WaitForClientConnect() auf.


In WaitForClientConnect() bearbeiten wir alle eingehenden Verbindungen zu dem Server.
Dazu erstellen wir für jeden neuen Client einen eigenen Thread.
Dies dient dazu, wenn der Verbindungsaufbau zu Server länger dauert, die restliche Funktionalität nicht beeinflusst wird.























In OnClientConnect() erstellen wir ein neues Objekt TcpClient vom eingehenden Client und übergeben das der Klasse HandleClientRequest.

Anschließend starten wir die Kommunikation mit dem Client, clientReq.StartClient();



In der Klasse HandleClientRequest schaut das Initialisieren folgendermaßen aus:














Es wird der TcpClient verwendet welcher im Hauptprogramm erstellt wurde.
Die StartClient() Funktion ließt die Nachrichten vom Client und geht dann wieder in einen „Warte-Modus“ für neue Nachrichten.


Jetzt fehlt noch die Nachrichtenverarbeitung, die wie folgt aussieht.


































In WaitForRequest() beginnen wir mit dem Lesen der Nachricht und verwenden dabei die asynchrone Funktion ReadCallback.
Die Funktion ReadCallback sendet anschließen auch eine Nachricht zurück an den Client.

Zum Schluss müssen wir den Server noch einsatzbereit machen indem wir die StartServer() Funktion aufrufen.









Client Programm:


Wir erstellen wieder eine Consolen Anwendung.

Wir erstellen zuerst einen neue Klasse TCPClient.
Die Klasse enthält folgenden Funktionen:
  • ConnectToServer
  • ClosConnection
  •  SendData
  • ReceiveData




Funktion ConnectToServer:













Hier erstellen wir ein TCP Objekt welches zum Server zeigt.
Server IP durch die IP Adresse vom Server ersetzen.



Funktion CloseConnection:






Hier wird die aktuelle Verbindung geschlossen.



Funktion SendData:












Es wird die zu sendende Nachricht in ein byte Array umgewandelt und anschließend an den Server gesendet. (Auf Server Seite endet dieser Aufruf in der ReadCallback Funktion)



Funktion ReceiveData:



























Diese Funktion kann nach dem SendData aufgerufen werden um die Antwort vom Server zu empfangen.
Die Antwort wird von einem byte Array wieder in einen String umgewandelt.



Ein kompletter Aufruf würde dann wie folgt aussehen:


C# - Server Client application


Today I want to show a short tutorial about a server client communication via TCP/IP.
The server is able to handle more than one client at the same time.


Let´s start with the Server.

Server Program:


Create a new Console program.



First we create a new function StartServer();



Start Server












Due to that the server should listen to his own IP address, I´ve prepared a separate function for this.



















Within the function StartServer() we are creating a new TcpListener and calling the function WaitForClientConnect().


In WaitForClientConnect() we are handling all connections to the server.
Therefore we will start for every new connected client a new thread.
This will allow us, if a connection takes very long, the main program is still able to accept new requests.
























In OnClientConnect() we are creating a new object type of TcpClient for every new client and call the class HandleClientRequest.

Then we can start the communication with clientReq.StartClient();




In the class HandleClientRequest the Initialize looks like following:














We will use the TcpClient from the main program.
The StartClient() function is reading the message from the client and afterwards the function is going in a waiting mode.

The message process looks like following:


































In WaitForRequest() we are starting to read the message by using the function ReadCallback.
The function ReadCallback will send, after proceeding, the message back to the client.


At least we finish the server program by implementing the StartServer() function in the Main part.








Client Programm:


We also create a Console program.

First we create a new class TCPClient.
The class contains following functions:
  • ConnectToServer
  • ClosConnection
  •  SendData
  • ReceiveData




Funktion ConnectToServer:













Here we create a new object type of TcpClient.
Replace Server IP with the IP address from the server.



Funktion CloseConnection:






Here we are closing the active connection.



Funktion SendData:












Converts the message into a byte array and sends this array to the server.
(On server side this will end in the function ReadCallback)



Funktion ReceiveData:



























This function could be called after SendData and will receive the answer from the server.
Also this answer message will be converted from a byte array into a string message.



The whole flow for the client program looks like following:




C# - Impersonator Beispiel


Ich denke es war schon jeder einmal an diesem Punkt ... Eine Applikation soll Laufwerkszugriffe (kopieren, löschen, umbenennen, etc) ausführen, jedoch sollen die User selbst keinen Zugriff auf das Laufwerk haben.
Wie soll dieses Problem gelöst werden? :-(

Die Antwort könnte sein, einen speziellen Account innerhalb der Applikation zu verwenden.

Ich habe diese Problemstellung wie folgt gelöst:


1) Erstellen einer neuen Klasse "Impersonator"

using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;


namespace Test.Impersonator

{
    public class Impersonator : IDisposable
    {
        public Impersonator(string userName, string domainName, string password)
        {
            ImpersonateValidUser(userName, domainName, password);
        }

        public void Dispose()
        {
            UndoImpersonation();
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);

        private const int LOGON32_LOGON_INTERACTIVE = 2;
        private const int LOGON32_PROVIDER_DEFAULT = 0;

        private void ImpersonateValidUser(string userName, string domain, string password)
        {
            WindowsIdentity tempWindowsIdentity = null;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {
                if (RevertToSelf())
                {
                    if (LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                    {
                        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                        {
                            tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                            impersonationContext = tempWindowsIdentity.Impersonate();
                        }
                        else
                        {
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (token != IntPtr.Zero)
                {
                    CloseHandle(token);
                }
                if (tokenDuplicate != IntPtr.Zero)
                {
                    CloseHandle(tokenDuplicate);
                }
            }
        }

        private void UndoImpersonation()
        {
            if (impersonationContext != null)
            {
                impersonationContext.Undo();
            }
        }

        private WindowsImpersonationContext impersonationContext = null;
    }
}



2) Um die Impersonator Klasse im Hauptprogramm verwenden zu können, muss diese einfach um den Block welcher als anderer User ausgeführt werden soll eingebaut werden.

using...
using Test.Impersonator;
namespace Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private void StartDataTransfer()
        {

              //Do something as User1
            using (new Impersonator("UserName", "Domain", "Password"))
            {

                     //Do something as User2
            }
        }

    }
}
  


Für Fragen oder Anregungen einfach kommentieren :-)

C# - FileSystemWacher Beispiel

Kurzes Beispiel wie die FileSystemWaccher Klasse eingebunden werden kann.

In meinem Beispiel verwende ich eine leere Windows Form Applikation.

Erster Schritt, wir fügen eine TextBox und einen Button zu der Form.












Also nächstes fügen wir die FileSystemWatcher Klasse hinzu.
In diesem Beispiel verwende ich den StartButton um die FileSystemWatcher Klasse zu initialisieren.











Die FileSystemWatcher Klasse soll einen lokalen Pfad überwachen.

Mit watcher.Created += watcherEventwatcher.Deleted += watcherEvent and watcher.Renamed += watcherEvent erstellen wir die Events welche überwacht werden sollen.
Das erste Event dient zur Überwachung für neu erstellte Objekte. (watcher.Created += watcherEvent)
Das zweite Event dient zur Überwachung für gelöschte Objekte. (watcher.Deleted += watcherEvent)
Das dritte Event dient zur Überwachung für umbenannte Objekte. (watcher.Renamed += watcherEvent)

Objekte können in diesem Fall zum Beispiel Ordner oder Dateien sein.

Als nächsten Schritt implementieren wie die Aktion welche hinter dem watcherEvent ausgeführt werden soll.



















Mit dieser Funktion arbeiten wir alle 3 Events ab.
In diesem Beispiel schreibe ich nur den Event-Typ in die TextBox.

Dazu müssen wir das ganze mit einem delegate versehen.
Das ist notwendig, weil der FileSystemWatcher im Hintergrund als eigener Prozess läuft und prozessübergreifende Änderungen an der Form ohne delegate nicht erlaubt sind.

Dazu habe ich eine Funkion "UpdateWatcherOutput" und ein dazugehöriges delegate UpdateWatcherOutputCallback erstellt.

In dieser Funktion können wir Änderungen an der Form zur Laufzeit vornehmen.

Zusätzlich überprüfen wir im FileSystemWatcher ob ein Invoke (Prozessänderung) notwendig ist. (WatcherOutputTextBox.InvokeRequired)

Solle dies zutreffen (TRUE), erstellen wir ein neues delegate UpdateWatcherOutputCallback.


Jetzt können wir das kleine Programm starten und mit dem Button können wir dem FileSystemWatcher mitteilen, dass er mit seiner Aufgabe beginnen soll.
Wenn wir jetzt einen Ordner oder eine Datei in dem angegeben Pfad erstellen, wird Create in der TextBox erscheinen.

Diese Spielerei kann nach Belieben erweitert werden.

Have fun :-)

Monday, June 16, 2014

C# - Impersonator


I think everybody was already at this point... A program should handle file transfers or modifications but the access to the target path is not given to everybody.
How to handle this situation? :-(

One answer could be, to use one common account (inside the program) to do these actions.

I have tried to solve this situation like following:


1) Create a new class "Impersonator"

using System;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.ComponentModel;


namespace Test.Impersonator

{
    public class Impersonator : IDisposable
    {
        public Impersonator(string userName, string domainName, string password)
        {
            ImpersonateValidUser(userName, domainName, password);
        }

        public void Dispose()
        {
            UndoImpersonation();
        }

        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern int LogonUser(string lpszUserName, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken);

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern bool RevertToSelf();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        private static extern bool CloseHandle(IntPtr handle);

        private const int LOGON32_LOGON_INTERACTIVE = 2;
        private const int LOGON32_PROVIDER_DEFAULT = 0;

        private void ImpersonateValidUser(string userName, string domain, string password)
        {
            WindowsIdentity tempWindowsIdentity = null;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

            try
            {
                if (RevertToSelf())
                {
                    if (LogonUser(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                    {
                        if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                        {
                            tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                            impersonationContext = tempWindowsIdentity.Impersonate();
                        }
                        else
                        {
                            throw new Win32Exception(Marshal.GetLastWin32Error());
                        }
                    }
                    else
                    {
                        throw new Win32Exception(Marshal.GetLastWin32Error());
                    }
                }
                else
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
            }
            finally
            {
                if (token != IntPtr.Zero)
                {
                    CloseHandle(token);
                }
                if (tokenDuplicate != IntPtr.Zero)
                {
                    CloseHandle(tokenDuplicate);
                }
            }
        }

        private void UndoImpersonation()
        {
            if (impersonationContext != null)
            {
                impersonationContext.Undo();
            }
        }

        private WindowsImpersonationContext impersonationContext = null;
    }
}



2) Use the Impersonator class in you main program around the function which should run under a different user.

using...
using Test.Impersonator;
namespace Test
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        private void StartDataTransfer()
        {

              //Do something as User1
            using (new Impersonator("UserName", "Domain", "Password"))
            {

                     //Do something as User2
            }
        }

    }
}
  


Feel free to ask/feedback :-)