123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773 |
- unit Setup.ScriptFunc.HelperFunc;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Helper types and functions for the script support functions (run time - used by Setup)
- }
- interface
- uses
- Windows, Diagnostics,
- uPSRuntime, MD5, SHA1,
- Shared.CommonFunc, Shared.FileClass, Setup.MainForm, Setup.WizardForm,
- Setup.UninstallProgressForm, Setup.Install, Compression.SevenZipDecoder;
- type
- { Must keep this in synch with Compiler.ScriptFunc.pas }
- TOnLog = procedure(const S: String; const Error, FirstLine: Boolean) of object;
- { Must keep this in synch with Compiler.ScriptFunc.pas }
- TFindRec = record
- Name: String;
- Attributes: LongWord;
- SizeHigh: LongWord;
- SizeLow: LongWord;
- CreationTime: TFileTime;
- LastAccessTime: TFileTime;
- LastWriteTime: TFileTime;
- AlternateName: String;
- FindHandle: THandle;
- end;
- { Must keep this in synch with Compiler.ScriptFunc.pas }
- TWindowsVersion = packed record
- Major: Cardinal;
- Minor: Cardinal;
- Build: Cardinal;
- ServicePackMajor: Cardinal;
- ServicePackMinor: Cardinal;
- NTPlatform: Boolean;
- ProductType: Byte;
- SuiteMask: Word;
- end;
- { Makes sure script isn't called crazy often because that would slow the download significantly.
- Only reports:
- -At start or finish
- -If at least 50 ms passed since last report }
- TProgressThrottler = class
- private
- FOnDownloadProgress: TOnDownloadProgress;
- FOnExtractionProgress: TOnExtractionProgress;
- FStopWatch: TStopWatch;
- FLastOkProgress: Int64;
- function ThrottleOk(const Progress, ProgressMax: Int64): Boolean;
- public
- constructor Create(const OnDownloadProgress: TOnDownloadProgress); overload;
- constructor Create(const OnExtractionProgress: TOnExtractionProgress); overload;
- procedure Reset;
- function OnDownloadProgress(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean;
- function OnExtractionProgress(const ArchiveName, FileName: string; const Progress, ProgressMax: Int64): Boolean;
- end;
- var
- ScaleBaseUnitX, ScaleBaseUnitY: Integer;
- procedure NoUninstallFuncError(const C: AnsiString); overload;
- procedure OnlyUninstallFuncError(const C: AnsiString); overload;
- function GetWizardForm: TWizardForm;
- function GetUninstallProgressForm: TUninstallProgressForm;
- function GetMsgBoxCaption: String;
- procedure InitializeScaleBaseUnits;
- function IsProtectedSrcExe(const Filename: String): Boolean;
- function FindFirstHelper(const FileName: String; var FindRec: TFindRec): Boolean;
- function FindNextHelper(var FindRec: TFindRec): Boolean;
- procedure FindCloseHelper(var FindRec: TFindRec);
- function FmtMessageHelper(const S: String; const Args: array of String): String;
- procedure GetWindowsVersionExHelper(var Version: TWindowsVersion);
- procedure CrackCodeRootKey(CodeRootKey: HKEY; var RegView: TRegView;
- var RootKey: HKEY);
- function GetSubkeyOrValueNames(const RegView: TRegView; const RootKey: HKEY;
- const SubKeyName: String; const Stack: TPSStack; const ItemNo: Longint; const Subkey: Boolean): Boolean;
- function GetMD5OfFile(const DisableFsRedir: Boolean; const Filename: String): TMD5Digest;
- function GetSHA1OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA1Digest;
- function GetMD5OfAnsiString(const S: AnsiString): TMD5Digest;
- function GetMD5OfUnicodeString(const S: UnicodeString): TMD5Digest;
- function GetSHA1OfAnsiString(const S: AnsiString): TSHA1Digest;
- function GetSHA1OfUnicodeString(const S: UnicodeString): TSHA1Digest;
- procedure ProcessMessagesProc; far;
- procedure ExecAndLogOutputLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
- procedure ExecAndLogOutputLogCustom(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
- function CustomMessage(const MsgName: String): String;
- function NewExtractRelativePath(BaseName, DestName: string): string;
- function NewFileSearch(const DisableFsRedir: Boolean;
- const Name, DirList: String): String;
- function GetExceptionMessage(const Caller: TPSExec): String;
- function GetCodePreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
- function SetCodePreviousData(const PreviousDataKey: HKEY; const ValueName, ValueData: String): Boolean;
- function LoadStringFromFile(const FileName: String; var S: AnsiString;
- const Sharing: TFileSharing): Boolean;
- function LoadStringsFromFile(const FileName: String; const Stack: TPSStack;
- const ItemNo: Longint; const Sharing: TFileSharing): Boolean;
- function SaveStringToFile(const FileName: String; const S: AnsiString; Append: Boolean): Boolean;
- function SaveStringsToFile(const FileName: String; const Stack: TPSStack;
- const ItemNo: Longint; Append, UTF8, UTF8WithoutBOM: Boolean): Boolean;
- function CreateCallback(const Caller: TPSExec; const P: PPSVariantProcPtr): LongWord;
- implementation
- uses
- Forms, SysUtils, Graphics,
- uPSUtils, PathFunc, ASMInline, PSStackHelper,
- Setup.MainFunc, SetupLdrAndSetup.RedirFunc, Setup.InstFunc,
- SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
- Shared.SetupTypes, Shared.SetupSteps, Setup.LoggingFunc, Setup.SetupForm;
- procedure NoUninstallFuncError(const C: AnsiString); overload;
- begin
- InternalError(Format('Cannot call "%s" function during Uninstall', [C]));
- end;
- procedure OnlyUninstallFuncError(const C: AnsiString); overload;
- begin
- InternalError(Format('Cannot call "%s" function during Setup', [C]));
- end;
- function GetWizardForm: TWizardForm;
- begin
- Result := WizardForm;
- if Result = nil then
- InternalError('An attempt was made to access WizardForm before it has been created');
- end;
- function GetUninstallProgressForm: TUninstallProgressForm;
- begin
- Result := UninstallProgressForm;
- if Result = nil then
- InternalError('An attempt was made to access UninstallProgressForm before it has been created');
- end;
- function GetMsgBoxCaption: String;
- var
- ID: TSetupMessageID;
- begin
- if IsUninstaller then
- ID := msgUninstallAppTitle
- else
- ID := msgSetupAppTitle;
- Result := SetupMessages[ID];
- end;
- var
- ScaleBaseUnitsInitialized: Boolean;
- procedure InitializeScaleBaseUnits;
- var
- Font: TFont;
- begin
- if ScaleBaseUnitsInitialized then
- Exit;
- Font := TFont.Create;
- try
- SetFontNameSize(Font, LangOptions.DialogFontName, LangOptions.DialogFontSize,
- '', 8);
- CalculateBaseUnitsFromFont(Font, ScaleBaseUnitX, ScaleBaseUnitY);
- finally
- Font.Free;
- end;
- ScaleBaseUnitsInitialized := True;
- end;
- function IsProtectedSrcExe(const Filename: String): Boolean;
- begin
- if (MainForm = nil) or (MainForm.CurStep < ssInstall) then begin
- var ExpandedFilename := PathExpand(Filename);
- Result := PathCompare(ExpandedFilename, SetupLdrOriginalFilename) = 0;
- end else
- Result := False;
- end;
- procedure FindDataToFindRec(const FindData: TWin32FindData;
- var FindRec: TFindRec);
- begin
- FindRec.Name := FindData.cFileName;
- FindRec.Attributes := FindData.dwFileAttributes;
- FindRec.SizeHigh := FindData.nFileSizeHigh;
- FindRec.SizeLow := FindData.nFileSizeLow;
- FindRec.CreationTime := FindData.ftCreationTime;
- FindRec.LastAccessTime := FindData.ftLastAccessTime;
- FindRec.LastWriteTime := FindData.ftLastWriteTime;
- FindRec.AlternateName := FindData.cAlternateFileName;
- end;
- function FindFirstHelper(const FileName: String; var FindRec: TFindRec): Boolean;
- var
- FindHandle: THandle;
- FindData: TWin32FindData;
- begin
- FindHandle := FindFirstFileRedir(ScriptFuncDisableFsRedir, FileName, FindData);
- if FindHandle <> INVALID_HANDLE_VALUE then begin
- FindRec.FindHandle := FindHandle;
- FindDataToFindRec(FindData, FindRec);
- Result := True;
- end
- else begin
- FindRec.FindHandle := 0;
- Result := False;
- end;
- end;
- function FindNextHelper(var FindRec: TFindRec): Boolean;
- var
- FindData: TWin32FindData;
- begin
- Result := (FindRec.FindHandle <> 0) and FindNextFile(FindRec.FindHandle, FindData);
- if Result then
- FindDataToFindRec(FindData, FindRec);
- end;
- procedure FindCloseHelper(var FindRec: TFindRec);
- begin
- if FindRec.FindHandle <> 0 then begin
- Windows.FindClose(FindRec.FindHandle);
- FindRec.FindHandle := 0;
- end;
- end;
- function FmtMessageHelper(const S: String; const Args: array of String): String;
- begin
- Result := FmtMessage(PChar(S), Args);
- end;
- procedure GetWindowsVersionExHelper(var Version: TWindowsVersion);
- begin
- Version.Major := WindowsVersion shr 24;
- Version.Minor := (WindowsVersion shr 16) and $FF;
- Version.Build := WindowsVersion and $FFFF;
- Version.ServicePackMajor := Hi(NTServicePackLevel);
- Version.ServicePackMinor := Lo(NTServicePackLevel);
- Version.NTPlatform := True;
- Version.ProductType := WindowsProductType;
- Version.SuiteMask := WindowsSuiteMask;
- end;
- procedure CrackCodeRootKey(CodeRootKey: HKEY; var RegView: TRegView;
- var RootKey: HKEY);
- begin
- if (CodeRootKey and not CodeRootKeyValidFlags) = HKEY_AUTO then begin
- { Change HKA to HKLM or HKCU, keeping our special flag bits. }
- CodeRootKey := (CodeRootKey and CodeRootKeyValidFlags) or InstallModeRootKey;
- end else begin
- { Allow only predefined key handles (8xxxxxxx). Can't accept handles to
- open keys because they might have our special flag bits set.
- Also reject unknown flags which may have a meaning in the future. }
- if (CodeRootKey shr 31 <> 1) or
- ((CodeRootKey and CodeRootKeyFlagMask) and not CodeRootKeyValidFlags <> 0) then
- InternalError('Invalid RootKey value');
- end;
- if CodeRootKey and CodeRootKeyFlag32Bit <> 0 then
- RegView := rv32Bit
- else if CodeRootKey and CodeRootKeyFlag64Bit <> 0 then begin
- if not IsWin64 then
- InternalError('Cannot access 64-bit registry keys on this version of Windows');
- RegView := rv64Bit;
- end
- else
- RegView := InstallDefaultRegView;
- RootKey := CodeRootKey and not CodeRootKeyFlagMask;
- end;
- function GetSubkeyOrValueNames(const RegView: TRegView; const RootKey: HKEY;
- const SubKeyName: String; const Stack: TPSStack; const ItemNo: Longint; const Subkey: Boolean): Boolean;
- const
- samDesired: array [Boolean] of REGSAM = (KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS);
- var
- K: HKEY;
- Buf, S: String;
- BufSize, R: DWORD;
- begin
- Result := False;
- SetString(Buf, nil, 512);
- if RegOpenKeyExView(RegView, RootKey, PChar(SubKeyName), 0, samDesired[Subkey], K) <> ERROR_SUCCESS then
- Exit;
- try
- var ArrayBuilder := Stack.InitArrayBuilder(ItemNo);
- while True do begin
- BufSize := Length(Buf);
- if Subkey then
- R := RegEnumKeyEx(K, ArrayBuilder.I, @Buf[1], BufSize, nil, nil, nil, nil)
- else
- R := RegEnumValue(K, ArrayBuilder.I, @Buf[1], BufSize, nil, nil, nil, nil);
- case R of
- ERROR_SUCCESS: ;
- ERROR_NO_MORE_ITEMS: Break;
- ERROR_MORE_DATA:
- begin
- { Double the size of the buffer and try again }
- if Length(Buf) >= 65536 then begin
- { Sanity check: If we tried a 64 KB buffer and it's still saying
- there's more data, something must be seriously wrong. Bail. }
- Exit;
- end;
- SetString(Buf, nil, Length(Buf) * 2);
- Continue;
- end;
- else
- Exit; { unknown failure... }
- end;
- SetString(S, PChar(@Buf[1]), BufSize);
- ArrayBuilder.Add(S);
- end;
- finally
- RegCloseKey(K);
- end;
- Result := True;
- end;
- function GetMD5OfFile(const DisableFsRedir: Boolean; const Filename: String): TMD5Digest;
- { Gets MD5 sum of the file Filename. An exception will be raised upon
- failure. }
- var
- Buf: array[0..65535] of Byte;
- begin
- var Context: TMD5Context;
- MD5Init(Context);
- var F := TFileRedir.Create(DisableFsRedir, Filename, fdOpenExisting, faRead, fsReadWrite);
- try
- while True do begin
- var NumRead := F.Read(Buf, SizeOf(Buf));
- if NumRead = 0 then
- Break;
- MD5Update(Context, Buf, NumRead);
- end;
- finally
- F.Free;
- end;
- Result := MD5Final(Context);
- end;
- function GetSHA1OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA1Digest;
- { Gets SHA-1 sum of the file Filename. An exception will be raised upon
- failure. }
- var
- Buf: array[0..65535] of Byte;
- begin
- var Context: TSHA1Context;
- SHA1Init(Context);
- var F := TFileRedir.Create(DisableFsRedir, Filename, fdOpenExisting, faRead, fsReadWrite);
- try
- while True do begin
- var NumRead := F.Read(Buf, SizeOf(Buf));
- if NumRead = 0 then
- Break;
- SHA1Update(Context, Buf, NumRead);
- end;
- finally
- F.Free;
- end;
- Result := SHA1Final(Context);
- end;
- function GetMD5OfAnsiString(const S: AnsiString): TMD5Digest;
- begin
- Result := MD5Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
- end;
- function GetMD5OfUnicodeString(const S: UnicodeString): TMD5Digest;
- begin
- Result := MD5Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
- end;
- function GetSHA1OfAnsiString(const S: AnsiString): TSHA1Digest;
- begin
- Result := SHA1Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
- end;
- function GetSHA1OfUnicodeString(const S: UnicodeString): TSHA1Digest;
- begin
- Result := SHA1Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
- end;
- procedure ProcessMessagesProc; far;
- begin
- Application.ProcessMessages;
- end;
- procedure ExecAndLogOutputLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
- begin
- Log(S);
- end;
- procedure ExecAndLogOutputLogCustom(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
- begin
- var OnLog := TOnLog(PMethod(Data)^);
- OnLog(S, Error, FirstLine);
- end;
- function CustomMessage(const MsgName: String): String;
- begin
- if not GetCustomMessageValue(MsgName, Result) then
- InternalError(Format('Unknown custom message name "%s"', [MsgName]));
- end;
- { ExtractRelativePath is not in Delphi 2's SysUtils. Use the one from Delphi 7.01. }
- function NewExtractRelativePath(BaseName, DestName: string): string;
- var
- BasePath, DestPath: string;
- BaseLead, DestLead: PChar;
- BasePtr, DestPtr: PChar;
- function ExtractFilePathNoDrive(const FileName: string): string;
- begin
- Result := PathExtractPath(FileName);
- Delete(Result, 1, Length(PathExtractDrive(FileName)));
- end;
- function Next(var Lead: PChar): PChar;
- begin
- Result := Lead;
- if Result = nil then Exit;
- Lead := PathStrScan(Lead, '\');
- if Lead <> nil then
- begin
- Lead^ := #0;
- Inc(Lead);
- end;
- end;
- begin
- { For consistency with the PathExtract* functions, normalize slashes so
- that forward slashes and multiple slashes work with this function also }
- BaseName := PathNormalizeSlashes(BaseName);
- DestName := PathNormalizeSlashes(DestName);
- if PathCompare(PathExtractDrive(BaseName), PathExtractDrive(DestName)) = 0 then
- begin
- BasePath := ExtractFilePathNoDrive(BaseName);
- UniqueString(BasePath);
- DestPath := ExtractFilePathNoDrive(DestName);
- UniqueString(DestPath);
- BaseLead := Pointer(BasePath);
- BasePtr := Next(BaseLead);
- DestLead := Pointer(DestPath);
- DestPtr := Next(DestLead);
- while (BasePtr <> nil) and (DestPtr <> nil) and (PathCompare(BasePtr, DestPtr) = 0) do
- begin
- BasePtr := Next(BaseLead);
- DestPtr := Next(DestLead);
- end;
- Result := '';
- while BaseLead <> nil do
- begin
- Result := Result + '..\'; { Do not localize }
- Next(BaseLead);
- end;
- if (DestPtr <> nil) and (DestPtr^ <> #0) then
- Result := Result + DestPtr + '\';
- if DestLead <> nil then
- Result := Result + DestLead; // destlead already has a trailing backslash
- Result := Result + PathExtractName(DestName);
- end
- else
- Result := DestName;
- end;
- { Use our own FileSearch function which includes these improvements over
- Delphi's version:
- - it supports MBCS and uses Path* functions
- - it uses NewFileExistsRedir instead of FileExists
- - it doesn't search the current directory unless it's told to
- - it always returns a fully-qualified path }
- function NewFileSearch(const DisableFsRedir: Boolean;
- const Name, DirList: String): String;
- var
- I, P, L: Integer;
- begin
- { If Name is absolute, drive-relative, or root-relative, don't search DirList }
- if PathDrivePartLengthEx(Name, True) <> 0 then begin
- Result := PathExpand(Name);
- if NewFileExistsRedir(DisableFsRedir, Result) then
- Exit;
- end
- else begin
- P := 1;
- L := Length(DirList);
- while True do begin
- while (P <= L) and (DirList[P] = ';') do
- Inc(P);
- if P > L then
- Break;
- I := P;
- while (P <= L) and (DirList[P] <> ';') do
- Inc(P, PathCharLength(DirList, P));
- Result := PathExpand(PathCombine(Copy(DirList, I, P - I), Name));
- if NewFileExistsRedir(DisableFsRedir, Result) then
- Exit;
- end;
- end;
- Result := '';
- end;
- function GetExceptionMessage(const Caller: TPSExec): String;
- var
- Code: TPSError;
- E: TObject;
- begin
- Code := Caller.LastEx;
- if Code = erNoError then
- Result := '(There is no current exception)'
- else begin
- E := Caller.LastExObject;
- if Assigned(E) and (E is Exception) then
- Result := Exception(E).Message
- else
- Result := String(PSErrorToString(Code, Caller.LastExParam));
- end;
- end;
- function GetCodePreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
- begin
- { do not localize or change the following string }
- Result := GetPreviousData(ExpandedAppId, 'Inno Setup CodeFile: ' + ValueName, DefaultValueData);
- end;
- { Also see RegisterUninstallInfo in Install.pas }
- function SetCodePreviousData(const PreviousDataKey: HKEY; const ValueName, ValueData: String): Boolean;
- begin
- if ValueData <> '' then begin
- { do not localize or change the following string }
- Result := RegSetValueEx(PreviousDataKey, PChar('Inno Setup CodeFile: ' + ValueName), 0, REG_SZ, PChar(ValueData), (Length(ValueData)+1)*SizeOf(ValueData[1])) = ERROR_SUCCESS
- end else
- Result := True;
- end;
- function LoadStringFromFile(const FileName: String; var S: AnsiString;
- const Sharing: TFileSharing): Boolean;
- var
- F: TFile;
- N: Cardinal;
- begin
- try
- F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenExisting, faRead, Sharing);
- try
- N := F.CappedSize;
- SetLength(S, N);
- F.ReadBuffer(S[1], N);
- finally
- F.Free;
- end;
- Result := True;
- except
- Result := False;
- end;
- end;
- function LoadStringsFromFile(const FileName: String; const Stack: TPSStack;
- const ItemNo: Longint; const Sharing: TFileSharing): Boolean;
- var
- F: TTextFileReader;
- begin
- try
- F := TTextFileReaderRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenExisting, faRead, Sharing);
- try
- var ArrayBuilder := Stack.InitArrayBuilder(ItemNo);
- while not F.Eof do
- ArrayBuilder.Add(F.ReadLine);
- finally
- F.Free;
- end;
- Result := True;
- except
- Result := False;
- end;
- end;
- function SaveStringToFile(const FileName: String; const S: AnsiString; Append: Boolean): Boolean;
- var
- F: TFile;
- begin
- try
- if Append then
- F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenAlways, faWrite, fsNone)
- else
- F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
- try
- F.SeekToEnd;
- F.WriteAnsiString(S);
- finally
- F.Free;
- end;
- Result := True;
- except
- Result := False;
- end;
- end;
- function SaveStringsToFile(const FileName: String; const Stack: TPSStack;
- const ItemNo: Longint; Append, UTF8, UTF8WithoutBOM: Boolean): Boolean;
- var
- F: TTextFileWriter;
- begin
- try
- if Append then
- F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenAlways, faWrite, fsNone)
- else
- F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
- try
- if UTF8 and UTF8WithoutBOM then
- F.UTF8WithoutBOM := UTF8WithoutBOM;
- var ArrayEnumerator := Stack.InitArrayEnumerator(ItemNo);
- while ArrayEnumerator.HasNext do begin
- var S := ArrayEnumerator.Next;
- if not UTF8 then
- F.WriteAnsiLine(AnsiString(S))
- else
- F.WriteLine(S);
- end;
- finally
- F.Free;
- end;
- Result := True;
- except
- Result := False;
- end;
- end;
- var
- ASMInliners: array of Pointer;
- function CreateCallback(const Caller: TPSExec; const P: PPSVariantProcPtr): LongWord;
- var
- ProcRec: TPSInternalProcRec;
- Method: TMethod;
- Inliner: TASMInline;
- ParamCount, SwapFirst, SwapLast: Integer;
- S: tbtstring;
- begin
- { ProcNo 0 means nil was passed by the script }
- if P.ProcNo = 0 then
- InternalError('Invalid Method value');
- { Calculate parameter count of our proc, will need this later. }
- ProcRec := Caller.GetProcNo(P.ProcNo) as TPSInternalProcRec;
- S := ProcRec.ExportDecl;
- GRFW(S);
- ParamCount := 0;
- while S <> '' do begin
- Inc(ParamCount);
- GRFW(S);
- end;
- { Turn our proc into a callable TMethod - its Code will point to
- ROPS' MyAllMethodsHandler and its Data to a record identifying our proc.
- When called, MyAllMethodsHandler will use the record to call our proc. }
- Method := MkMethod(Caller, P.ProcNo);
- { Wrap our TMethod with a dynamically generated stdcall callback which will
- do two things:
- -Remember the Data pointer which MyAllMethodsHandler needs.
- -Handle the calling convention mismatch.
- Based on InnoCallback by Sherlock Software, see
- http://www.sherlocksoftware.org/page.php?id=54 and
- https://github.com/thenickdude/InnoCallback. }
- Inliner := TASMInline.create;
- try
- Inliner.Pop(EAX); //get the retptr off the stack
- SwapFirst := 2;
- SwapLast := ParamCount-1;
- //Reverse the order of parameters from param3 onwards in the stack
- while SwapLast > SwapFirst do begin
- Inliner.Mov(ECX, Inliner.Addr(ESP, SwapFirst * 4)); //load the first item of the pair
- Inliner.Mov(EDX, Inliner.Addr(ESP, SwapLast * 4)); //load the last item of the pair
- Inliner.Mov(Inliner.Addr(ESP, SwapFirst * 4), EDX);
- Inliner.Mov(Inliner.Addr(ESP, SwapLast * 4), ECX);
- Inc(SwapFirst);
- Dec(SwapLast);
- end;
- if ParamCount >= 1 then
- Inliner.Pop(EDX); //load param1
- if ParamCount >= 2 then
- Inliner.Pop(ECX); //load param2
- Inliner.Push(EAX); //put the retptr back onto the stack
- Inliner.Mov(EAX, LongWord(Method.Data)); //Load the self ptr
- Inliner.Jmp(Method.Code); //jump to the wrapped proc
- SetLength(ASMInliners, Length(ASMInliners) + 1);
- ASMInliners[High(ASMInliners)] := Inliner.SaveAsMemory;
- Result := LongWord(ASMInliners[High(ASMInliners)]);
- finally
- Inliner.Free;
- end;
- end;
- procedure FreeASMInliners;
- var
- I: Integer;
- begin
- for I := 0 to High(ASMInliners) do
- FreeMem(ASMInliners[I]);
- SetLength(ASMInliners, 0);
- end;
- { TProgressThrottler }
- constructor TProgressThrottler.Create(const OnDownloadProgress: TOnDownloadProgress);
- begin
- inherited Create;
- FOnDownloadProgress := OnDownloadProgress;
- end;
- constructor TProgressThrottler.Create(const OnExtractionProgress: TOnExtractionProgress);
- begin
- inherited Create;
- FOnExtractionProgress := OnExtractionProgress;
- end;
- procedure TProgressThrottler.Reset;
- begin
- FStopWatch.Stop;
- end;
- function TProgressThrottler.ThrottleOk(const Progress, ProgressMax: Int64): Boolean;
- begin
- if FStopWatch.IsRunning then begin
- Result := ((Progress = ProgressMax) and (FLastOkProgress <> ProgressMax)) or (FStopWatch.ElapsedMilliseconds >= 50);
- if Result then
- FStopWatch.Reset;
- end else begin
- Result := True;
- FStopWatch := TStopwatch.StartNew;
- end;
- if Result then
- FLastOkProgress := Progress;
- end;
- function TProgressThrottler.OnDownloadProgress(const Url, BaseName: string; const Progress,
- ProgressMax: Int64): Boolean;
- begin
- if Assigned(FOnDownloadProgress) and ThrottleOk(Progress, ProgressMax) then begin
- Result := FOnDownloadProgress(Url, BaseName, Progress, ProgressMax)
- end else
- Result := True;
- end;
- function TProgressThrottler.OnExtractionProgress(const ArchiveName, FileName: string;
- const Progress, ProgressMax: Int64): Boolean;
- begin
- if Assigned(FOnExtractionProgress) and ThrottleOk(Progress, ProgressMax) then
- Result := FOnExtractionProgress(ArchiveName, FileName, Progress, ProgressMax)
- else
- Result := True;
- end;
- initialization
- finalization
- FreeASMInliners;
- end.
|