Programm mit Check auf Updates - Threading

  • C#

Es gibt 2 Antworten in diesem Thema. Der letzte Beitrag () ist von MasterQ.

    Programm mit Check auf Updates - Threading

    Moin,

    was mit Click-Once von alleine geht, muss bei der Veröffentlichung in einem Install-Ordner händisch gemacht werden: Das Suchen nach Updates und dann bei Bedarf dem Installieren der neuen Dateien und dem Restart der Anwendung.

    Man kann dazu viel im Netz lesen und auch leicht selber drauf kommen wie man sowas macht. Doch der Updatevorgang bricht bei mir unvermittelt hab.

    Ich arbeite mit WPF und .NET 5. In der StartUp-Eventroutine kommt folgendes:

    C#-Quellcode

    1. if (VersionLocal != VersionCurrent)
    2. {
    3. List<string> msg = new();
    4. msg.Add(CaL.Properties.Translate.UpdateVerfügbar);
    5. msg.Add($"{VersionLocal} => {VersionCurrent}");
    6. msg.Add(CaL.Properties.Translate.Updatefortsetzen);
    7. Err.Set(string.Join(Environment.NewLine, msg), GetType().Name, Err.ErrorLevels.Informal);
    8. if (!Err.Show())
    9. {
    10. core.Update();
    11. }
    12. Shutdown();
    13. }


    Dann in einer DLL:

    C#-Quellcode

    1. public static void Update(string path = null)
    2. {
    3. Task.Run(() =>
    4. {
    5. if (string.IsNullOrEmpty(path))
    6. path = Process.GetCurrentProcess().MainModule.FileName;
    7. Process cal = new();
    8. cal.StartInfo.FileName = path;
    9. cal.StartInfo.UseShellExecute = false;
    10. cal.StartInfo.CreateNoWindow = true;
    11. try
    12. {
    13. Thread.Sleep(2000);
    14. Install();
    15. cal.Start();
    16. }
    17. catch (Exception ex)
    18. {
    19. Err.Set(ex.Message, className, Err.ErrorLevels.Critical);
    20. Err.Show();
    21. }
    22. });
    23. }


    C#-Quellcode

    1. public static void Install(string sourceDir = null, string targetDir = null)
    2. {
    3. if (string.IsNullOrEmpty(sourceDir)) sourceDir = CaLSourceDir;
    4. if (string.IsNullOrEmpty(targetDir)) targetDir = CaLRootDir;
    5. if (!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir);
    6. foreach (var item in Directory.GetFiles(sourceDir))
    7. {
    8. var target = Path.Combine(targetDir, Path.GetFileName(item));
    9. try
    10. {
    11. Debug.Print($"{item} => {target}");
    12. if (File.Exists(target)) File.Delete(target);
    13. File.Copy(item, target);
    14. }
    15. catch (Exception ex)
    16. {
    17. Debug.Print(ex.Message);
    18. }
    19. }
    20. }


    In der Routine Install bricht alles ab. Ich vermute mal, dass dann der Task beendet wird, wenn in der StartUp-Eventroutine das Shutdown ausgeführt wird. Ich dachte eigentlich, der neu erzeuge Process wäre vom Hauptthread unabhängig und läuft bis zum Ende auch wenn der Hauptthread vorher beendet wird.

    Wie kriege ich es denn sonst hin, dass ein unabhängiger Process/Thread ausgelöst wird, der auch weiter läuft wenn der erzeugende Thread beendet ist? Da die Kopiermethode auch die exe überschreiben muss, muss die Anwendung beendet sein bevor das versucht wird. Ansonsten erhalte ich eine Zugriffsverweigerung weil die exe natürlich noch belegt ist.

    Unter Winforms gibt es eine Restart Funktion in Application. In WPF gibt es die nicht. Ich möchte aber nicht wegen einer einzigen Routine noch WinForms einbinden müssen. Und das Problem mit dem Überschreiben der exe wäre ich damit auch nicht los

    Gruß

    MQ



    Das Ganze ist doch etwas komplexer als gedacht.

    Wenn ich den Shutdown ausblende, dann sollten am Ende zwei Instanzen des Programms laufen oder aber eine Fehlermeldung beim Kopieren kommen.

    Nichts dergleichen. Ich erhalten letztendlich ein Meldung "Das Stub erhielt falsche Daten"! öhmm.

    1) Also, der erzeugte Process stirbt, wenn der erstellende Process beendet wird Der erzeugte Task stirbt, wenn der erzeugende Prozess beendet wird. Und das ist normal.
    • Somit dauert der erzeugte Task zu lange und kommt gar nicht bis nicht zum Prozessstart bevor im Papa den Stecker zieht.

    2) Um mit Process.Start() im Firmennetz eine Anwendung zu starten, benötige ich den Nutzernamen und Domäne, was kein Problem darstellt, aber auch das Passwort. Andernfalls ist der Prozess an der Domäne nicht angemeldet und hat keinerlei Rechte im Netz. Das is bissl blöd weil ich das Passwort nicht programmtechnisch abfragen kann. Ich muss wohl noch ein wenig mit den Startinfo rumspielen.

    Das las sich alles so einfach in den Foren. ;(

    Beiträge zusammengefügt. ~Thunderbolt

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Thunderbolt“ ()

    Vielleicht wäre es besser, eine Update-Routine zu schreiben, die losgelöst von deinem Programm ist (gerade, weil Firmennetzwerke so etwas strikt unterbinden - meist aber auch aus gutem Grund) -> Quasi ein zweites Projekt.

    Dieses kompilierst du und fügst es deiner Update-Packung mit bei. Beides versiehst du in einem Archiv und lädst es an die entsprechende Stelle hoch.
    Nun greift dein Hauptprogramm nicht auf einen Prozess zu, sondern lädt beim "Update" das Update-Archiv (wenn Update-Info > Hauptprogramm), entpackt es und führt die Update-Routine aus -> mit eigener Prozess-ID als Start-Args.

    Im UpdateProgramm wartest du, bis der Prozess nicht mehr läuft (oder alternativ killst ihn direkt) und führst nach einem kurzen Timeout (Um auch Reste des Prozesses kaltgestellt zu wissen und IOs geschlossen wurden) den Updatecode hier aus.
    Das hätte den Vorteil, dass du das nicht über das Netzwerk machen musst, sondern lokal über den PC.
    ja, das könnte die Lösung sein. Ich habe gestern noch lange drüber nachgedacht und bin zu einer ähnlichen Überlegung gekommen wie du. Bisher wurde das mit einem DOS-Script auf der Lommandozeile gelöst. Das hat gut geklappt. Das Integrieren des Updates in die selbe Anwendung stößt an die oben genannten Grenzen und ist wohl keine Lösung.

    --EDIT--
    Ich habs doch noch hinbekommen.

    Der neue Process wird mit folgenden Einstellungen (StartInfo) gestartet:

    C#-Quellcode

    1. Process cal = new();
    2. cal.StartInfo.CreateNoWindow = false;
    3. cal.StartInfo.ErrorDialog = true;
    4. cal.StartInfo.FileName = path;
    5. cal.StartInfo.UseShellExecute = true;
    6. cal.StartInfo.WorkingDirectory= Path.GetDirectoryName(path);


    Nur wenn man in der StartInfo einen UserName angibt, versucht sich der neue Prozess an der Domain anzumelden. Bleibt der UserName leer, dann startet der Prozess in der vorhandenen Anmeldung.

    Und ich habe den Kopiervorgang aus dem Task raus. Komischerweise kommt keine Fehlermeldung mehr, wenn ich die exe-Datei der laufenden Instanz des Programmes überschreibe. Warum auch immer.

    C#-Quellcode

    1. if (VersionLocal != VersionCurrent)
    2. {
    3. List<string> msg = new();
    4. msg.Add(CaL.Properties.Translate.UpdateVerfügbar);
    5. msg.Add($"{VersionLocal} => {VersionCurrent}");
    6. msg.Add(CaL.Properties.Translate.Updatefortsetzen);
    7. Err.Set(string.Join(Environment.NewLine, msg), GetType().Name, Err.ErrorLevels.Informal);
    8. if (!Err.Show())
    9. {
    10. core.Install();
    11. core.Restart();
    12. }
    13. Shutdown();
    14. }


    Alles gut!

    Gruß

    MQ

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „MasterQ“ ()