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_StartEin 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 versuchenFinde 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
dieprotected 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.
-
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)
KurzfassungZusammenfassung:
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. MitShutdownBlockReasonCreate
undShutdownBlockReasonDestroy
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