소스 검색

Merge branch 'main' into files-downloadarchive

Martijn Laan 2 달 전
부모
커밋
c87fc4e99b
8개의 변경된 파일117개의 추가작업 그리고 52개의 파일을 삭제
  1. 18 0
      .github/workflows/sync-fork.yml
  2. 3 1
      ISHelp/isetup.xml
  3. 22 1
      ISHelp/isxfunc.xml
  4. 60 49
      Projects/Src/Compression.SevenZipDLLDecoder.pas
  5. 4 0
      Projects/Src/Setup.ScriptFunc.pas
  6. 1 0
      Projects/Src/Shared.ScriptFunc.pas
  7. 7 0
      README.md
  8. 2 1
      whatsnew.htm

+ 18 - 0
.github/workflows/sync-fork.yml

@@ -0,0 +1,18 @@
+name: sync-fork
+on:
+  schedule:
+    - cron: '0 0 * * *'
+  workflow_dispatch:
+jobs:
+  sync:
+    # Only set the topic `has-issrc-build-env-sync-token` if the secret is available
+    if: contains(github.event.repository.topics, 'has-issrc-build-env-sync-token')
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+    steps:
+      - run: gh repo sync $REPOSITORY -b $BRANCH_NAME
+        env:
+          GITHUB_TOKEN: ${{ secrets.ISSRC_BUILD_ENV_SYNC_TOKEN }}
+          REPOSITORY: ${{ github.repository }}
+          BRANCH_NAME: ${{ github.ref_name }}

+ 3 - 1
ISHelp/isetup.xml

@@ -1750,6 +1750,7 @@ Instructs Setup to proceed to comparing time stamps (last write/modified time) i
 <flag name="extractarchive">
 <flag name="extractarchive">
 <p>This flag instructs Setup not to copy an existing archive file, but instead to extract it. Optionally use the <tt>ExtractArchivePassword</tt> parameter to specify a password.</p>
 <p>This flag instructs Setup not to copy an existing archive file, but instead to extract it. Optionally use the <tt>ExtractArchivePassword</tt> parameter to specify a password.</p>
 <p>The supported archive formats, beyond .7z, and the support for password-protected and multi-volume archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive, that must not be set to <tt>basic</tt>.</p>
 <p>The supported archive formats, beyond .7z, and the support for password-protected and multi-volume archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive, that must not be set to <tt>basic</tt>.</p>
+<p>To allow the extraction of archives with custom extensions, such as self-extracting archives, call <link topic="isxfunc_MapArchiveExtensions">MapArchiveExtensions</link>.</p>
 <p>This flag must be combined with the <tt>external</tt> and <tt>ignoreversion</tt> flags, meaning it should only be used on files private to your application, <i>never</i> on shared system files.</p>
 <p>This flag must be combined with the <tt>external</tt> and <tt>ignoreversion</tt> flags, meaning it should only be used on files private to your application, <i>never</i> on shared system files.</p>
 <p>This flag is usually combined with the <tt>recursesubdirs</tt> and <tt>createallsubdirs</tt> flags.</p>
 <p>This flag is usually combined with the <tt>recursesubdirs</tt> and <tt>createallsubdirs</tt> flags.</p>
 <p>Using a solid archive is not recommended; extraction performance may degrade depending on the solid block size.</p>
 <p>Using a solid archive is not recommended; extraction performance may degrade depending on the solid block size.</p>
@@ -4788,7 +4789,7 @@ ExtraDiskSpaceRequired=1_048_576</pre></example>
 <p><tt>basic</tt> uses an embedded version of the "7z ANSI-C Decoder" from the LZMA SDK by Igor Pavlov, as-is, except that Unicode support and error messages were improved and that it outputs memory requirements. It only supports .7z archives that are not password-protected.</p>
 <p><tt>basic</tt> uses an embedded version of the "7z ANSI-C Decoder" from the LZMA SDK by Igor Pavlov, as-is, except that Unicode support and error messages were improved and that it outputs memory requirements. It only supports .7z archives that are not password-protected.</p>
 <p><tt>enhanced/nopassword</tt> internally uses 7zxr.dll from the 7-Zip source code by Igor Pavlov, as-is, except that it was recompiled, code-signed, and renamed to is7zxr.dll. Compared to <tt>basic</tt>, it has lower memory requirements for archives that contain large files but increases the size of the Setup file(s). It still only supports .7z archives that are not password-protected.</p>
 <p><tt>enhanced/nopassword</tt> internally uses 7zxr.dll from the 7-Zip source code by Igor Pavlov, as-is, except that it was recompiled, code-signed, and renamed to is7zxr.dll. Compared to <tt>basic</tt>, it has lower memory requirements for archives that contain large files but increases the size of the Setup file(s). It still only supports .7z archives that are not password-protected.</p>
 <p><tt>enhanced</tt> uses 7zxa.dll instead of 7zxr.dll, recompiled, code-signed, and renamed to is7zxa.dll. It still only supports .7z archives, but they may be password-protected.</p>
 <p><tt>enhanced</tt> uses 7zxa.dll instead of 7zxr.dll, recompiled, code-signed, and renamed to is7zxa.dll. It still only supports .7z archives, but they may be password-protected.</p>
-<p><tt>full</tt> uses 7z.dll instead of 7zxa.dll, recompiled, code-signed, and renamed to is7z.dll. It supports multiple archive formats, although not as many as the original 7z.dll, to reduce its size. It supports multi-volume archives.</p>
+<p><tt>full</tt> uses 7z.dll instead of 7zxa.dll, recompiled, code-signed, and renamed to is7z.dll. It supports multiple archive formats, although not as many as the original 7z.dll, to reduce its size. Additionally, it supports multi-volume archives.</p>
 <p>The following table summarizes the differences between these methods.</p>
 <p>The following table summarizes the differences between these methods.</p>
 <indent>
 <indent>
 <table>
 <table>
@@ -4811,6 +4812,7 @@ ExtraDiskSpaceRequired=1_048_576</pre></example>
 </ul>
 </ul>
 <p><b>See also:</b><br/>
 <p><b>See also:</b><br/>
 <link topic="isxfunc_ExtractArchive">ExtractArchive</link><br/>
 <link topic="isxfunc_ExtractArchive">ExtractArchive</link><br/>
+<link topic="isxfunc_MapArchiveExtensions">MapArchiveExtensions</link><br/>
 <link topic="isxfunc_CreateExtractionPage">CreateExtractionPage</link>
 <link topic="isxfunc_CreateExtractionPage">CreateExtractionPage</link>
 </p>
 </p>
 </body>
 </body>

+ 22 - 1
ISHelp/isxfunc.xml

@@ -1913,17 +1913,36 @@ end;</pre></example>
         <description><p>Extracts the specified archive to the specified directory, with or without preserving full path names.</p>
         <description><p>Extracts the specified archive to the specified directory, with or without preserving full path names.</p>
 <p>An exception will be raised if there was an error.</p>
 <p>An exception will be raised if there was an error.</p>
 <p>The supported archive formats, beyond .7z, and the support for password-protected and multi-volume archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive.</p>
 <p>The supported archive formats, beyond .7z, and the support for password-protected and multi-volume archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive.</p>
+<p>To allow the extraction of archives with custom extensions, such as self-extracting archives, call <link topic="isxfunc_MapArchiveExtensions">MapArchiveExtensions</link>.</p>
 <p>Set OnExtractionProgress to a function to be informed of progress, or <tt>nil</tt> otherwise.</p>
 <p>Set OnExtractionProgress to a function to be informed of progress, or <tt>nil</tt> otherwise.</p>
-<p>When extracting without preservering path names (i.e. FullPaths is set to False), ensure that the archive does not contain multiple files with the same name. Doing so will help avoid file-in-use errors.</p>
+<p>When extracting without preserving path names (i.e. FullPaths is set to False), ensure that the archive does not contain multiple files with the same name. Doing so will help avoid file-in-use errors.</p>
 <p>See <i>CodeDownloadFiles.iss</i> for an example of archive extraction using just a [Files] entry instead.</p></description>
 <p>See <i>CodeDownloadFiles.iss</i> for an example of archive extraction using just a [Files] entry instead.</p></description>
         <remarks><p>TOnExtractionProgress is defined as:</p>
         <remarks><p>TOnExtractionProgress is defined as:</p>
 <p><tt>TOnExtractionProgress = function(const ArchiveName, FileName: String; const Progress, ProgressMax: Int64): Boolean;</tt></p>
 <p><tt>TOnExtractionProgress = function(const ArchiveName, FileName: String; const Progress, ProgressMax: Int64): Boolean;</tt></p>
 <p>Return True to allow the extraction to continue, False otherwise.</p></remarks>
 <p>Return True to allow the extraction to continue, False otherwise.</p></remarks>
         <seealso><p><link topic="isxfunc_CreateExtractionPage">CreateExtractionPage</link><br />
         <seealso><p><link topic="isxfunc_CreateExtractionPage">CreateExtractionPage</link><br />
+<link topic="isxfunc_MapArchiveExtensions">MapArchiveExtensions</link><br />
 <link topic="isxfunc_CreateDownloadPage">CreateDownloadPage</link><br />
 <link topic="isxfunc_CreateDownloadPage">CreateDownloadPage</link><br />
 <link topic="isxfunc_DownloadTemporaryFile">DownloadTemporaryFile</link><br />
 <link topic="isxfunc_DownloadTemporaryFile">DownloadTemporaryFile</link><br />
 <link topic="isxfunc_ExtractTemporaryFile">ExtractTemporaryFile</link></p></seealso>
 <link topic="isxfunc_ExtractTemporaryFile">ExtractTemporaryFile</link></p></seealso>
       </function>
       </function>
+      <function>
+        <name>MapArchiveExtensions</name>
+        <prototype>procedure MapArchiveExtensions(const DestExt, SourceExt: String);</prototype>
+        <description><p>Allows files with a specified destination extension, such as .exe, to be treated as if they have a different source extension, such as .7z, for extraction purposes.</p>
+<p>An exception will be raised if there was an error.</p></description>
+        <seealso><p><link topic="isxfunc_ExtractArchive">ExtractArchive</link><br/>
+<link topic="isxfunc_CreateExtractionPage">CreateExtractionPage</link></p></seealso>
+        <example><pre>
+[Files]
+Source: "{src}\My7ZipSFX.exe; DestDir: "{app}"; Flags: external extractarchive recursesubdirs createallsubdirs ignoreversion
+
+[Code]
+function InitializeSetup: Boolean;
+begin
+  MapArchiveExtensions('.exe', '.7z');
+end;</pre></example>
+      </function>
     </subcategory>
     </subcategory>
     <subcategory>
     <subcategory>
       <function>
       <function>
@@ -2726,6 +2745,7 @@ Page := CreateOutputMsgMemoPage(wpWelcome,
         <prototype>function CreateExtractionPage(const ACaption, ADescription: String; const OnExtractionProgress: TOnExtractionProgress): TExtractionWizardPage;</prototype>
         <prototype>function CreateExtractionPage(const ACaption, ADescription: String; const OnExtractionProgress: TOnExtractionProgress): TExtractionWizardPage;</prototype>
         <description><p>Creates a wizard page to extract archives and show progress.</p>
         <description><p>Creates a wizard page to extract archives and show progress.</p>
 <p>The supported archive formats, beyond .7z, and the support for password-protected and multi-volume archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive.</p>
 <p>The supported archive formats, beyond .7z, and the support for password-protected and multi-volume archives, depend on the <link topic="setup_archiveextraction">ArchiveExtraction</link> [Setup] section directive.</p>
+<p>To allow the extraction of archives with custom extensions, such as self-extracting archives, call <link topic="isxfunc_MapArchiveExtensions">MapArchiveExtensions</link>.</p>
 <p>Set OnExtractionProgress to a function to be informed of progress, or <tt>nil</tt> otherwise.</p>    
 <p>Set OnExtractionProgress to a function to be informed of progress, or <tt>nil</tt> otherwise.</p>    
 <p>Unlike the other types of wizard pages, progress pages are not displayed as part of the normal page sequence (note that there is no <tt>AfterID</tt> parameter). A progress page can only be displayed programmatically by calling its <tt>Show</tt> method.</p></description>
 <p>Unlike the other types of wizard pages, progress pages are not displayed as part of the normal page sequence (note that there is no <tt>AfterID</tt> parameter). A progress page can only be displayed programmatically by calling its <tt>Show</tt> method.</p></description>
         <remarks><p>Call the <tt>Show</tt> method to activate and show the page. When you're finished with it, call the <tt>Hide</tt> method to revert to the previous page.</p>
         <remarks><p>Call the <tt>Show</tt> method to activate and show the page. When you're finished with it, call the <tt>Hide</tt> method to revert to the previous page.</p>
@@ -2737,6 +2757,7 @@ Page := CreateOutputMsgMemoPage(wpWelcome,
         <example><p>See <i>CodeDownloadFiles.iss</i> for an example of <tt>CreateDownloadPage</tt> which works very similar to <tt>CreateExtractionPage</tt>, and an example of archive extraction using just a [Files] entry instead.</p></example>
         <example><p>See <i>CodeDownloadFiles.iss</i> for an example of <tt>CreateDownloadPage</tt> which works very similar to <tt>CreateExtractionPage</tt>, and an example of archive extraction using just a [Files] entry instead.</p></example>
         <seealso><p><link topic="scriptclasses" anchor="TExtractionWizardPage">TExtractionWizardPage</link><br />
         <seealso><p><link topic="scriptclasses" anchor="TExtractionWizardPage">TExtractionWizardPage</link><br />
 <link topic="isxfunc_ExtractArchive">ExtractArchive</link><br />
 <link topic="isxfunc_ExtractArchive">ExtractArchive</link><br />
+<link topic="isxfunc_MapArchiveExtensions">MapArchiveExtensions</link><br />
 <link topic="isxfunc_CreateOutputProgressPage">CreateOutputProgressPage</link></p></seealso>
 <link topic="isxfunc_CreateOutputProgressPage">CreateOutputProgressPage</link></p></seealso>
       </function>
       </function>
       <function>
       <function>

+ 60 - 49
Projects/Src/Compression.SevenZipDLLDecoder.pas

@@ -21,6 +21,8 @@ function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
   [ref] const VersionNumbers: TFileVersionNumbers): Boolean;
   [ref] const VersionNumbers: TFileVersionNumbers): Boolean;
 procedure SevenZipDLLDeInit;
 procedure SevenZipDLLDeInit;
 
 
+procedure MapArchiveExtensions(const DestExt, SourceExt: String);
+
 procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
 procedure ExtractArchiveRedir(const DisableFsRedir: Boolean;
   const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean;
   const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean;
   const OnExtractionProgress: TOnExtractionProgress);
   const OnExtractionProgress: TOnExtractionProgress);
@@ -52,7 +54,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  Classes, SysUtils, Forms, Variants, ActiveX, ComObj, Generics.Collections,
+  Classes, SysUtils, Forms, Variants, ActiveX, ComObj, Generics.Collections, Generics.Defaults,
   Compression.SevenZipDLLDecoder.Interfaces, PathFunc,
   Compression.SevenZipDLLDecoder.Interfaces, PathFunc,
   Shared.SetupMessageIDs, Shared.CommonFunc,
   Shared.SetupMessageIDs, Shared.CommonFunc,
   SetupLdrAndSetup.Messages, SetupLdrAndSetup.RedirFunc,
   SetupLdrAndSetup.Messages, SetupLdrAndSetup.RedirFunc,
@@ -222,48 +224,7 @@ begin
   SevenZipError(ExceptMessage, LogMessage);
   SevenZipError(ExceptMessage, LogMessage);
 end;
 end;
 
 
-function GetHandler(const Filename, NotFoundErrorMsg: String): TGUID;
-begin
-  const Ext = PathExtractExt(Filename);
-  if SameText(Ext, '.7z') then
-    Result := CLSID_Handler7z
-  else if SameText(Ext, '.zip') then
-    Result := CLSID_HandlerZip
-  else if SameText(Ext, '.gz') then
-    Result := CLSID_HandlerGzip
-  else if SameText(Ext, '.bz2') then
-    Result := CLSID_HandlerBZip2
-  else if SameText(Ext, '.xz') then
-    Result := CLSID_HandlerXz
-  else if SameText(Ext, '.tar') then
-    Result := CLSID_HandlerTar
-  else if SameText(Ext, '.rar') then
-    Result := CLSID_HandlerRar
-  else if SameText(Ext, '.iso') then
-    Result := CLSID_HandlerIso
-  else if SameText(Ext, '.msi') then
-    Result := CLSID_HandlerCompound
-  else if SameText(Ext, '.cab') then
-    Result := CLSID_HandlerCab
-  else if SameText(Ext, '.rpm') then
-    Result := CLSID_HandlerRpm
-  else if SameText(Ext, '.vhd') then
-    Result := CLSID_HandlerVhd
-  else if SameText(Ext, '.vhdx') then
-    Result := CLSID_HandlerVhdx
-  else if SameText(Ext, '.vdi') then
-    Result := CLSID_HandlerVDI
-  else if SameText(Ext, '.vmdk') then
-    Result := CLSID_HandlerVMDK
-  else if SameText(Ext, '.wim') then
-    Result := CLSID_HandlerWim
-  else if SameText(Ext, '.dmg') then
-    Result := CLSID_HandlerDmg
-  else if SameText(Ext, '.001') then
-    Result := CLSID_HandlerSplit
-  else
-    InternalError(NotFoundErrorMsg);
-end;
+function GetHandler(const Filename, NotFoundErrorMsg: String): TGUID; forward;
 
 
 const
 const
   varFileTime = 64; { Delphi lacks proper VT_FILETIME support }
   varFileTime = 64; { Delphi lacks proper VT_FILETIME support }
@@ -962,9 +923,13 @@ end;
 
 
 { Additional helper functions }
 { Additional helper functions }
 
 
+type
+  TSevenZipHandlers = TDictionary<String, TGUID>;
+
 var
 var
   CreateSevenZipObject: function(const clsid, iid: TGUID; var outObject): HRESULT; stdcall;
   CreateSevenZipObject: function(const clsid, iid: TGUID; var outObject): HRESULT; stdcall;
   VersionBanner: String;
   VersionBanner: String;
+  Handlers: TSevenZipHandlers;
 
 
 function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
 function SevenZipDLLInit(const SevenZipLibrary: HMODULE;
   [ref] const VersionNumbers: TFileVersionNumbers): Boolean;
   [ref] const VersionNumbers: TFileVersionNumbers): Boolean;
@@ -975,6 +940,45 @@ begin
     VersionBanner := Format(' %u.%.2u', [(VersionNumbers.MS shr 16) and $FFFF, VersionNumbers.MS and $FFFF])
     VersionBanner := Format(' %u.%.2u', [(VersionNumbers.MS shr 16) and $FFFF, VersionNumbers.MS and $FFFF])
   else
   else
     VersionBanner := '';
     VersionBanner := '';
+
+  Handlers := TSevenZipHandlers.Create(TIStringComparer.Ordinal);
+  Handlers.Add('.7z', CLSID_Handler7z);
+  Handlers.Add('.zip', CLSID_HandlerZip);
+  Handlers.Add('.gz', CLSID_HandlerGzip);
+  Handlers.Add('.bz2', CLSID_HandlerBZip2);
+  Handlers.Add('.xz', CLSID_HandlerXz);
+  Handlers.Add('.tar', CLSID_HandlerTar);
+  Handlers.Add('.rar', CLSID_HandlerRar);
+  Handlers.Add('.iso', CLSID_HandlerIso);
+  Handlers.Add('.msi', CLSID_HandlerCompound);
+  Handlers.Add('.cab', CLSID_HandlerCab);
+  Handlers.Add('.rpm', CLSID_HandlerRpm);
+  Handlers.Add('.vhd', CLSID_HandlerVhd);
+  Handlers.Add('.vhdx', CLSID_HandlerVhdx);
+  Handlers.Add('.vdi', CLSID_HandlerVDI);
+  Handlers.Add('.vmdk', CLSID_HandlerVMDK);
+  Handlers.Add('.wim', CLSID_HandlerWim);
+  Handlers.Add('.dmg', CLSID_HandlerDmg);
+  Handlers.Add('.001', CLSID_HandlerSplit);
+end;
+
+function GetHandlerForExt(const Ext, NotFoundErrorMsg: String): TGUID;
+begin
+  if not Handlers.TryGetValue(Ext, Result) then
+    InternalError(NotFoundErrorMsg);
+end;
+
+function GetHandler(const Filename, NotFoundErrorMsg: String): TGUID;
+begin;
+  Result := GetHandlerForExt(PathExtractExt(Filename), NotFoundErrorMsg);
+end;
+
+procedure MapArchiveExtensions(const DestExt, SourceExt: String);
+begin
+  if (Length(DestExt) < 2) or (DestExt[1] <> '.') then
+    InternalError('MapArchiveExtensions: Invalid DestExt');
+  const clsid = GetHandlerForExt(SourceExt, 'MapArchiveExtensions: Invalid SourceExt');
+  Handlers.AddOrSetValue(DestExt, clsid);
 end;
 end;
 
 
 var
 var
@@ -1013,7 +1017,8 @@ begin
     SevenZipError(SetupMessages[msgArchiveIsCorrupted], 'Cannot get number of items');
     SevenZipError(SetupMessages[msgArchiveIsCorrupted], 'Cannot get number of items');
 
 
   if numItems = 1 then begin
   if numItems = 1 then begin
-    { Get inner archive stream if it exists - See OpenArchive.cpp CArchiveLink::Open }
+    { Get inner archive stream if it exists - See OpenArchive.cpp CArchiveLink::Open
+      Give up trying to get or open it on any error }
     var MainSubFile: Cardinal;
     var MainSubFile: Cardinal;
     var SubSeqStream: ISequentialInStream;
     var SubSeqStream: ISequentialInStream;
     if not GetProperty(Result, $FFFF, kpidMainSubfile, MainSubFile) or
     if not GetProperty(Result, $FFFF, kpidMainSubfile, MainSubFile) or
@@ -1032,7 +1037,12 @@ begin
     if MainSubFilePath = '' then
     if MainSubFilePath = '' then
       MainSubFilePath := PathChangeExt(ArchiveFilename, '');
       MainSubFilePath := PathChangeExt(ArchiveFilename, '');
 
 
-    const SubClsid = GetHandler(MainSubFilePath, '');
+    var SubClsid: TGUID;
+    try
+      SubClsid := GetHandler(MainSubFilePath, '');
+    except
+      Exit;
+    end;
     var SubResult: IInArchive;
     var SubResult: IInArchive;
     if CreateSevenZipObject(SubClsid, IInArchive, SubResult) <> S_OK then
     if CreateSevenZipObject(SubClsid, IInArchive, SubResult) <> S_OK then
       Exit;
       Exit;
@@ -1040,11 +1050,13 @@ begin
     var SubScanSize := DefaultScanSize;
     var SubScanSize := DefaultScanSize;
     const SubOpenCallback: IArchiveOpenCallback =
     const SubOpenCallback: IArchiveOpenCallback =
       TArchiveOpenCallbackWithStreamBackup.Create(Password, InStream); { In tests the backup of InStream wasn't needed but better safe than sorry }
       TArchiveOpenCallbackWithStreamBackup.Create(Password, InStream); { In tests the backup of InStream wasn't needed but better safe than sorry }
+    var SubNumItems: UInt32;
     if (SubResult.Open(SubStream, @SubScanSize, SubOpenCallback) <> S_OK) or
     if (SubResult.Open(SubStream, @SubScanSize, SubOpenCallback) <> S_OK) or
-       (SubResult.GetNumberOfItems(numItems) <> S_OK) then
+       (SubResult.GetNumberOfItems(SubNumItems) <> S_OK) then
       Exit;
       Exit;
 
 
     Result := SubResult;
     Result := SubResult;
+    numItems := SubNumItems;
   end;
   end;
 end;
 end;
 
 
@@ -1266,10 +1278,9 @@ end;
 
 
 procedure SevenZipDLLDeInit;
 procedure SevenZipDLLDeInit;
 begin
 begin
+  FreeAndNil(Handlers);
   { ArchiveFindStates has references to 7-Zip so must be cleared before the DLL is unloaded }
   { ArchiveFindStates has references to 7-Zip so must be cleared before the DLL is unloaded }
-  ArchiveFindStates.Free;
+  FreeAndNil(ArchiveFindStates);
 end;
 end;
 
 
-{ TArchiveOpenCallbackWithStreamBackup }
-
 end.
 end.

+ 4 - 0
Projects/Src/Setup.ScriptFunc.pas

@@ -1838,6 +1838,10 @@ var
           raise Exception.Create(FmtSetupMessage1(msgErrorExtractionFailed, GetExceptMessage));
           raise Exception.Create(FmtSetupMessage1(msgErrorExtractionFailed, GetExceptMessage));
       end;
       end;
     end);
     end);
+    RegisterScriptFunc('MapArchiveExtensions', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
+    begin
+      MapArchiveExtensions(Stack.GetString(PStart), Stack.GetString(PStart-1));
+    end);
     RegisterScriptFunc('DEBUGGING', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
     RegisterScriptFunc('DEBUGGING', procedure(const Caller: TPSExec; const OrgName: AnsiString; const Stack: TPSStack; const PStart: Cardinal)
     begin
     begin
       Stack.SetBool(PStart, Debugging);
       Stack.SetBool(PStart, Debugging);

+ 1 - 0
Projects/Src/Shared.ScriptFunc.pas

@@ -542,6 +542,7 @@ initialization
     'function InitializeBitmapImageFromIcon(const BitmapImage: TBitmapImage; const IconFilename: String; const BkColor: TColor; const AscendingTrySizes: TArrayOfInteger): Boolean;',
     'function InitializeBitmapImageFromIcon(const BitmapImage: TBitmapImage; const IconFilename: String; const BkColor: TColor; const AscendingTrySizes: TArrayOfInteger): Boolean;',
     'procedure Extract7ZipArchive(const ArchiveFileName, DestDir: String; const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);',
     'procedure Extract7ZipArchive(const ArchiveFileName, DestDir: String; const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);',
     'procedure ExtractArchive(const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);',
     'procedure ExtractArchive(const ArchiveFilename, DestDir, Password: String; const FullPaths: Boolean; const OnExtractionProgress: TOnExtractionProgress);',
+    'procedure MapArchiveExtensions(const DestExt, SourceExt: String);',
     'function Debugging: Boolean;',
     'function Debugging: Boolean;',
     'function StringJoin(const Separator: String; const Values: TArrayOfString): String;',
     'function StringJoin(const Separator: String; const Values: TArrayOfString): String;',
     'function StringSplit(const S: String; const Separators: TArrayOfString; const Typ: TSplitType): TArrayOfString;',
     'function StringSplit(const S: String; const Separators: TArrayOfString; const Typ: TSplitType): TArrayOfString;',

+ 7 - 0
README.md

@@ -273,6 +273,13 @@ https://github.com/YOUR-USER-NAME/issrc to add the topic).
 Once that's done, you're set! The next time you push a branch to your fork, the
 Once that's done, you're set! The next time you push a branch to your fork, the
 workflow will be triggered automatically.
 workflow will be triggered automatically.
 
 
+To set up automatic synchronization for your fork, first create a Fine-Grained Personal
+Access Token with access to your fork or all repositories you own, ensuring it has Read and
+Write permissions for Contents. After that, add this token as a new repository secret, under
+the name `ISSRC_BUILD_ENV_ZIP_SYNC_TOKEN`. Finally, indicate that your fork has this secret,
+by adding the topic `has-issrc-build-env-sync-token`. Your fork will nowsynchronize daily,
+and will automatically run the aforementioned build workflow on changes, if it's configured.
+
 <!-- Link references -->
 <!-- Link references -->
 [CONTRIBUTING.md]: <CONTRIBUTING.md>
 [CONTRIBUTING.md]: <CONTRIBUTING.md>
 [Projects\Bin]: <Projects/Bin>
 [Projects\Bin]: <Projects/Bin>

+ 2 - 1
whatsnew.htm

@@ -50,7 +50,7 @@ For conditions of distribution and use, see <a href="files/is/license.txt">LICEN
         <li><tt>basic</tt> (default) is the method introduced by Inno Setup 6.4.0. It only supports .7z archives that are not password-protected.</li>
         <li><tt>basic</tt> (default) is the method introduced by Inno Setup 6.4.0. It only supports .7z archives that are not password-protected.</li>
         <li><tt>enhanced/nopassword</tt> is a new method and internally uses 7zxr.dll from the 7-Zip source code by Igor Pavlov, as-is, except that it was recompiled, code-signed, and renamed to is7zxr.dll. Compared to <tt>basic</tt>, it has lower memory requirements for archives that contain large files but increases the size of the Setup file(s). It still only supports .7z archives that are not password-protected.</li>
         <li><tt>enhanced/nopassword</tt> is a new method and internally uses 7zxr.dll from the 7-Zip source code by Igor Pavlov, as-is, except that it was recompiled, code-signed, and renamed to is7zxr.dll. Compared to <tt>basic</tt>, it has lower memory requirements for archives that contain large files but increases the size of the Setup file(s). It still only supports .7z archives that are not password-protected.</li>
         <li><tt>enhanced</tt> uses 7zxa.dll instead of 7zxr.dll, recompiled, code-signed, and renamed to is7zxa.dll. It still only supports .7z archives, but they may be password-protected.</li>
         <li><tt>enhanced</tt> uses 7zxa.dll instead of 7zxr.dll, recompiled, code-signed, and renamed to is7zxa.dll. It still only supports .7z archives, but they may be password-protected.</li>
-        <li><tt>full</tt> uses 7z.dll instead of 7zxa.dll, recompiled, code-signed, and renamed to is7z.dll. It supports multiple archive formats (.7z, .zip, .rar, and more), although not as many as the original 7z.dll, to reduce its size.</li>
+        <li><tt>full</tt> uses 7z.dll instead of 7zxa.dll, recompiled, code-signed, and renamed to is7z.dll. It supports multiple archive formats (.7z, .zip, .rar, and more), although not as many as the original 7z.dll, to reduce its size. Additionally, it supports multi-volume archives.</li>
       </ul>
       </ul>
       New documentation topic <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_archiveextraction">ArchiveExtraction</a> has a table summarizing the differences between these methods.
       New documentation topic <a href="https://jrsoftware.org/ishelp/index.php?topic=setup_archiveextraction">ArchiveExtraction</a> has a table summarizing the differences between these methods.
     </li>
     </li>
@@ -78,6 +78,7 @@ Source: "{tmp}\MyProg-ExtraReadmes.7z"; DestDir: "{app}"; \
     <ul>
     <ul>
       <li>New support function <tt>ExtractArchive</tt> replaces the deprecated <tt>Extract7ZipArchive</tt>. <tt>ExtractArchive</tt> includes an additional parameter to optionally specify a password.</li>
       <li>New support function <tt>ExtractArchive</tt> replaces the deprecated <tt>Extract7ZipArchive</tt>. <tt>ExtractArchive</tt> includes an additional parameter to optionally specify a password.</li>
       <li><tt>ExtractArchive</tt> and <tt>CreateExtractionPage</tt> now overwrite read-only files which already exist in the destination directory without prompting the user. Previously this would cause an extraction error.</li>
       <li><tt>ExtractArchive</tt> and <tt>CreateExtractionPage</tt> now overwrite read-only files which already exist in the destination directory without prompting the user. Previously this would cause an extraction error.</li>
+      <li>Added new <tt>MapArchiveExtensions</tt> support function to allow the extraction of archives with custom extensions, such as self-extracting archives.</li>
       <li>Added new <tt>AddEx</tt> function to support class <tt>TExtractionWizardPage</tt> to add password-protected archives.</li>
       <li>Added new <tt>AddEx</tt> function to support class <tt>TExtractionWizardPage</tt> to add password-protected archives.</li>
     </ul>
     </ul>
   </li>
   </li>