Skip to content

Commit 8b33941

Browse files
committed
Harden plugins against failure on XP, and...
... improve messaging for XP users on why some image formats aren't supported. This commit also softens messaging in the Tools > 3rd-party libraries menu when users manually update local library copies. This relates to #435 - thank you to @user-by for the suggestion!
1 parent c969ac7 commit 8b33941

File tree

9 files changed

+105
-72
lines changed

9 files changed

+105
-72
lines changed

Forms/Tools_PluginManager.frm

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,7 @@ Attribute VB_Exposed = False
278278
'Last update: allow libraries to be missing but OK, like libavif (which is downloaded on-demand)
279279
'
280280
'I've considered merging this form with the main Tools > Options dialog, but that dialog
281-
' is already cluttered, and I really prefer that users don't mess around with 3rd-party libraries.
281+
' is already cluttered and I'd prefer that average users don't interact with this dialog at all.
282282
' So this dialog exists as a standalone UI, and it should really be used only if there are problems.
283283
'
284284
'As of April '16, this dialog should never need to be updated against new libraries. All library
@@ -578,15 +578,25 @@ Private Function CheckLibraryStateUI(ByVal pluginID As CORE_PLUGINS, Optional By
578578
'Version mismatch
579579
Else
580580
dstStateString = g_Language.TranslateMessage("installed, but version is unexpected")
581-
dstStateUIColor = m_Colors.RetrieveColor(PDPM_BadText)
582-
CheckLibraryStateUI = False
581+
dstStateUIColor = m_Colors.RetrieveColor(PDPM_GoodText)
582+
CheckLibraryStateUI = True
583583
End If
584584

585585
'Plugin is disabled
586586
Else
587-
dstStateString = g_Language.TranslateMessage("installed, but disabled by user")
587+
588+
'If this is as simple as an XP compatibility issue (more prevalent now that PD supports
589+
' a variety of modern image formats that can't be built in XP-compatible ways),
590+
' let the user know
591+
If (Not OS.IsVistaOrLater) And PluginManager.IsPluginUnavailableOnXP(pluginID) Then
592+
dstStateString = g_Language.TranslateMessage("incompatible with Windows XP")
593+
Else
594+
dstStateString = g_Language.TranslateMessage("installed, but disabled by user")
595+
End If
596+
588597
dstStateUIColor = m_Colors.RetrieveColor(PDPM_BadText)
589598
CheckLibraryStateUI = False
599+
590600
End If
591601

592602
'Plugin is not present on the machine. For some libraries, this is problematic (e.g. critical libraries like lcms).
@@ -597,7 +607,7 @@ Private Function CheckLibraryStateUI(ByVal pluginID As CORE_PLUGINS, Optional By
597607
dstStateString = g_Language.TranslateMessage("not installed")
598608

599609
'If this plugin doesn't ship with PD, leave it marked as OK
600-
If IsPluginAvailableOnDemand(pluginID) Then
610+
If PluginManager.IsPluginAvailableOnDemand(pluginID) Then
601611
dstStateUIColor = m_Colors.RetrieveColor(PDPM_GoodText)
602612
CheckLibraryStateUI = True
603613
Else
@@ -686,7 +696,7 @@ Private Sub LibraryChanged()
686696
Else
687697

688698
'On-demand libraries are not a problem if they simply haven't been installed yet
689-
If IsPluginAvailableOnDemand(pluginIndex) Then
699+
If PluginManager.IsPluginAvailableOnDemand(pluginIndex) Then
690700
lblPluginVersion.Caption = g_Language.TranslateMessage("not installed, but available on-demand")
691701
lblPluginVersion.ForeColor = m_Colors.RetrieveColor(PDPM_GoodText)
692702
Else
@@ -702,7 +712,7 @@ Private Sub LibraryChanged()
702712
hypLicense.URL = PluginManager.GetPluginLicenseURL(pluginIndex)
703713

704714
'Some plugins support optional explanation text.
705-
If IsPluginAvailableOnDemand(pluginIndex) And (Not PluginManager.IsPluginCurrentlyInstalled(pluginIndex)) Then
715+
If PluginManager.IsPluginAvailableOnDemand(pluginIndex) And (Not PluginManager.IsPluginCurrentlyInstalled(pluginIndex)) Then
706716

707717
Dim actionTarget As String
708718
If (pluginIndex = CCP_AvifExport) Or (pluginIndex = CCP_AvifImport) Then
@@ -730,20 +740,3 @@ Private Sub LibraryChanged()
730740
End If
731741

732742
End Sub
733-
734-
'Some libraries are available on-demand (like libavif, which is enormous and most users won't need it).
735-
' It's OK if these libraries are missing on a default install - it just means the user hasn't triggered
736-
' any actions that prompt their download.
737-
Private Function IsPluginAvailableOnDemand(ByVal pluginID As CORE_PLUGINS) As Boolean
738-
739-
Select Case pluginID
740-
741-
Case CCP_AvifExport, CCP_AvifImport
742-
IsPluginAvailableOnDemand = True
743-
744-
Case Else
745-
IsPluginAvailableOnDemand = False
746-
747-
End Select
748-
749-
End Function

Modules/Plugin_8bf.bas

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,11 @@ Public Function GetInitialEffectTimestamp() As Currency
304304
End Function
305305

306306
Public Function GetPspiVersion() As String
307-
Dim ptrVersion As Long
308-
ptrVersion = pspiGetVersion()
309-
If (ptrVersion <> 0) Then GetPspiVersion = Strings.StringFromCharPtr(ptrVersion, False, 3, True) & ".0"
307+
If (m_LibHandle <> 0) And m_LibAvailable Then
308+
Dim ptrVersion As Long
309+
ptrVersion = pspiGetVersion()
310+
If (ptrVersion <> 0) Then GetPspiVersion = Strings.StringFromCharPtr(ptrVersion, False, 3, True) & ".0"
311+
End If
310312
End Function
311313

312314
Public Function InitializeEngine(ByRef pathToDLLFolder As String) As Boolean

Modules/Plugin_AVIF.bas

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ End Function
251251
Public Function GetVersion(ByVal testExportLibrary As Boolean) As String
252252

253253
GetVersion = vbNullString
254+
If (Not OS.IsVistaOrLater) Then Exit Function
254255

255256
Dim okToCheck As Boolean
256257
If testExportLibrary Then

Modules/Plugin_CharLS.bas

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,11 @@ Public Sub ForciblySetAvailability(ByVal newState As Boolean)
234234
End Sub
235235

236236
Public Function GetVersion() As String
237-
Dim vMajor As Long, vMinor As Long, vPatch As Long
238-
charls_get_version_number vMajor, vMinor, vPatch
239-
GetVersion = vMajor & "." & vMinor & "." & vPatch
237+
If (m_LibHandle <> 0) And m_LibAvailable Then
238+
Dim vMajor As Long, vMinor As Long, vPatch As Long
239+
charls_get_version_number vMajor, vMinor, vPatch
240+
GetVersion = vMajor & "." & vMinor & "." & vPatch
241+
End If
240242
End Function
241243

242244
Public Function InitializeEngine(ByRef pathToDLLFolder As String) As Boolean

Modules/Plugin_ExifTool.bas

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,8 @@ Public Function GetExifToolVersion() As String
645645
If (InStr(1, outputString, "[", vbBinaryCompare) <> 0) Then outputString = Left$(outputString, InStr(1, outputString, "[", vbBinaryCompare) - 1)
646646
GetExifToolVersion = Trim$(outputString)
647647

648+
Else
649+
GetExifToolVersion = vbNullString
648650
End If
649651

650652
End If
@@ -1429,18 +1431,20 @@ End Sub
14291431
' ALSO NOTE: This function is a heavily modified version of code originally written by Joacim Andersson. A download link to his
14301432
' original version is available at the top of this module.
14311433
Public Function ShellExecuteCapture(ByVal sApplicationPath As String, sCommandLineParams As String, ByRef dstString As String, Optional bShowWindow As Boolean = False) As Boolean
1432-
1434+
1435+
dstString = vbNullString
1436+
14331437
Dim hPipeRead As Long, hPipeWrite As Long
14341438
Dim hCurProcess As Long
14351439
Dim sa As SECURITY_ATTRIBUTES
1436-
Dim baOutput() As Byte
1440+
14371441
Dim sNewOutput As String
14381442
Dim lBytesRead As Long
14391443

14401444
'This pipe buffer size is effectively arbitrary, but I haven't had any problems with 1024
14411445
Const BUFFER_SIZE As Long = 1024
1442-
1443-
dstString = vbNullString
1446+
1447+
Dim baOutput() As Byte
14441448
ReDim baOutput(0 To BUFFER_SIZE - 1) As Byte
14451449

14461450
With sa
@@ -1453,7 +1457,7 @@ Public Function ShellExecuteCapture(ByVal sApplicationPath As String, sCommandLi
14531457
PDDebug.LogAction "Failed to start plugin service (couldn't create pipe)."
14541458
Exit Function
14551459
End If
1456-
1460+
14571461
hCurProcess = GetCurrentProcess()
14581462

14591463
'Replace the inheritable read handle with a non-inheritable one. (MSDN suggestion)
@@ -1495,7 +1499,7 @@ Public Function ShellExecuteCapture(ByVal sApplicationPath As String, sCommandLi
14951499
Exit Function
14961500
End If
14971501

1498-
CloseHandle hPipeRead
1502+
If (hPipeRead <> 0) Then CloseHandle hPipeRead
14991503
If (hPipeWrite <> 0) Then CloseHandle hPipeWrite
15001504
If (hCurProcess <> 0) Then CloseHandle hCurProcess
15011505

Modules/Plugin_Management.bas

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,21 @@ Attribute VB_Name = "PluginManager"
33
'3rd-Party Library Manager
44
'Copyright 2014-2022 by Tanner Helland
55
'Created: 30/August/15
6-
'Last updated: 28/February/22
7-
'Last update: add resvg
6+
'Last updated: 13/October/22
7+
'Last update: add a function for querying XP-compatibility; this will make it easier for the plugin manager UI
8+
' to notify a user that non-functional plugins are simple XP-compatibility issues, not technical problems
89
'
910
'As with any project of reasonable size, PhotoDemon can't supply all of its needs through WAPI alone.
10-
' A number of third-party libraries are required for correct program operation.
11+
' Current builds require a number of third-party libraries for full feature availability. (Some of these
12+
' libraries are mandatory, some are not; PD will attempt to gracefully degrade feature availability if it
13+
' can, but if you're acquiring PD from photodemon.org or it's official GitHub page you never need to
14+
' worry about this.)
1115
'
12-
'To simplify the management of these libraries, I've created this "plugin manager". Its purpose is
16+
'To simplify the management of external libraries, I've created this small "plugin manager". It exists
1317
' to make third-party library deployment and maintainence easier in a "portable" application context.
1418
'
1519
'When adding a new required library, please make sure to read the module-level declarations,
16-
' particularly the CORE_PLUGINS enum and the CORE_PLUGIN_COUNT constant at the top of this page.
20+
' particularly the CORE_PLUGINS enum and the CORE_PLUGIN_COUNT constant at the top of this file.
1721
'
1822
'Unless otherwise noted, all source code in this file is shared under a simplified BSD license.
1923
' Full license details are available in the LICENSE.md file, or at https://photodemon.org/license/
@@ -23,13 +27,13 @@ Attribute VB_Name = "PluginManager"
2327
Option Explicit
2428

2529
'This constant is used to iterate all core plugins (as listed under the CORE_PLUGINS enum),
26-
' so if you add or remove a plugin, YOU MUST update this. PD iterates plugins in order, so if
27-
' you do not update this, the plugin at the end of the chain (probably zstd) won't get
30+
' so if you add or remove a plugin, YOU MUST UPDATE THIS. PhotoDemon iterates plugins in order,
31+
' so if you do not update this count, the plugin at the end of the chain (probably zstd) won't be
2832
' initialized and PD will crash.
2933
Private Const CORE_PLUGIN_COUNT As Long = 14
3034

3135
'Currently supported core plugins. These values are arbitrary and can be changed without consequence, but THEY MUST
32-
' ALWAYS BE SEQUENTIAL, STARTING WITH ZERO, because the enum is iterated using For loops (e.g. during initialization).
36+
' ALWAYS BE SEQUENTIAL, STARTING WITH ZERO, because the enum is iterated using for..next loops (during initialization).
3337
Public Enum CORE_PLUGINS
3438
CCP_CharLS
3539
CCP_ExifTool
@@ -468,40 +472,63 @@ Public Function IsPluginCurrentlyInstalled(ByVal pluginEnumID As CORE_PLUGINS) A
468472
IsPluginCurrentlyInstalled = Files.FileExists(PluginManager.GetPluginPath & GetPluginFilename(pluginEnumID))
469473
End Function
470474

471-
'PD loads plugins in two waves. Before the splash screen appears, "high-priority" plugins are loaded. These include the
472-
' decompression plugins required to decompress things like the splash screen image. Much later in the load process,
473-
' we load the rest of the program's core plugins. This function determines which wave a plugin is loaded during.
475+
'Some libraries are available on-demand (like libavif, which is enormous and most users won't need it).
476+
' It's OK if these libraries are missing on a default install - it just means the user hasn't triggered
477+
' any actions that prompt their download.
478+
Public Function IsPluginAvailableOnDemand(ByVal pluginID As CORE_PLUGINS) As Boolean
479+
480+
IsPluginAvailableOnDemand = False
481+
482+
Select Case pluginID
483+
Case CCP_AvifExport, CCP_AvifImport
484+
IsPluginAvailableOnDemand = True
485+
End Select
486+
487+
End Function
488+
489+
'PD loads plugins in two waves. Before the splash screen appears, "high-priority" plugins are loaded.
490+
' These include compression plugins required to decompress things like the splash screen image.
491+
'
492+
'Much later in the load process, we load other third-party libraries (if necessary - some may yet be loaded
493+
' on-demand, as user operations require.
494+
'
495+
'This function determines if a plugin is forcibly loaded during that initial "high-priority" wave, or later.
474496
Public Function IsPluginHighPriority(ByVal pluginEnumID As CORE_PLUGINS) As Boolean
497+
498+
IsPluginHighPriority = False
499+
475500
Select Case pluginEnumID
476-
Case CCP_AvifExport
477-
IsPluginHighPriority = False
478-
Case CCP_AvifImport
479-
IsPluginHighPriority = False
480-
Case CCP_CharLS
481-
IsPluginHighPriority = False
482-
Case CCP_ExifTool
483-
IsPluginHighPriority = False
484-
Case CCP_EZTwain
485-
IsPluginHighPriority = False
486-
Case CCP_FreeImage
487-
IsPluginHighPriority = False
488501
Case CCP_libdeflate
489502
IsPluginHighPriority = True
490-
Case CCP_libjxl
491-
IsPluginHighPriority = False
492503
Case CCP_LittleCMS
493504
IsPluginHighPriority = True
494505
Case CCP_lz4
495506
IsPluginHighPriority = True
496-
Case CCP_pspiHost
497-
IsPluginHighPriority = False
498-
Case CCP_libwebp
499-
IsPluginHighPriority = False
500-
Case CCP_resvg
501-
IsPluginHighPriority = False
502507
Case CCP_zstd
503508
IsPluginHighPriority = True
504509
End Select
510+
511+
End Function
512+
513+
'As I've attempted to support more modern image formats (e.g. AVIF, HEIF, JPEG XL), I've also had to accept
514+
' that it's just not feasible to build some of the required 3rd-party libraries in XP-compatible ways.
515+
' This function can be used to query whether a plugin was disabled simply because it's not XP-compatible.
516+
' (Ignore the return of this function when running on Vista+, obviously.)
517+
Public Function IsPluginUnavailableOnXP(ByVal pluginEnumID As CORE_PLUGINS) As Boolean
518+
519+
IsPluginUnavailableOnXP = False
520+
521+
Select Case pluginEnumID
522+
Case CCP_AvifExport
523+
IsPluginUnavailableOnXP = True
524+
Case CCP_AvifImport
525+
IsPluginUnavailableOnXP = True
526+
Case CCP_libjxl
527+
IsPluginUnavailableOnXP = True
528+
Case CCP_resvg
529+
IsPluginUnavailableOnXP = True
530+
End Select
531+
505532
End Function
506533

507534
'Simplified function to return the expected version number of a plugin. These numbers change with each PD release, and they can

Modules/Plugin_WebP.bas

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ Public Function GetHandle_LibWebPMux() As Long
5757
End Function
5858

5959
Public Function GetVersion() As String
60-
60+
61+
If (m_hLibWebP = 0) Or (Not m_LibAvailable) Then Exit Function
62+
6163
'Byte version numbers get packed into a long
6264
Dim versionAsInt(0 To 3) As Byte
6365

Modules/Plugin_lz4.bas

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,11 @@ Public Sub ReleaseLz4()
125125
End Sub
126126

127127
Public Function GetLz4Version() As String
128-
Dim ptrVersion As Long
129-
ptrVersion = CallCDeclW(LZ4_versionNumber, vbLong)
130-
GetLz4Version = ptrVersion
128+
If (m_Lz4Handle <> 0) Then
129+
Dim ptrVersion As Long
130+
ptrVersion = CallCDeclW(LZ4_versionNumber, vbLong)
131+
GetLz4Version = ptrVersion
132+
End If
131133
End Function
132134

133135
Public Function IsLz4Available() As Boolean

Modules/Plugin_zstd.bas

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ Public Sub ReleaseZstd()
196196
End Sub
197197

198198
Public Function GetZstdVersion() As Long
199-
GetZstdVersion = CallCDeclW(ZSTD_versionNumber, vbLong)
199+
If (m_ZstdHandle <> 0) Then GetZstdVersion = CallCDeclW(ZSTD_versionNumber, vbLong)
200200
End Function
201201

202202
Public Function IsZstdAvailable() As Boolean

0 commit comments

Comments
 (0)