Windowsshutdown-Event



  • Hallöchen
    ich fange mit den entsprechenden Event das Ausloggen bzw. Herunterfahren
    meines Computers ab, ich wollte nun in dem entsprechenden Event
    mein Daten speichern und mein Programm beenden, doch leider scheint mein
    speichern zu lange zu dauern und Windows killt meine Anwendung einfach.
    Somit ist in meinem SaveGame unbrauchbar. Was für Möglichkeiten hätte ich
    um das Herunterfahren solange zu verhindern, bis ich fertig bin mit dem speichern?

    Microsoft.Win32.SystemEvents.SessionEnding += this.WindowShutDownHandler;
    private void WindowShutDownHandler(object pSender, Microsoft.Win32.SessionEndingEventArgs pArgs)
        {
          LogInfoMessage("WindowShutDown_event");
          pArgs.Cancel = false;
          SaveData();            
        }
    

    Ich könnte jetzt die Cancel-Eigenschaft auf true setzten
    aber dann würde das speichern immer noch zu lange dauern
    bis Windows es merkt, dass es Abbrechen sollen bzw. warten soll.
    Und wenn ich das speichern Komplett aus den Event entferne und
    das Cancle auf true stetze, wie könnte ich dann das Speichern-tickern?

    private void WindowShutDownHandler(object pSender, Microsoft.Win32.SessionEndingEventArgs pArgs)
        {
          LogInfoMessage("WindowShutDown_event");
          pArgs.Cancel = true;
         // SaveData();            
        }
    

    Grüße



  • Naja, du kannst Cancel = true setzen, und dann ein BeginInvoke auf deine Speicherfunktion machen.
    Nur wird dann vermutlich der Shutdown ganz abgebrochen - könntest du aber mal ausprobieren.

    Wie bei einem Logout/Shutdown "um mehr Zeit bittet" weiss ich ehrlich gesagt nicht. Weiss nichtmal ob das überhaupt geht.



  • Morgen,
    ich hab es jetzt mal so umgebaut, dass ich die Speicherfunktion Invoke
    und nach dem Speichern, falls ein ShutdownSignal gekommen ist mein Anwendung
    mit Application.Exit beende.

    ...
    LogErrorMessage("WindowShutDown_event_start");
    pArgs.Cancel = true;
    mAfterSaveExit = true;
    EventSave.BeginInvoke(null, null);
    LogErrorMessage("WindowShutDown_event_ende");
    ...
    

    Mein Log beim Herunterfahren sieht dann so aus:

    Information: 0 : WindowShutDown_event
    Information: 0 : WindowShutDown_event_ende
    Information: 0 : SaveData_Start

    Ein Save_Ende kommt leider nicht, die Anwendung wird wieder einfach gekillt
    und das pArgs.Cancel=true wird scheinbar ignoriert.

    Es steht noch nicht mal da, dass Windows beim herunterfahren auf die Anwendung wartet, sowie das bei Steam ist, da wird ja auch gewartet bis Steam komplett gesynct hat.
    Grüße



  • Hihi
    Hätte mir auch früher einfallen können.
    Windows versucht beim Shutdown deine Anwendung ganz normal zu schliessen - also so mit Hauptfenster zumachen.
    Fängst du das auch ab?



  • Hi,
    ja ich hab beide drin.

    Mit den letzten Änderungen
    die Speicherfunktion zu Invoken hatte die
    Anwendung auch ein paar mal geschafft zu speichern,
    Aber ich sag mal vielleicht 2 von 10 versuchen 😉

    Finde auch komisch das Windows 7 scheinbar gar nicht
    auf das cancel = true reagiert. Das Herunterfahren
    bzw. Abmelden kann ich so scheinbar nicht abbrechen.

    Jedenfalls hab ich mir jetzt in mein HauptFenster
    die

    protected override void DestroyHandle()
    {}
    

    überschrieben und da gespeichert, dass lief bis jetzt immer durch
    und hat keine fehlerhaften Speicherstände erzeugt.
    Muss ich jetzt erst mal weiter beobachten 😃
    Grüße



  • Du wirst mit einem Simplen .NET Programm auch kein Herrunterfahren des PCs verhindern koennen. Das waere ja fatal wenn sowas geht 😃



  • Naja abbrechen wollte ich es ja auch nicht,
    ich wollte halt nur sagen "Hey Windows ich brauch noch ne Sekunde,um meine
    Daten zu speichern!";) Aber das scheint in diesen Events wohl nicht möglich
    zu sein. Ich finde auch irgendwie komisch das beim DestroyHandle() das Speichern einfach durchläuft, wird ja schließlich erst nach den Events gerufen.
    Ich vermute fasst, dass bei den Event meine Threads im Hintergrund noch laufen
    und die vielleicht beim speichern nen Lock machen und Windows wird es dann zu bunt und killt die Threads einfach alle.
    Müsste ich vielleicht noch mal prüfen.


  • Administrator

    Es ist möglich, ihr solltet nur endlich anfangen weniger rumzuspekulieren und die Dokumentation zum Shutdown zu lesen 🙄

    Dokumentation (Änderungen von XP zu Vista/7)
    Kurzfassung

    Zusammenfassung:
    1. Shutdown verhindern geht gar nicht, da der User diesen erwzingen kann.
    2. Konsolenanwendungen können einen Shutdown nicht verzögern.
    3. Fensterapplikationen benötigen ein sichtbares Toplevel-Window, sonst wird die Applikation terminiert.
    4. Die Applikation muss reaktiv bleiben. Wenn man somit für etwas Zeit benötigt, muss man dies unbedingt in einem Thread ausserhalb der Windows-Message-Queue tun. Sonst wird die Applikation terminiert.
    5. Mit ShutdownBlockReasonCreate und ShutdownBlockReasonDestroy kann man dem User eine Nachricht anzeigen. Man sollte nie selber vom User Input erwarten, da dieser dies gar nicht mehr tun kann.

    [DllImport("User32.dll", CharSet = CharSet.Unicode)]
    private extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, string pwszReason);
    
    [DllImport("User32.dll", CharSet = CharSet.Unicode)]
    private static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);
    

    Unter WPF dürfte hier noch WindowInteropHelper hilfreich sein.

    Einfaches Testprogramm mit WPF:

    ////////////////////////////////////////////////////////////////////////////////
    // MainWindow.xaml.cs : C# file
    
    using System;
    using System.Runtime.InteropServices;
    using System.Windows;
    using System.Windows.Interop;
    using System.Windows.Threading;
    
    using Microsoft.Win32;
    
    namespace WpfTest
    {
      public partial class MainWindow
        : Window
      {
        [DllImport("User32.dll", CharSet = CharSet.Unicode)]
        private extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, string pwszReason);
    
        [DllImport("User32.dll", CharSet = CharSet.Unicode)]
        private static extern bool ShutdownBlockReasonDestroy(IntPtr hWnd);
    
        private readonly DispatcherTimer m_timer;
    
        private int m_counter;
    
        public MainWindow()
        {
          InitializeComponent();
    
          m_timer = new DispatcherTimer();
          m_timer.Tick += this.OnTimerTick;
          m_timer.Interval = new TimeSpan(0, 0, 1);
    
          SystemEvents.SessionEnding += this.OnSessionEnding;
    
          this.Closing += this.OnWindowClosing;
        }
    
        private void OnSessionEnding(object sender, SessionEndingEventArgs sessionEndingEventArgs)
        {
          if(m_counter < 20)
          {
            m_timer.Start();
    
            var helper = new WindowInteropHelper(this);
            ShutdownBlockReasonCreate(helper.Handle, "We want to test this message!");
    
            sessionEndingEventArgs.Cancel = true;
          }
        }
    
        private void OnTimerTick(object sender, EventArgs eventArgs)
        {
          ++m_counter;
    
          if(m_counter >= 20)
          {
            var helper = new WindowInteropHelper(this);
            ShutdownBlockReasonDestroy(helper.Handle);
            m_timer.Stop();
            Close();
          }
        }
    
        private void OnWindowClosing(object sender, System.ComponentModel.CancelEventArgs e)
        {
          if(m_counter < 20)
          {
            m_timer.Start();
            e.Cancel = true;
          }
        }
      }
    }
    

    (Edit: Kleine Änderung im Beispielcode, damit dieser ohne Änderung des XAML übernommen werden kann)

    Grüssli



  • Hi Dravere,
    vielen Dank für diese nette Zusammenfassung 🙂
    damit sollte ich mein Problem lösen können!
    Ich probiere es heute Abend gleich mal aus.
    Grüße


Anmelden zum Antworten