none
Windows Service Problem beim Start und allg. Frage

    Frage

  • Hallo Leute,

    ich hätte da ein kleines Problem.

    Ich will einen Service auf unseren Schülerrechner installieren, der auf bestimmte Ports hört und dann was ausführt. Da ich noch in den Anfängerschuhen stecke beim C# programmieren wäre ich dankbar für eure Hilfen.

    Unzwar habe einen Service der je nach Raum einen bestimmten Port abhört. Das Programm hat als Formanwendung funktioniert aber als Dienst bekomme ich es nicht hin.

    Je nachdem was da ankommt, macht er irgendwas z.B. einen Netzwerkordner verbinden.

    Ich hab jetzt folgendes Problem: Bin ich richtig mit dem Glauben, dass er diese Port "immer" abhört, oder tut er es solange was kommt und danach nicht mehr? Bleibt der Code an dem Punkt stehen solange was kommt? Oder macht er normal weiter... bin grad etwas verwirrt.

    Mein Ziel ist es also, dass er die Port dauerhaft abhört und immer dementsprechend was macht.. 

    Momentan tut der Dienst nicht mal richtig starten.... ist mein erster Windows Dienst. Also sorry für die blöden Fragen..

    Also hier mein Code:

    public partial class LaufwerkVerbinden : ServiceBase
        {
            //private System.ComponentModel.IContainer components;
            private System.Diagnostics.EventLog eventLog1;
            public LaufwerkVerbinden()
            {
                InitializeComponent();
                eventLog1 = new System.Diagnostics.EventLog();
                if (!System.Diagnostics.EventLog.SourceExists("MySource"))
                {
                    System.Diagnostics.EventLog.CreateEventSource(
                        "MySource", "MyNewLog");
                }
                eventLog1.Source = "MySource";
                eventLog1.Log = "MyNewLog";
            }
    
            private void MapNetworkDriveConnect(string drive, string server, string user, string password)
            {
                Console.WriteLine("Server:" + server + "\n");
                Process p = new Process();
                p.StartInfo.FileName = "net";
                p.StartInfo.Arguments = string.Format("use {0} {1} /user:{2} {3}", drive, server, user, password);
                p.StartInfo.UseShellExecute = false;
                p.Start();
    
            }
            private static void OpenExplorer(string path)
            {
                if (Directory.Exists(path))
                    Process.Start("explorer.exe", path);
            }
    
    
            [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);
            public static void pcSperren()
            {
                //Bildschirm sperren
                Process.Start("rundll32.exe", "user32.dll,LockWorkStation");
            }
            private int eventId = 1;
            public void OnTimer(object sender, System.Timers.ElapsedEventArgs args)
            {
                // TODO: Insert monitoring activities here.  
                eventLog1.WriteEntry("Monitoring the System", EventLogEntryType.Information, eventId++);
            }
            protected override void OnStart(string[] args)
            {
    
                //Log Datei
                eventLog1.WriteEntry("In OnStart");
    
                // Update the service state to Start Pending.  
                ServiceStatus serviceStatus = new ServiceStatus();
                serviceStatus.dwCurrentState = ServiceState.SERVICE_START_PENDING;
                serviceStatus.dwWaitHint = 100000;
                SetServiceStatus(this.ServiceHandle, ref serviceStatus);
    
                
                // Update the service state to Running.  
                serviceStatus.dwCurrentState = ServiceState.SERVICE_RUNNING;
                SetServiceStatus(this.ServiceHandle, ref serviceStatus);
               
               
    
            }
            protected override void OnContinue()
            {
                base.OnContinue();
                System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
                UdpClient receivingUdpClient = new UdpClient(3000);
                if (Environment.MachineName == "Systembetreun")
                {
                    receivingUdpClient = new UdpClient(3003);
                }
                else if (Environment.MachineName.Substring(1, 3) == "402")
                {
                    receivingUdpClient = new UdpClient(3002);
                }
                else if (Environment.MachineName.Substring(1, 3) == "403")
                {
                    receivingUdpClient = new UdpClient(3003);
                }
                else if (Environment.MachineName.Substring(1, 3) == "406")
                {
                    receivingUdpClient = new UdpClient(3006);
                }
               
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
                try
                {
                    Console.WriteLine("Nichts erhalten");
                    // Blocks until a message returns on this socket from a remote host.
                    Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
    
                    string returnData = Encoding.ASCII.GetString(receiveBytes);
                    Console.WriteLine("Nichts erhalten2");
                    Console.WriteLine("This is the message you received " +
                                                 returnData.ToString());
                    Console.WriteLine("This message was sent from " +
                                                RemoteIpEndPoint.Address.ToString() +
                                                " on their port number " +
                                                RemoteIpEndPoint.Port.ToString());
                    if (returnData.ToString() == "verbinden")
                    {
                        Console.WriteLine("User versucht");
                        MapNetworkDriveConnect("P:", "\\\\gsrbackupnas\\" + Environment.MachineName.ToLowerInvariant(), Environment.MachineName.ToLowerInvariant(), "test");
                        // Computernamen lesen 1: System.Security.Principal.WindowsIdentity.GetCurrent().Name.ToString()
                        //textBox1.Text = "du wurdest eingeloggt";
                    }
                    if (returnData.ToString() == "öffnen")
                    {
                        Console.WriteLine("User versucht");
                        OpenExplorer(@"P:\");
                    }
                    if (returnData.ToString() == "pcsperren")
                    {
                        Console.WriteLine("User Pc wird gesperrt");
                        pcSperren();
                    }
                    if (returnData.ToString() == "internetentsperren")
                    {
                        Console.WriteLine("User Internet wird entsperrt");
                        // neuer Code
                    }
                    if (returnData.ToString() == "internetsperren")
                    {
                        Console.WriteLine("User Intenet wird gesperrt");
                        // neuer Code
                    }
    
                }
                catch (Exception e2)
                {
                    Console.WriteLine("Nichts erhalten0");
                    Console.WriteLine(e2.ToString());
                }
    
            }
            protected override void OnStop()
            {
    
                eventLog1.WriteEntry("In onStop.");
            }
        }
        [System.ServiceProcess.ServiceProcessDescription("ServiceControllerDesc")]
        public class ServiceController : System.ComponentModel.Component
    
    
        public enum ServiceState
        {
            SERVICE_STOPPED = 0x00000001,
            SERVICE_START_PENDING = 0x00000002,
            SERVICE_STOP_PENDING = 0x00000003,
            SERVICE_RUNNING = 0x00000004,
            SERVICE_CONTINUE_PENDING = 0x00000005,
            SERVICE_PAUSE_PENDING = 0x00000006,
            SERVICE_PAUSED = 0x00000007,
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public struct ServiceStatus
        {
            public int dwServiceType;
            public ServiceState dwCurrentState;
            public int dwControlsAccepted;
            public int dwWin32ExitCode;
            public int dwServiceSpecificExitCode;
            public int dwCheckPoint;
            public int dwWaitHint;
        };

    Mir ist also etwas die Reihenfolge unklar und wie ich genau diese logfile auch erstelle, was der Fehler ist und wo ich diese lesen kann.

    Danke schonmal für eure Hilfe

    Dienstag, 9. Oktober 2018 11:15

Antworten

  • Hallo,

    Sachen wie:

    Process.Start("rundll32.exe", "user32.dll,LockWorkStation");

    oder:

    [DllImport("user32.dll")]
    public static extern bool LockWorkStation();

    ...lassen sich nur Prozesse aufrufen, sie auf dem "interaktiven Desktop" laufen. Dienste machen das aber nicht, also funktionieren diese Dinge nicht. 

    Um den Rechner zu sperren, kannst du aber die WTSDisconnectSession - Api nutzen. Hier ein Beispiel

    Dementsprechend kann man davon ausgehen, das auch viele andere Dinge nicht direkt von einem Dienst möglich sind, die du eventuell brauchst.

    Diese Map Network Drive (API) soll auch von einem Dienst aus funktionieren, getestet habe ich sie aber noch nicht.

    Ein weiteres Problem ist, das du nicht einfach den Explorer öffnen kannst oder ein Programm von einem Dienst starten kannst, weil eben genau dieser immer läuft, auch wenn kein Benutzer angemeldet ist. Für welchen Benutzer soll der Dienst den Explorer öffnen? Im Netz gibt es verschiedene Ansätze das zu lösen, vielleicht geht das auch, wenn du den Dienst nur für einen bestimmten User installierst. 
    Dienste laufen (soweit ich weiß) in der Session 0, während alles was auf dem Desktop zu sehen ist, im interaktiven angemeldeten User läuft. Somit sind die Aufgaben die dein Programm erledigen soll eigentlich etwas für eine "normale" Desktop- Anwendung. Die Idee auf einem "Schüler-PC" einen Dienst zu installieren kann ich nachvollziehen. Wenn es darum geht, das die anwendung nicht beendet werden kann/soll, kannst du das eventuell durch die Aufgabenplanung direkt oder in Verbindung mit einer Batch-Datei erledigen.

    Eventuell hat noch jemand anderes hier ein paar Lösungen, aber für mich ist das alles irgendwie "durch die Brust ins Auge". 

    Gruß

    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP


    • Bearbeitet Stefan Krömer Donnerstag, 11. Oktober 2018 10:58
    • Als Antwort markiert hopi1 Donnerstag, 11. Oktober 2018 18:25
    Mittwoch, 10. Oktober 2018 18:15

Alle Antworten


  • public partial class LaufwerkVerbinden : ServiceBase
        {
            //private System.ComponentModel.IContainer components;
            private System.Diagnostics.EventLog eventLog1;
            public LaufwerkVerbinden()
            {
                InitializeComponent();
                eventLog1 = new System.Diagnostics.EventLog();
                if (!System.Diagnostics.EventLog.SourceExists("MySource"))
                {
                    System.Diagnostics.EventLog.CreateEventSource(
                        "MySource", "MyNewLog");
                }
                eventLog1.Source = "MySource";
                eventLog1.Log = "MyNewLog";
            }
    
           

    ... und wie ich genau diese logfile auch erstelle, was der Fehler ist und wo ich diese lesen kann.

    Zu dem Dienst kann ich dir nicht viel sagen, bin noch ganz durch dein Programm gestiegen...

    Aber dein "LogFile" landet in der Ereignisanzeige, die du in die Computerverwaltung findest:

    Gruß


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP

    Dienstag, 9. Oktober 2018 16:07
  • Hi nochmal,

    ich habe mal einen Dienst erstellt, wie hier beschrieben (dein Code sieht so aus, als hättest du den auch genommen).

    Ich habe diesen um einen einfachen UDP-Listener erweitert. Den Timer aus dem Beispiel kannst du natürlich rauswerfen.

    Das Projekt habe ich mal hier hochgeladen.

    Aber eine Frage habe ich noch, warum machst du das:

                UdpClient receivingUdpClient = new UdpClient(3000);
                if (Environment.MachineName == "Systembetreun")
                {
                    receivingUdpClient = new UdpClient(3003);
                }
                else if (Environment.MachineName.Substring(1, 3) == "402")
                {
                    receivingUdpClient = new UdpClient(3002);
                }
                else if (Environment.MachineName.Substring(1, 3) == "403")
                {
                    receivingUdpClient = new UdpClient(3003);
                }
                else if (Environment.MachineName.Substring(1, 3) == "406")
                {
                    receivingUdpClient = new UdpClient(3006);
                }

    ???

    Um die unterschiedlichen PC zu konfigurieren (Räume o.ä.) wurde ich dem Dienst Startparameter übergeben:

    Und das:

    System.Net.Sockets.TcpClient clientSocket = new System.Net.Sockets.TcpClient();
    ...ist doch eigentlich überflüssig, oder?

    Gruß


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP

    Dienstag, 9. Oktober 2018 19:44
  • Hey vielen vielen Dank für deine Antworten.

    Also zum 1.  super gute INfo wegen Logfile danke!

    zum 2. Das mit den unterschiedlichen Räumen läuft bei mir so ab.. Da alle im selben Subnetz liegen und ich alle PC's fast gleich clone aber nur den Pc Namen schon automatisch mitgeben kann z.b. R402_15 oder so, hatte ich entschieden dass ich so im Raum 402 am Lehrer PC automatisch einen String über port 3002 schicke und dies wirklich nur PC's empfangen bei denen auch nur der 402 im Namen steckt und nur diesen port 3002 hören. So musste ich nicht für jeden Raum ein anderen Dienst installieren sondern alle haben den selben. So mein Gedanke zumindest :)

    Das mit dem Startparameter klingt auch interessant. Weiß aber nicht wie ich das auf jedem PC in dem Raum automatisch übergeben könnte.

    zu 3. ja mit dem Socket wollte ich einen neuen anderen Versuch starten :) war noch ein Überbleibsel.

    Ich schau mir mal deinen hochgeladenen Dienst an. Übrigens startet mein Dienst anscheinend schon, aber das Senden und Empfangen hat noch nicht geklappt. Bzw. wie gesagt bin ich mir unsicher ob das wirklich so funktioniert das es "dauerhaft" abhört.

    Dienstag, 9. Oktober 2018 20:00
  • Hab mir deinen UDP Listener mal angeschaut. Sieht auf jedenfall schöner aus wie meiner :)

    Ich werd es morgen in der Schule mal probieren und berichte dann.

    Dienstag, 9. Oktober 2018 20:16
  • Eins hab ich vergessen, du musst den dienst nach der Installation in der Firewall freigeben. Eine Desktop-Anwendung fragt beim ersten Start nach "Erlaubnis", ein Dienst kann das nicht.

    Und das mit den Parametern ist doch nicht so easy. Ich bessere das noch aus.

    Meine Idee war, das man beim installieren des Dienstes den Raumnamen o.ä. mitgibt. Dann weiß der Dienst, ob die Nachricht für ihn ist und alle Rechner lauschen auf dem gleichen Port. Wenn es für ihn ist, macht er etwas...

    Gruß


    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP

    Dienstag, 9. Oktober 2018 20:44
  • Also, es hat superrr geklappt bisss zu diesem Punkt:

    while (!ListenerStop)
                    {
                        byte[] bytes = listener.Receive(ref groupEP);
                        
                        
                        string returnData = Encoding.ASCII.GetString(bytes);
                        eventLog1.WriteEntry("Listener wird ausgeführt:"+returnData);
                        if (returnData.ToString() == "verbinden")
                        {
                            eventLog1.WriteEntry("User verbindet sich mit Laufwerk");
                            MapNetworkDriveConnect("P:", "\\\\gsrbackupnas\\" + Environment.MachineName.ToLowerInvariant(), Environment.MachineName.ToLowerInvariant(), "test");
                           
                        }
    }

    in der Log Datei sehe ich den Eintrag: " User verbindet sich mit Laufwerk" so weit sog gut.Aber danach wird der Befehl nicht ausgeführt. Egal ob es jetzt User laufwerk verbinden ist, oder pcsperren oder was auch immer..  der will nichts ausführen. Obwohl mein Code genau der selbe ist wie bei der Formsanwendung.

    Hab ich da noch einen Denkfehler? Hier der Teil zum Netzwerkverbinden:

     
    private void MapNetworkDriveConnect(string drive, string server, string user, string password)
            {
                Console.WriteLine("Server:" + server + "\n");
                Process p = new Process();
                p.StartInfo.FileName = "net";
                p.StartInfo.Arguments = string.Format("use {0} {1} /user:{2} {3}", drive, server, user, password);
                p.StartInfo.UseShellExecute = false;
                p.Start();
    
            }


    Der hat so eigentlich immer Funktioniert oder auch der Befehl pcsperren()

     [DllImport("advapi32.dll", SetLastError = true)]
            private static extern bool SetServiceStatus(IntPtr handle, ref ServiceStatus serviceStatus);
            public static void pcSperren()
            {
                //Bildschirm sperren
                Process.Start("rundll32.exe", "user32.dll,LockWorkStation");
            }

    macht das der Dienst nicht? oder muss ich die Funktion anderswohin schreiben?

    Oder ist das ein Rechteproblem?

    Der Dienst wurde ja als Admin installiert. Läuft aber auf dem Schülerkonto s1. Kann dort auch auf die Logdaten zugriefen und sehe auch die EInträge. Aber das andere führt er nicht aus.

    Mittwoch, 10. Oktober 2018 09:28
  • Hallo,

    Sachen wie:

    Process.Start("rundll32.exe", "user32.dll,LockWorkStation");

    oder:

    [DllImport("user32.dll")]
    public static extern bool LockWorkStation();

    ...lassen sich nur Prozesse aufrufen, sie auf dem "interaktiven Desktop" laufen. Dienste machen das aber nicht, also funktionieren diese Dinge nicht. 

    Um den Rechner zu sperren, kannst du aber die WTSDisconnectSession - Api nutzen. Hier ein Beispiel

    Dementsprechend kann man davon ausgehen, das auch viele andere Dinge nicht direkt von einem Dienst möglich sind, die du eventuell brauchst.

    Diese Map Network Drive (API) soll auch von einem Dienst aus funktionieren, getestet habe ich sie aber noch nicht.

    Ein weiteres Problem ist, das du nicht einfach den Explorer öffnen kannst oder ein Programm von einem Dienst starten kannst, weil eben genau dieser immer läuft, auch wenn kein Benutzer angemeldet ist. Für welchen Benutzer soll der Dienst den Explorer öffnen? Im Netz gibt es verschiedene Ansätze das zu lösen, vielleicht geht das auch, wenn du den Dienst nur für einen bestimmten User installierst. 
    Dienste laufen (soweit ich weiß) in der Session 0, während alles was auf dem Desktop zu sehen ist, im interaktiven angemeldeten User läuft. Somit sind die Aufgaben die dein Programm erledigen soll eigentlich etwas für eine "normale" Desktop- Anwendung. Die Idee auf einem "Schüler-PC" einen Dienst zu installieren kann ich nachvollziehen. Wenn es darum geht, das die anwendung nicht beendet werden kann/soll, kannst du das eventuell durch die Aufgabenplanung direkt oder in Verbindung mit einer Batch-Datei erledigen.

    Eventuell hat noch jemand anderes hier ein paar Lösungen, aber für mich ist das alles irgendwie "durch die Brust ins Auge". 

    Gruß

    Freiberufler im Bereich Softwareentwicklung Von der PLC und Robotik zu VB.NET & C#, vorrangig WPF und UWP


    • Bearbeitet Stefan Krömer Donnerstag, 11. Oktober 2018 10:58
    • Als Antwort markiert hopi1 Donnerstag, 11. Oktober 2018 18:25
    Mittwoch, 10. Oktober 2018 18:15
  • Also erst mal vielen Dank für deine Hilfe,

    mittlerweile bin ich auch dazu gekommen, dass das mit dem Dienst nicht sinnvoll enden wird, denn ich will ja viel Schnick schnack einbauen, was man so im Unterricht brauchen kann. Das ist auch bitter wenn man so weit sich alles zusammencoded und dann merkt, war doch der falsche Weg.

    Also minimum will ich auf jedenfall private Netzwerkordner vom Nas einbinden können zwecks Prüfungen abspeichern, die Pc's sperren können, das Internet abtrennen bzw. das Surfen unterbinden und zu guter Letzt kommunizieren mit Dateien verschicken etc.

    Meine Desktopanwendung hat zwar genau dies getan, aber die war sehr unreif und wurde immer falsch bedient, so dass es meistens nicht lief, weil Schüler doch nicht so klicken wie man es will.Mit dem Dienst dachte ich, ich kann das automatisieren.. aber das hat auch nicht geklappt.

    Jetzt seit heut Mittag versuche ich das mit einer Tray Anwendung die man am besten nur als Admin klicken kann, aber da les ich mich jetzt auch wieder überall durch und komme auch wieder nicht weiter weil ich irgendeine Exception mit Sockets bekomme mit dem Code den ich vorher schon verwendet habe.

    Also irgendwie wirds wohl noch etwas dauern bis zu meinem Ziel :)

    Aber trotzdem vielen vielen Dank, hab nochmal einiges dazu gelernt dank dir.

    Donnerstag, 11. Oktober 2018 18:25