Skip to content

Commit 735ba00

Browse files
committed
New option for automatically recovering session after a forced system reboot
Unavoidable system reboots will always (I assume) be a part of Windows. PhotoDemon can now survive such a reboot. If PD is open with one or more unsaved changes, and the system forces a reboot, PD will now auto-open after the reboot and restore your previous session, with all work intact. Note that if you do *not* have any unsaved changes, PD will still auto-start after the reboot but will not re-load any images. I still need to solve this (it's tied to PD's AutoSave system, which tries not to bug the user with prompts unless work risks being lost), presumably by subclassing WM_ENDSESSION and modifying PD's AutoSave triggers accordingly.
1 parent 0b093be commit 735ba00

File tree

7 files changed

+118
-17
lines changed

7 files changed

+118
-17
lines changed

Forms/Dialog_AutosaveFound.frm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ Public Sub ShowDialog()
168168

169169
'Load command button images
170170
Dim buttonIconSize As Long
171-
buttonIconSize = FixDPI(32)
171+
buttonIconSize = Interface.FixDPI(32)
172172
cmdOK.AssignImage "generic_ok", , buttonIconSize, buttonIconSize
173173
cmdCancel.AssignImage "generic_cancel", , buttonIconSize, buttonIconSize
174174

Forms/Tools_Options.frm

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,28 @@ Begin VB.Form FormOptions
335335
FontSize = 12
336336
ForeColor = 5263440
337337
End
338+
Begin PhotoDemon.pdLabel lblTitle
339+
Height = 285
340+
Index = 22
341+
Left = 0
342+
Top = 5520
343+
Width = 8115
344+
_ExtentX = 14314
345+
_ExtentY = 503
346+
Caption = "session restore"
347+
FontSize = 12
348+
ForeColor = 5263440
349+
End
350+
Begin PhotoDemon.pdCheckBox chkSystemReboots
351+
Height = 330
352+
Left = 180
353+
TabIndex = 42
354+
Top = 5880
355+
Width = 7920
356+
_ExtentX = 13970
357+
_ExtentY = 582
358+
Caption = "automatically restore sessions interrupted by system updates or reboots"
359+
End
338360
End
339361
Begin PhotoDemon.pdContainer picContainer
340362
Height = 6720
@@ -1168,6 +1190,10 @@ Private Sub cmdBarMini_OKClick()
11681190

11691191
UserPrefs.SetPref_Boolean "Loading", "ExifAutoRotate", chkLoadingOrientation.Value
11701192

1193+
'Restore after reboot behavior requires an immediate API to de/activate
1194+
UserPrefs.SetPref_Boolean "Loading", "RestoreAfterReboot", chkSystemReboots.Value
1195+
OS.SetRestartRestoreBehavior chkSystemReboots.Value
1196+
11711197
'Saving preferences
11721198
SetProgBarVal 3
11731199

@@ -1388,6 +1414,7 @@ Private Sub LoadAllPreferences()
13881414
chkMetadataUnknown.Value = UserPrefs.GetPref_Boolean("Loading", "Metadata Extract Unknown", False)
13891415
chkMetadataBinary.Value = UserPrefs.GetPref_Boolean("Loading", "Metadata Extract Binary", False)
13901416
chkLoadingOrientation.Value = UserPrefs.GetPref_Boolean("Loading", "EXIF Auto Rotate", True)
1417+
chkSystemReboots.Value = UserPrefs.GetPref_Boolean("Loading", "RestoreAfterReboot", False)
13911418

13921419
'Saving preferences
13931420
chkConfirmUnsaved.Value = g_ConfirmClosingUnsaved
@@ -1525,7 +1552,8 @@ Private Sub Form_Load()
15251552
chkMetadataUnknown.AssignTooltip "Some camera manufacturers store proprietary metadata tags inside image files. These tags are not generally useful to humans, but PhotoDemon can attempt to extract them anyway."
15261553
chkMetadataBinary.AssignTooltip "By default, large binary tags (like image thumbnails) are not processed. Instead, PhotoDemon simply reports the size of the embedded data. If you require this data, PhotoDemon can manually convert it to Base64 for further analysis."
15271554
chkLoadingOrientation.AssignTooltip "Most digital photos include rotation instructions (EXIF orientation metadata), which PhotoDemon will use to automatically rotate photos. Some older smartphones and cameras may not write these instructions correctly, so if your photos are being imported sideways or upside-down, you can try disabling the auto-rotate feature."
1528-
1555+
chkSystemReboots.AssignTooltip "If your PC reboots while PhotoDemon is running, PhotoDemon can automatically restore your previous session."
1556+
15291557
'Saving prefs
15301558
chkConfirmUnsaved.AssignTooltip "By default, PhotoDemon will warn you when you attempt to close an image with unsaved changes."
15311559

Modules/AutosaveEngine.bas

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ Attribute VB_Name = "Autosaves"
33
'Image Autosave Handler
44
'Copyright 2014-2021 by Tanner Helland
55
'Created: 18/January/14
6-
'Last updated: 14/September/20
7-
'Last update: rely on new Mutex class for detecting parallel sessions and modifying behavior accordingly
6+
'Last updated: 18/October/21
7+
'Last update: use autosave engine to (silently) restore previous sessions after forced system reboot
88
'
99
'PhotoDemon's Autosave engine is closely tied to the pdUndo class, so some understanding of that class is necessary
1010
' to appreciate how this module operates.
@@ -134,20 +134,46 @@ Public Sub InitializeAutosave()
134134
Dim userWantsAutosaves As VbMsgBoxResult
135135
Dim listOfFilesToSave() As AutosaveXML
136136

137-
userWantsAutosaves = DisplayAutosaveWarning(listOfFilesToSave)
137+
'If this is an auto-recovery session after a forced system reboot, and the user allows
138+
' us to auto-recover their previous session, skip displaying any UI and just jump
139+
' right to restoration.
140+
Dim cmdParams As pdStringStack, showUI As Boolean
141+
showUI = True
142+
143+
If OS.CommandW(cmdParams, True) Then
144+
145+
Dim i As Long
146+
For i = 0 To cmdParams.GetNumOfStrings - 1
147+
If Strings.StringsEqual(cmdParams.GetString(i), "/system-reboot", True) Then
148+
showUI = False
149+
Exit For
150+
End If
151+
Next i
152+
153+
'If the autosave data comes from a perfectly good session interrupted by a forced system reboot,
154+
' restore all work silently.
155+
If (Not showUI) Then
156+
Dim tmpXMLCount As Long
157+
Autosaves.GetXMLAutosaveEntries listOfFilesToSave, tmpXMLCount
158+
userWantsAutosaves = vbYes
159+
End If
160+
161+
End If
162+
163+
If showUI Then userWantsAutosaves = DisplayAutosaveWarning(listOfFilesToSave)
138164

139165
'If the user wants to restore old Autosave data, do so now.
140166
If (userWantsAutosaves = vbYes) Then
141167

142168
'listOfFilesToSave contains the list of Autosave files the user wants restored.
143169
' Hand them off to the autosave handler, which will load and restore each file in turn.
144170
Autosaves.LoadTheseAutosaveFiles listOfFilesToSave
145-
SyncInterfaceToCurrentImage
171+
Interface.SyncInterfaceToCurrentImage
146172

147173
Else
148174

149-
'The user has no interest in recovering AutoSave data. Purge all the entries we found, so they don't show
150-
' up in future AutoSave searches.
175+
'The user has no interest in recovering AutoSave data. Purge all the entries we found,
176+
' so they don't show up in future AutoSave checks.
151177
Autosaves.PurgeOldAutosaveData
152178

153179
End If

Modules/Loading.bas

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -823,14 +823,22 @@ Public Function LoadMultipleImageFiles(ByRef srcList As pdStringStack, Optional
823823

824824
Dim tmpFilename As String
825825
Do While srcList.PopString(tmpFilename)
826-
If LoadFileAsNewImage(tmpFilename, , updateRecentFileList, True, False) Then
827-
numSuccesses = numSuccesses + 1
828-
Else
829-
If (Len(tmpFilename) <> 0) Then
830-
numFailures = numFailures + 1
831-
brokenFiles = brokenFiles & Files.FileGetName(tmpFilename) & vbCrLf
826+
827+
'The command-line may include other switches besides just filenames. Ensure target file
828+
' exists before forwarding it to the loader.
829+
If Files.FileExists(tmpFilename) Then
830+
831+
If LoadFileAsNewImage(tmpFilename, , updateRecentFileList, True, False) Then
832+
numSuccesses = numSuccesses + 1
833+
Else
834+
If (LenB(tmpFilename) <> 0) Then
835+
numFailures = numFailures + 1
836+
brokenFiles = brokenFiles & Files.FileGetName(tmpFilename) & vbCrLf
837+
End If
832838
End If
839+
833840
End If
841+
834842
Loop
835843

836844
'Make sure we loaded at least one image from the original list

Modules/Main.bas

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,11 @@ Public Function ContinueLoadingProgram(Optional ByRef suspendAdditionalMessages
288288
PDDebug.LogAction "(Note: this PD instance is unique; no other instances discovered.)"
289289
End If
290290

291+
'While here, check another start-up related user perference. Forced system reboots are
292+
' an ever-more-annoying issue on modern versions of Window. PhotoDemon can automatically
293+
' recover sessions interrupted by reboots.
294+
If UserPrefs.GetPref_Boolean("Loading", "RestoreAfterReboot", False) Then OS.SetRestartRestoreBehavior True
295+
291296

292297
'*************************************************************************************************************************************
293298
' Initialize the plugin manager and load any high-priority plugins (e.g. those required to start the program successfully)

Modules/OS.bas

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ Attribute VB_Name = "OS"
33
'OS-specific specific features
44
'Copyright 2013-2021 by Tanner Helland
55
'Created: 21/November/13
6-
'Last updated: 18/July/17
7-
'Last update: migrate various OS-specific bits to this module
6+
'Last updated: 18/October/21
7+
'Last update: new helper function for requesting app restart after forced system reboots
88
'
99
'Newer Windows versions expose some neat features (like progress bars overlaying the taskbar), and PhotoDemon
1010
' tries to make use of them when relevant. Similarly, some OS-level features are not easily mimicked from within VB
@@ -287,6 +287,7 @@ Private Declare Function LocalFree Lib "kernel32" (ByVal hMem As Long) As Long
287287
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long
288288
Private Declare Function ProcessFirst Lib "kernel32" Alias "Process32FirstW" (ByVal hSnapShot As Long, ByVal ptrToUProcess As Long) As Long
289289
Private Declare Function ProcessNext Lib "kernel32" Alias "Process32NextW" (ByVal hSnapShot As Long, ByVal ptrToUProcess As Long) As Long
290+
Private Declare Function RegisterApplicationRestart Lib "kernel32" (ByVal pwzCommandline As Long, ByVal dwFlags As Long) As Long
290291
Private Declare Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
291292
Private Declare Function VirtualFree Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long
292293

@@ -909,6 +910,39 @@ Public Function RAM_SystemTotal() As String
909910

910911
End Function
911912

913+
'PhotoDemon can automatically recover sessions interrupted by forced system reboots. This function
914+
' can be called to enable or disable this behavior (but note that there are some restrictions;
915+
' for example, the app must be running for ~60 seconds before it's eligible for recovery. This is
916+
' an OS-level restriction.) Note also that this is only available on Vista+, but PD actually limits
917+
' it to Win 7+ due to a lack of testing on Vista.
918+
Public Sub SetRestartRestoreBehavior(ByVal allowToRestore As Boolean)
919+
920+
'Not available on XP. Possibly available on Vista but I no longer maintain a Vista VM, so I'm
921+
' not enabling until someone can test for me.
922+
If OS.IsWin7OrLater Then
923+
924+
'Possible flags for this API; not all are used by PD
925+
Const RESTART_NO_CRASH As Long = 1 'Do not restart the process if it terminates due to an unhandled exception.
926+
Const RESTART_NO_HANG As Long = 2 'Do not restart the process if it terminates due to the application not responding.
927+
'Const RESTART_NO_PATCH As Long = 4 'Do not restart the process if it terminates due to the installation of an update.
928+
'Const RESTART_NO_REBOOT As Long = 8 'Do not restart the process if the computer is restarted as the result of an update.
929+
Const RESTART_NONE As Long = &HF& 'All the above; used to disable restarts completely after previously requesting them
930+
931+
'This function can also pass command-line parameters; we use an internal flag that bypasses
932+
' the AutoSave Recovery prompt.
933+
Dim cmdParams As String
934+
cmdParams = " /system-reboot"
935+
936+
If allowToRestore Then
937+
RegisterApplicationRestart StrPtr(cmdParams), RESTART_NO_CRASH Or RESTART_NO_HANG
938+
Else
939+
RegisterApplicationRestart ByVal 0&, RESTART_NONE
940+
End If
941+
942+
End If
943+
944+
End Sub
945+
912946
'If desired, a custom state can be set for the taskbar. (Normally this is handled by the SetTaskbarProgressValue function.)
913947
Public Function SetTaskbarProgressState(ByVal tbpFlags As PD_TaskBarProgress) As Long
914948
If WIN7_FEATURES_ALLOWED Then SetTaskbarProgressState = CallInterface(m_taskbarObjHandle, SetProgressState_, 2, FormMain.hWnd, tbpFlags)

PhotoDemon.vbp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ Description="PhotoDemon Photo Editor"
488488
CompatibleMode="0"
489489
MajorVer=8
490490
MinorVer=9
491-
RevisionVer=925
491+
RevisionVer=929
492492
AutoIncrementVer=1
493493
ServerSupportFiles=0
494494
VersionComments="Copyright 2000-2021 Tanner Helland - photodemon.org"

0 commit comments

Comments
 (0)