Laden...
T
TomRie myCSharp.de - Member
Ingenieur Schweiz Dabei seit 21.01.2021 14 Beiträge
Benutzerbeschreibung

Forenbeiträge von TomRie Ingesamt 14 Beiträge

05.02.2021 - 18:55 Uhr

@inflames2k, in der Konsolenapplikation ohne Service gibt es doch noch gar keinen DataProcessor. Ich denke da verstehst Du etwas ganz und gar falsch. Dass der Service "und nur der Service (inkl. DataProzessor)" nicht für diese Art Anwendung geeignet ist sage ich ja schon lange, daher muss ja etwas anderes her.

Übrigens wenn das gezeigte nur als Konsole ausgeführt wird, kommt der Timer entgegen deiner Ansicht gar nicht zum Zug, nur beim auführen des Services. Das erkennt man kurzerhand im Beispiel über den Link.

05.02.2021 - 16:09 Uhr

@inflames2k, ja, das entspricht genau meiner Vermutung. Da ich mich mit Diensten noch ziemlich schlecht auskenne, kann ich nur auffindbare Beispiele heranziehen und sie testen. Leider findet sich kaum eins das genau zur Anwendung passt, so dass herumpröbeln und testen angesagt ist, daher also besser mal die Profis fragen ...

05.02.2021 - 16:00 Uhr

Jetzt habe ich vom Admin erfahren, dass die Konsolenapplikation über Always Up problemlos auf den Servern läuft. Also die Variante ohne Dienst was also bedeutet, dass gegenwärtig nur dieser Dienst das Problem ist. Wie bereits gesagt, nehme ich an, dass die Timer darin das Problem verursachen. Daher schon zu Begin die Frage, wie der aussehen muss damit eben nicht alle 10 sec. ein neuer DataProzessor gestartet wird.

05.02.2021 - 11:55 Uhr

@Th69, danke für die Analyse. Der Timer in der Service-Klasse mach im reduzierten Code hier natürlich keinen Sinn, hingegen im Orginal schon. Sinn und Zweck der App ist es, empfangene Deposit informationen die als XML daherkommen in CSV zu übersetzen und in definierbaren Pfaden abzulegen. Dabei wird nach erfolgreicher Übersetzung auch eine Bestätigung an den Client gesendet. Da alles automatisch ablaufen muss, sind die Thread's hier auch etwas verwirrend. Ich fand leider keine Lösung die besser funktioniert hat als diese. Ich kann Dir den gesamten Code auch gerne mal zum durchchecken übergeben. Bestimmt kan man den wesentlich verbessern.

05.02.2021 - 11:39 Uhr

@Abt, sieht interessant aus! Das werde ich mir vornehmen, bin allerdings punkto Dienste und Worker noch ziemlich grün hinter den Ohren.

05.02.2021 - 11:31 Uhr

Hallo,

erst mal Danke für die Hinweise. Es ist so, dass das näturlich nicht der ganze Code ist. Dabei handelt es sich nur um einen kleinen Ausschnitt. Ansonsten wäre das zu unübersichtlich. Es geht nur um die grundlegende Starterei als Service, weniger um den Inhalt von DataProcessor, aber ja, da gibt es sicher Verbesserungspotenzial. Ich habe das noch mit Always Up getestet und da läuft das Ganze problemlos, zumindest lokal. Wie es sich jetzt über Always Up auf einem WinServer2019 verhält wird sich in Kürze zeigen. @Palladin007, ich könnte mir vorstellen, dass das mit dem "Port und Pfad im Windows mit Admin-Rechten registrieren" das Problem sein könnte, falls es nun auf dem Server nicht korrekt laufen sollte. Wo und wie müssten diese Rechte eingetragen werden? Registry?

05.02.2021 - 08:20 Uhr

Hallo Allerseits

Ich habe eine korrekt funktionierende Konsolenapplikation so umgebaut, dass sie sowohl auf Konsole als auch als WindowsDienst auf einem Server im Hintergrund läuft, bzw. laufen sollte. Als Konsolenanwendung läuft sie nach wie vor, als Dienst jedoch nicht korrekt.

Das Ganze wurde nach dieser Anleitung erstellt: https://www.debuggershub.com/dotnet-console-app-as-a-windows-service/

Ich denke es liegt daran, dass der verwendete DataProcessor über den Timer alle 10 Sec. wiederholt aufgerufen wird. Meine Frage ist also: Wie wird der Aufruf hier korrekt gemacht?


class Program
{
    public class DataProcessor
    {
        private static Thread n_server;
        private static Thread n_send_server;
        private static TcpClient client;
        private static TcpListener listener;
        private static Socket socket;
        private static byte[] bufferReceive = new byte[4096];
        private static byte[] bufferSend = new byte[4096];
        private static string XMLMessage;
        private static string CSVMessage;
        private static string XMLCallback;

        internal void Execute()
        {
            try
            {
                n_server = new Thread(new ThreadStart(Server));
                n_server.IsBackground = true;
                n_server.Start();
                Console.WriteLine("MessageReceiver started");
                TimerTranslate.TimerTranslateStart();
            }
            catch (Exception ex)
            {
                StreamWriter Filelog = new StreamWriter(ConfigurationManager.AppSettings.Get("logs") + "\\log.txt", append: true);
                Filelog.Write(DateTime.Now + " *** " + ex.Message + Environment.NewLine);
                Filelog.Close();
            }

            public static void Server()
            {
                listener = new TcpListener(IPAddress.Any, Convert.ToInt32(ConfigurationManager.AppSettings.Get("port")));
                listener.Start();

                try
                {
                    socket = listener.AcceptSocket();
                    if (socket.Connected)
                    {
                        Console.WriteLine("Client connected on: " + socket.RemoteEndPoint.ToString());
                    }
                    while (true)
                    {
                        int length = socket.Receive(bufferReceive);
                        if (length > 0)
                        {
                            XMLMessage = (Encoding.UTF8.GetString(bufferReceive, 0, length));
                            Console.WriteLine("XML_Message received successfully");
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("A client has disconnected");

                    StreamWriter Filelog = new StreamWriter(ConfigurationManager.AppSettings.Get("logs") + "\\log.txt", append: true);
                    Filelog.Write(DateTime.Now + " " + ex.Message + Environment.NewLine);
                    Filelog.Close();
                }
                finally
                {
                    listener.Stop();
                    n_server = new Thread(new ThreadStart(Server));
                    n_server.IsBackground = true;
                    n_server.Abort();

                    if (socket != null)
                    {
                        n_server = new Thread(new ThreadStart(Server));
                        n_server.IsBackground = true;
                        n_server.Start();

                        Console.Clear();
                        Console.WriteLine("MessageReceiver started, waitig for next connection...");
                    }
                }
            }

            private static void send()
            {
                if (socket != null)
                {
                    bufferSend = Encoding.UTF8.GetBytes(XMLCallback);
                    socket.Send(bufferSend);
                }
                else
                {
                    try
                    {
                        if (client.Connected)
                        {
                            bufferSend = Encoding.UTF8.GetBytes(XMLMessage);
                            NetworkStream nts = client.GetStream();
                            if (nts.CanWrite)
                            {
                                nts.Write(bufferSend, 0, bufferSend.Length);
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine("Error : " + ex.Message);

                        StreamWriter File = new StreamWriter(ConfigurationManager.AppSettings.Get("logs") + "\\log.txt", append: true);
                        File.Write(DateTime.Now + " *** " + ex.Message + Environment.NewLine);
                        File.Close();
                    }
                    finally
                    {
                        //client.Close();                    
                    }
                }
            }
        }
    }
}

Die MainClass


public class MainClass
{
    static void Main(string[] args)
    {            
        if ((!Environment.UserInteractive))
        {
            MainClass.RunAsAService();
        }
        else
        {
            if (args != null && args.Length > 0)
            {
                if (args[0].Equals("-i", StringComparison.OrdinalIgnoreCase))
                {
                    SelfInstaller.InstallMe();
                }
                else
                {
                    if (args[0].Equals("-u", StringComparison.OrdinalIgnoreCase))
                    {
                        SelfInstaller.UninstallMe();
                    }
                    else
                    {
                        Console.WriteLine("Invalid argument!");
                    }
                }
            }
            else
            {
                MainClass.RunAsAConsole();
            }
        }
    }

    static void RunAsAConsole()
    {
        DataProcessor dataProcessor = new DataProcessor();
        dataProcessor.Execute();
    }

    static void RunAsAService()
    {
        ServiceBase[] servicesToRun = new ServiceBase[]
        {
            new MessageReceiverService()
        };
        ServiceBase.Run(servicesToRun);
    }            
}

Die ServiceBase hier wird über den Timer gestartet. Wenn ich hier dataProcessor.Execute(); direkt verwende läuft das Programm etwas weiten, zeigt aber die gleichen Fehler wie unten beschrieben.


partial class MessageReceiverService : ServiceBase
{
    private static Timer aTimer;

    public MessageReceiverService()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        aTimer = new Timer(10000); 
        aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        aTimer.Enabled = true;
    }

    private static void OnTimedEvent(object source, ElapsedEventArgs e)
    {
        DataProcessor dataProcessor = new DataProcessor();
        dataProcessor.Execute();
    }

    protected override void OnStop()
    {
        aTimer.Stop();
    }
}

Die Fehlermeldungen bei Start ist "The handle is invalid" anstatt "MessageReceiver started"

In den Logs findet sich dann folgendes:

Fehlermeldung:
Application: MessageReceiver.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.Net.Sockets.SocketException
at System.Net.Sockets.Socket.DoBind(System.Net.EndPoint, System.Net.SocketAddress)
at System.Net.Sockets.Socket.Bind(System.Net.EndPoint)
at System.Net.Sockets.TcpListener.Start(Int32)
at MessageReceiver.DataProcessor.Server()
at System.Threading.ThreadHelper.ThreadStart_Context(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object)
at System.Threading.ThreadHelper.ThreadStart()

24.01.2021 - 11:42 Uhr

Hi cbpanther1

Sowas in der Art könnet helfen.

private void button1_Click(object sender, EventArgs e)
{
      Form1 frm1 = new Form1();
      this.Hide();

      System.Threading.Thread.Sleep(10000); //ms 10000 = 10 sec.
          
      this.Show();
}
21.01.2021 - 18:26 Uhr

Hallo Palladin007

Genau, vielen Dank für die Hinweise! Ich bin mit dem Umbau bereits auf der Zielgerade. Nur mit den Timern hatte ich noch einiges zu tun. Die lassen sich jetzt aber auch schon beliebig starten und stoppen. Für die Settings habe ich mich für die XML (System.Configuration) Variante entschieden, da die Einstellungen ja auch über die Laufzeit hinaus verfügbar sein sollen.

21.01.2021 - 14:06 Uhr

Ich sage mal recht herzlichen Dank!! Ich denke es zeichnet sich dadurch langsam ein Weg ab den ich gehen kann, vielleicht schneller als gedacht. Es muss auf jeden Fall eine Neuentwicklung her. Und wenn ich diesen Weg schon gehe, stellt sich noch eine weitere Herausforderung, denn wenn schon, dann auch gleich mit integriertem ServiceController zum starten und stopen.

21.01.2021 - 11:43 Uhr

Ist das über XML am besten zu lösen? Oder gibt es eine besser Empfehlung?

21.01.2021 - 11:36 Uhr

Hallo T-Virus

Ja, das ist ja der Hacken an der Sache. In der FormApp wurden z.B Properies.Settings usw. verwendet damit der User Einstellungen und Pfade definieren kann. Um das über eine KonsolenApp zu realisieren fehlt mir wie gesagt die Erfahrung damit. Klar bekomme ich das irgendwann dann auch irgendwie mal hin, es sollte aber leider (wie immer) auch noch schnell gehen.

Das Problem ist nicht das anpassen des Codes ansich, der ist eigentlich bereits Konsolenkonform. Das Problem ist mehr der Bereich der Settings. Wenn eine Einstellungsmaske für Speicherpfade und Dateinamen so erstellt werden kann, dass diese über Konsolenbefehle aufgerufen, abgefüll und gespeichert werden kann, habe ich das Problem gelöst. Dann ist der Rest eigentlich keine Hexerei mehr.

In der FormApp werden über eine Maske alle Daten erfasst und in den Properties.Settings.Default gespeichert. Die App greift dann darauf zu. Lediglich dieser Teil müsste neu, Konsolenkonform realisiert werden. Welcher Weg ist dafür der Sinnfollste ?

21.01.2021 - 09:57 Uhr

Hallo Duesmannr

Danke für die Antwort! Ich habe den Ausgabetyp in den Einstellungen mal von Windows-Anwendung auf Konsolenanwendung umgestellt. Hat das nicht die selbe Auswirkung? Danach erscheint das GUI allerdings nach wie vor.

Wie sieht die zu implementierende Logik genau aus? Habe mal Application.EnableVisualStyles(); auskommentiert, ohne Erfolg. In der "Program.cs" einer Konsolenanwendung erkenne ich natürlich den Unterschied zur Form "Main.cs", mit der "public partial class Main : Form" und der "InitializeComponent();" Meinst Du ich soll das analog "Program.cs" der Konsolenapplikation umbauen?

21.01.2021 - 09:05 Uhr

Hallo Allerseits

Für ein Businessprojek habe ich eine WINForm Applikation erstellt. Diese empfängt über TCPSocket Abrechnungen von einem Client die als XML deherkommen, übersetzt sie in CSV und speichert diese dann in ferei definierbaren Pfaden ab.

Nun soll diese Applikation neu auf einem WinServer 2019 laufen. Das Problem ist nun, dass die App nicht als Service ausgeführt werden kann, und dass ab dem Build 1803 keine interaktiven Dienste mehr zugelassen sind. Somit kann die App auch nicht mehr über z.B AlwaysUp betrieben werden.

Einzige Lösung die ich sehe, ist die Konvertierung in eine Konsolen Applikation.

Da ich aber bislang mit C# nur WINForm Applikation entwickelt habe und mit Konsole eher wenig Erfahrung habe, die Frage an Euch, bzw. an den Mann, der das im Handumdrehen erledigen kann:

Hat jemand etwas Kapazität, genügend Erfahrung und Lust auf einen Nebenverdienst ?

Wäre super wenn sich da jemand finden würde!