Skip to content

Commit

Permalink
New option for automatically recovering session after a forced system…
Browse files Browse the repository at this point in the history
… 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.
  • Loading branch information
tannerhelland committed Oct 18, 2021
1 parent 0b093be commit 735ba00
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Forms/Dialog_AutosaveFound.frm
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ Public Sub ShowDialog()

'Load command button images
Dim buttonIconSize As Long
buttonIconSize = FixDPI(32)
buttonIconSize = Interface.FixDPI(32)
cmdOK.AssignImage "generic_ok", , buttonIconSize, buttonIconSize
cmdCancel.AssignImage "generic_cancel", , buttonIconSize, buttonIconSize

Expand Down
30 changes: 29 additions & 1 deletion Forms/Tools_Options.frm
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,28 @@ Begin VB.Form FormOptions
FontSize = 12
ForeColor = 5263440
End
Begin PhotoDemon.pdLabel lblTitle
Height = 285
Index = 22
Left = 0
Top = 5520
Width = 8115
_ExtentX = 14314
_ExtentY = 503
Caption = "session restore"
FontSize = 12
ForeColor = 5263440
End
Begin PhotoDemon.pdCheckBox chkSystemReboots
Height = 330
Left = 180
TabIndex = 42
Top = 5880
Width = 7920
_ExtentX = 13970
_ExtentY = 582
Caption = "automatically restore sessions interrupted by system updates or reboots"
End
End
Begin PhotoDemon.pdContainer picContainer
Height = 6720
Expand Down Expand Up @@ -1168,6 +1190,10 @@ Private Sub cmdBarMini_OKClick()

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

'Restore after reboot behavior requires an immediate API to de/activate
UserPrefs.SetPref_Boolean "Loading", "RestoreAfterReboot", chkSystemReboots.Value
OS.SetRestartRestoreBehavior chkSystemReboots.Value

'Saving preferences
SetProgBarVal 3

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

'Saving preferences
chkConfirmUnsaved.Value = g_ConfirmClosingUnsaved
Expand Down Expand Up @@ -1525,7 +1552,8 @@ Private Sub Form_Load()
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."
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."
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."

chkSystemReboots.AssignTooltip "If your PC reboots while PhotoDemon is running, PhotoDemon can automatically restore your previous session."

'Saving prefs
chkConfirmUnsaved.AssignTooltip "By default, PhotoDemon will warn you when you attempt to close an image with unsaved changes."

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

userWantsAutosaves = DisplayAutosaveWarning(listOfFilesToSave)
'If this is an auto-recovery session after a forced system reboot, and the user allows
' us to auto-recover their previous session, skip displaying any UI and just jump
' right to restoration.
Dim cmdParams As pdStringStack, showUI As Boolean
showUI = True

If OS.CommandW(cmdParams, True) Then

Dim i As Long
For i = 0 To cmdParams.GetNumOfStrings - 1
If Strings.StringsEqual(cmdParams.GetString(i), "/system-reboot", True) Then
showUI = False
Exit For
End If
Next i

'If the autosave data comes from a perfectly good session interrupted by a forced system reboot,
' restore all work silently.
If (Not showUI) Then
Dim tmpXMLCount As Long
Autosaves.GetXMLAutosaveEntries listOfFilesToSave, tmpXMLCount
userWantsAutosaves = vbYes
End If

End If

If showUI Then userWantsAutosaves = DisplayAutosaveWarning(listOfFilesToSave)

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

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

Else

'The user has no interest in recovering AutoSave data. Purge all the entries we found, so they don't show
' up in future AutoSave searches.
'The user has no interest in recovering AutoSave data. Purge all the entries we found,
' so they don't show up in future AutoSave checks.
Autosaves.PurgeOldAutosaveData

End If
Expand Down
20 changes: 14 additions & 6 deletions Modules/Loading.bas
Original file line number Diff line number Diff line change
Expand Up @@ -823,14 +823,22 @@ Public Function LoadMultipleImageFiles(ByRef srcList As pdStringStack, Optional

Dim tmpFilename As String
Do While srcList.PopString(tmpFilename)
If LoadFileAsNewImage(tmpFilename, , updateRecentFileList, True, False) Then
numSuccesses = numSuccesses + 1
Else
If (Len(tmpFilename) <> 0) Then
numFailures = numFailures + 1
brokenFiles = brokenFiles & Files.FileGetName(tmpFilename) & vbCrLf

'The command-line may include other switches besides just filenames. Ensure target file
' exists before forwarding it to the loader.
If Files.FileExists(tmpFilename) Then

If LoadFileAsNewImage(tmpFilename, , updateRecentFileList, True, False) Then
numSuccesses = numSuccesses + 1
Else
If (LenB(tmpFilename) <> 0) Then
numFailures = numFailures + 1
brokenFiles = brokenFiles & Files.FileGetName(tmpFilename) & vbCrLf
End If
End If

End If

Loop

'Make sure we loaded at least one image from the original list
Expand Down
5 changes: 5 additions & 0 deletions Modules/Main.bas
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ Public Function ContinueLoadingProgram(Optional ByRef suspendAdditionalMessages
PDDebug.LogAction "(Note: this PD instance is unique; no other instances discovered.)"
End If

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


'*************************************************************************************************************************************
' Initialize the plugin manager and load any high-priority plugins (e.g. those required to start the program successfully)
Expand Down
38 changes: 36 additions & 2 deletions Modules/OS.bas
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ Attribute VB_Name = "OS"
'OS-specific specific features
'Copyright 2013-2021 by Tanner Helland
'Created: 21/November/13
'Last updated: 18/July/17
'Last update: migrate various OS-specific bits to this module
'Last updated: 18/October/21
'Last update: new helper function for requesting app restart after forced system reboots
'
'Newer Windows versions expose some neat features (like progress bars overlaying the taskbar), and PhotoDemon
' tries to make use of them when relevant. Similarly, some OS-level features are not easily mimicked from within VB
Expand Down Expand Up @@ -287,6 +287,7 @@ Private Declare Function LocalFree Lib "kernel32" (ByVal hMem As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccessas As Long, ByVal bInheritHandle As Long, ByVal dwProcId As Long) As Long
Private Declare Function ProcessFirst Lib "kernel32" Alias "Process32FirstW" (ByVal hSnapShot As Long, ByVal ptrToUProcess As Long) As Long
Private Declare Function ProcessNext Lib "kernel32" Alias "Process32NextW" (ByVal hSnapShot As Long, ByVal ptrToUProcess As Long) As Long
Private Declare Function RegisterApplicationRestart Lib "kernel32" (ByVal pwzCommandline As Long, ByVal dwFlags As Long) As Long
Private Declare Function VirtualAlloc Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long
Private Declare Function VirtualFree Lib "kernel32" (ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long

Expand Down Expand Up @@ -909,6 +910,39 @@ Public Function RAM_SystemTotal() As String

End Function

'PhotoDemon can automatically recover sessions interrupted by forced system reboots. This function
' can be called to enable or disable this behavior (but note that there are some restrictions;
' for example, the app must be running for ~60 seconds before it's eligible for recovery. This is
' an OS-level restriction.) Note also that this is only available on Vista+, but PD actually limits
' it to Win 7+ due to a lack of testing on Vista.
Public Sub SetRestartRestoreBehavior(ByVal allowToRestore As Boolean)

'Not available on XP. Possibly available on Vista but I no longer maintain a Vista VM, so I'm
' not enabling until someone can test for me.
If OS.IsWin7OrLater Then

'Possible flags for this API; not all are used by PD
Const RESTART_NO_CRASH As Long = 1 'Do not restart the process if it terminates due to an unhandled exception.
Const RESTART_NO_HANG As Long = 2 'Do not restart the process if it terminates due to the application not responding.
'Const RESTART_NO_PATCH As Long = 4 'Do not restart the process if it terminates due to the installation of an update.
'Const RESTART_NO_REBOOT As Long = 8 'Do not restart the process if the computer is restarted as the result of an update.
Const RESTART_NONE As Long = &HF& 'All the above; used to disable restarts completely after previously requesting them

'This function can also pass command-line parameters; we use an internal flag that bypasses
' the AutoSave Recovery prompt.
Dim cmdParams As String
cmdParams = " /system-reboot"

If allowToRestore Then
RegisterApplicationRestart StrPtr(cmdParams), RESTART_NO_CRASH Or RESTART_NO_HANG
Else
RegisterApplicationRestart ByVal 0&, RESTART_NONE
End If

End If

End Sub

'If desired, a custom state can be set for the taskbar. (Normally this is handled by the SetTaskbarProgressValue function.)
Public Function SetTaskbarProgressState(ByVal tbpFlags As PD_TaskBarProgress) As Long
If WIN7_FEATURES_ALLOWED Then SetTaskbarProgressState = CallInterface(m_taskbarObjHandle, SetProgressState_, 2, FormMain.hWnd, tbpFlags)
Expand Down
2 changes: 1 addition & 1 deletion PhotoDemon.vbp
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ Description="PhotoDemon Photo Editor"
CompatibleMode="0"
MajorVer=8
MinorVer=9
RevisionVer=925
RevisionVer=929
AutoIncrementVer=1
ServerSupportFiles=0
VersionComments="Copyright 2000-2021 Tanner Helland - photodemon.org"
Expand Down

0 comments on commit 735ba00

Please sign in to comment.