Setup.ScriptFunc.HelperFunc.pas 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  1. unit Setup.ScriptFunc.HelperFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Helper types and functions for the script support functions (run time - used by Setup)
  8. Requires following globals to be set:
  9. -LangOptions.DialogFontName
  10. -LangOptions.DialogFontSize
  11. -LangOptions.DialogFontBaseScaleWidth
  12. -LangOptions.DialogFontBaseScaleHeight
  13. -shWizardKeepAspectRatio in SetupHeader.Options
  14. }
  15. interface
  16. uses
  17. Windows, Diagnostics,
  18. uPSRuntime, MD5, SHA1,
  19. Shared.CommonFunc, Shared.FileClass, Setup.MainForm, Setup.WizardForm,
  20. Setup.UninstallProgressForm, Setup.DownloadFileFunc, Compression.SevenZipDecoder;
  21. type
  22. { Must keep this in synch with Compiler.ScriptFunc.pas }
  23. TOnLog = procedure(const S: String; const Error, FirstLine: Boolean) of object;
  24. { Must keep this in synch with Compiler.ScriptFunc.pas }
  25. TFindRec = record
  26. Name: String;
  27. Attributes: LongWord;
  28. SizeHigh: LongWord;
  29. SizeLow: LongWord;
  30. CreationTime: TFileTime;
  31. LastAccessTime: TFileTime;
  32. LastWriteTime: TFileTime;
  33. AlternateName: String;
  34. FindHandle: THandle;
  35. end;
  36. { Must keep this in synch with Compiler.ScriptFunc.pas }
  37. TWindowsVersion = packed record
  38. Major: Cardinal;
  39. Minor: Cardinal;
  40. Build: Cardinal;
  41. ServicePackMajor: Cardinal;
  42. ServicePackMinor: Cardinal;
  43. NTPlatform: Boolean;
  44. ProductType: Byte;
  45. SuiteMask: Word;
  46. end;
  47. { Makes sure script isn't called crazy often because that would slow the download significantly.
  48. Only reports:
  49. -At start or finish
  50. -If at least 50 ms passed since last report }
  51. TProgressThrottler = class
  52. private
  53. FOnDownloadProgress: TOnDownloadProgress;
  54. FOnExtractionProgress: TOnExtractionProgress;
  55. FStopWatch: TStopWatch;
  56. FLastOkProgress: Int64;
  57. function ThrottleOk(const Progress, ProgressMax: Int64): Boolean;
  58. public
  59. constructor Create(const OnDownloadProgress: TOnDownloadProgress); overload;
  60. constructor Create(const OnExtractionProgress: TOnExtractionProgress); overload;
  61. procedure Reset;
  62. function OnDownloadProgress(const Url, BaseName: string; const Progress, ProgressMax: Int64): Boolean;
  63. function OnExtractionProgress(const ArchiveName, FileName: string; const Progress, ProgressMax: Int64): Boolean;
  64. end;
  65. var
  66. OrigScaleBaseUnitX, OrigScaleBaseUnitY: Integer;
  67. ScaleBaseUnitX, ScaleBaseUnitY: Integer;
  68. procedure NoUninstallFuncError(const C: AnsiString); overload;
  69. procedure OnlyUninstallFuncError(const C: AnsiString); overload;
  70. function GetWizardForm: TWizardForm;
  71. function GetUninstallProgressForm: TUninstallProgressForm;
  72. function GetMsgBoxCaption: String;
  73. procedure InitializeScaleBaseUnits;
  74. function IsProtectedSrcExe(const Filename: String): Boolean;
  75. function LogFmtHelper(const S: String; const Args: array of const): String;
  76. function FmtMessageHelper(const S: String; const Args: array of String): String;
  77. function FindFirstHelper(const FileName: String; var FindRec: TFindRec): Boolean;
  78. function FindNextHelper(var FindRec: TFindRec): Boolean;
  79. procedure FindCloseHelper(var FindRec: TFindRec);
  80. procedure GetWindowsVersionExHelper(var Version: TWindowsVersion);
  81. procedure CrackCodeRootKey(CodeRootKey: UInt32; var RegView: TRegView;
  82. var RootKey: HKEY);
  83. function GetSubkeyOrValueNames(const RegView: TRegView; const RootKey: HKEY;
  84. const SubKeyName: String; const Stack: TPSStack; const ItemNo: Longint; const Subkey: Boolean): Boolean;
  85. function GetMD5OfFile(const DisableFsRedir: Boolean; const Filename: String): TMD5Digest;
  86. function GetSHA1OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA1Digest;
  87. function GetMD5OfAnsiString(const S: AnsiString): TMD5Digest;
  88. function GetMD5OfUnicodeString(const S: UnicodeString): TMD5Digest;
  89. function GetSHA1OfAnsiString(const S: AnsiString): TSHA1Digest;
  90. function GetSHA1OfUnicodeString(const S: UnicodeString): TSHA1Digest;
  91. procedure ProcessMessagesProc; far;
  92. procedure ExecAndLogOutputLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  93. procedure ExecAndLogOutputLogCustom(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  94. function CustomMessage(const MsgName: String): String;
  95. function NewExtractRelativePath(BaseName, DestName: string): string;
  96. function NewFileSearch(const DisableFsRedir: Boolean;
  97. const Name, DirList: String): String;
  98. function GetExceptionMessage(const Caller: TPSExec): String;
  99. function GetCodePreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
  100. function SetCodePreviousData(const PreviousDataKey: HKEY; const ValueName, ValueData: String): Boolean;
  101. function LoadStringFromFile(const FileName: String; var S: AnsiString;
  102. const Sharing: TFileSharing): Boolean;
  103. function LoadStringsFromFile(const FileName: String; const Stack: TPSStack;
  104. const ItemNo: Longint; const Sharing: TFileSharing): Boolean;
  105. function SaveStringToFile(const FileName: String; const S: AnsiString; Append: Boolean): Boolean;
  106. function SaveStringsToFile(const FileName: String; const Stack: TPSStack;
  107. const ItemNo: Longint; Append, UTF8, UTF8WithoutBOM: Boolean): Boolean;
  108. function CreateCallback(const Caller: TPSExec; const P: PPSVariantProcPtr): LongWord;
  109. implementation
  110. uses
  111. Forms, SysUtils, Graphics,
  112. uPSUtils, PathFunc, ASMInline, PSStackHelper, UnsignedFunc,
  113. Setup.MainFunc, Setup.RedirFunc, Setup.InstFunc,
  114. SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Shared.Struct,
  115. Shared.SetupTypes, Shared.SetupSteps, Setup.LoggingFunc, Setup.SetupForm;
  116. procedure NoUninstallFuncError(const C: AnsiString); overload;
  117. begin
  118. InternalError(Format('Cannot call "%s" function during Uninstall', [C]));
  119. end;
  120. procedure OnlyUninstallFuncError(const C: AnsiString); overload;
  121. begin
  122. InternalError(Format('Cannot call "%s" function during Setup', [C]));
  123. end;
  124. function GetWizardForm: TWizardForm;
  125. begin
  126. Result := WizardForm;
  127. if Result = nil then
  128. InternalError('An attempt was made to access WizardForm before it has been created');
  129. end;
  130. function GetUninstallProgressForm: TUninstallProgressForm;
  131. begin
  132. Result := UninstallProgressForm;
  133. if Result = nil then
  134. InternalError('An attempt was made to access UninstallProgressForm before it has been created');
  135. end;
  136. function GetMsgBoxCaption: String;
  137. var
  138. ID: TSetupMessageID;
  139. begin
  140. if IsUninstaller then
  141. ID := msgUninstallAppTitle
  142. else
  143. ID := msgSetupAppTitle;
  144. Result := SetupMessages[ID];
  145. end;
  146. var
  147. ScaleBaseUnitsInitialized: Boolean;
  148. procedure InitializeScaleBaseUnits;
  149. var
  150. Font: TFont;
  151. begin
  152. if ScaleBaseUnitsInitialized then
  153. Exit;
  154. Font := TFont.Create;
  155. try
  156. SetFontNameSize(Font, LangOptions.DialogFontName, LangOptions.DialogFontSize, '', 9);
  157. CalculateBaseUnitsFromFont(Font, ScaleBaseUnitX, ScaleBaseUnitY);
  158. OrigScaleBaseUnitX := LangOptions.DialogFontBaseScaleWidth;
  159. OrigScaleBaseUnitY := LangOptions.DialogFontBaseScaleHeight;
  160. if shWizardKeepAspectRatio in SetupHeader.Options then begin
  161. if ScaleBaseUnitX * OrigScaleBaseUnitY > ScaleBaseUnitY * OrigScaleBaseUnitX then begin
  162. ScaleBaseUnitY := ScaleBaseUnitX;
  163. OrigScaleBaseUnitY := OrigScaleBaseUnitX;
  164. end else begin
  165. ScaleBaseUnitX := ScaleBaseUnitY;
  166. OrigScaleBaseUnitX := OrigScaleBaseUnitY;
  167. end;
  168. end;
  169. finally
  170. Font.Free;
  171. end;
  172. ScaleBaseUnitsInitialized := True;
  173. end;
  174. function IsProtectedSrcExe(const Filename: String): Boolean;
  175. begin
  176. if (MainForm = nil) or (MainForm.CurStep < ssInstall) then begin
  177. var ExpandedFilename := PathExpand(Filename);
  178. Result := PathCompare(ExpandedFilename, SetupLdrOriginalFilename) = 0;
  179. end else
  180. Result := False;
  181. end;
  182. function LogFmtHelper(const S: String; const Args: array of const): String;
  183. begin
  184. LogFmt(S, Args);
  185. end;
  186. function FmtMessageHelper(const S: String; const Args: array of String): String;
  187. begin
  188. Result := FmtMessage(PChar(S), Args);
  189. end;
  190. procedure FindDataToFindRec(const FindData: TWin32FindData;
  191. var FindRec: TFindRec);
  192. begin
  193. FindRec.Name := FindData.cFileName;
  194. FindRec.Attributes := FindData.dwFileAttributes;
  195. FindRec.SizeHigh := FindData.nFileSizeHigh;
  196. FindRec.SizeLow := FindData.nFileSizeLow;
  197. FindRec.CreationTime := FindData.ftCreationTime;
  198. FindRec.LastAccessTime := FindData.ftLastAccessTime;
  199. FindRec.LastWriteTime := FindData.ftLastWriteTime;
  200. FindRec.AlternateName := FindData.cAlternateFileName;
  201. end;
  202. function FindFirstHelper(const FileName: String; var FindRec: TFindRec): Boolean;
  203. var
  204. FindHandle: THandle;
  205. FindData: TWin32FindData;
  206. begin
  207. FindHandle := FindFirstFileRedir(ScriptFuncDisableFsRedir, FileName, FindData);
  208. if FindHandle <> INVALID_HANDLE_VALUE then begin
  209. FindRec.FindHandle := FindHandle;
  210. FindDataToFindRec(FindData, FindRec);
  211. Result := True;
  212. end
  213. else begin
  214. FindRec.FindHandle := 0;
  215. Result := False;
  216. end;
  217. end;
  218. function FindNextHelper(var FindRec: TFindRec): Boolean;
  219. var
  220. FindData: TWin32FindData;
  221. begin
  222. Result := (FindRec.FindHandle <> 0) and FindNextFile(FindRec.FindHandle, FindData);
  223. if Result then
  224. FindDataToFindRec(FindData, FindRec);
  225. end;
  226. procedure FindCloseHelper(var FindRec: TFindRec);
  227. begin
  228. if FindRec.FindHandle <> 0 then begin
  229. Windows.FindClose(FindRec.FindHandle);
  230. FindRec.FindHandle := 0;
  231. end;
  232. end;
  233. procedure GetWindowsVersionExHelper(var Version: TWindowsVersion);
  234. begin
  235. Version.Major := WindowsVersion shr 24;
  236. Version.Minor := (WindowsVersion shr 16) and $FF;
  237. Version.Build := WindowsVersion and $FFFF;
  238. Version.ServicePackMajor := Hi(NTServicePackLevel);
  239. Version.ServicePackMinor := Lo(NTServicePackLevel);
  240. Version.NTPlatform := True;
  241. Version.ProductType := WindowsProductType;
  242. Version.SuiteMask := WindowsSuiteMask;
  243. end;
  244. procedure CrackCodeRootKey(CodeRootKey: UInt32; var RegView: TRegView;
  245. var RootKey: HKEY);
  246. begin
  247. if (CodeRootKey and not CodeRootKeyValidFlags) = HKEY_AUTO then begin
  248. { Change HKA to HKLM or HKCU, keeping our special flag bits. }
  249. CodeRootKey := (CodeRootKey and CodeRootKeyValidFlags) or InstallModeRootKey;
  250. end else begin
  251. { Allow only predefined key handles (8xxxxxxx). Can't accept handles to
  252. open keys because they might have our special flag bits set.
  253. Also reject unknown flags which may have a meaning in the future. }
  254. if (CodeRootKey shr 31 <> 1) or
  255. ((CodeRootKey and CodeRootKeyFlagMask) and not CodeRootKeyValidFlags <> 0) then
  256. InternalError('Invalid RootKey value');
  257. end;
  258. if CodeRootKey and CodeRootKeyFlag32Bit <> 0 then
  259. RegView := rv32Bit
  260. else if CodeRootKey and CodeRootKeyFlag64Bit <> 0 then begin
  261. if not IsWin64 then
  262. InternalError('Cannot access 64-bit registry keys on this version of Windows');
  263. RegView := rv64Bit;
  264. end
  265. else
  266. RegView := InstallDefaultRegView;
  267. RootKey := CodeRootKey and not CodeRootKeyFlagMask;
  268. end;
  269. function GetSubkeyOrValueNames(const RegView: TRegView; const RootKey: HKEY;
  270. const SubKeyName: String; const Stack: TPSStack; const ItemNo: Longint; const Subkey: Boolean): Boolean;
  271. const
  272. samDesired: array [Boolean] of REGSAM = (KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS);
  273. var
  274. K: HKEY;
  275. Buf, S: String;
  276. BufSize, R: DWORD;
  277. begin
  278. Result := False;
  279. SetString(Buf, nil, 512);
  280. if RegOpenKeyExView(RegView, RootKey, PChar(SubKeyName), 0, samDesired[Subkey], K) <> ERROR_SUCCESS then
  281. Exit;
  282. try
  283. var ArrayBuilder := Stack.InitArrayBuilder(ItemNo);
  284. while True do begin
  285. BufSize := ULength(Buf);
  286. if Subkey then
  287. R := RegEnumKeyEx(K, ArrayBuilder.I, @Buf[1], BufSize, nil, nil, nil, nil)
  288. else
  289. R := RegEnumValue(K, ArrayBuilder.I, @Buf[1], BufSize, nil, nil, nil, nil);
  290. case R of
  291. ERROR_SUCCESS: ;
  292. ERROR_NO_MORE_ITEMS: Break;
  293. ERROR_MORE_DATA:
  294. begin
  295. { Double the size of the buffer and try again }
  296. if Length(Buf) >= 65536 then begin
  297. { Sanity check: If we tried a 64 KB buffer and it's still saying
  298. there's more data, something must be seriously wrong. Bail. }
  299. Exit;
  300. end;
  301. SetString(Buf, nil, Length(Buf) * 2);
  302. Continue;
  303. end;
  304. else
  305. Exit; { unknown failure... }
  306. end;
  307. SetString(S, PChar(@Buf[1]), BufSize);
  308. ArrayBuilder.Add(S);
  309. end;
  310. finally
  311. RegCloseKey(K);
  312. end;
  313. Result := True;
  314. end;
  315. function GetMD5OfFile(const DisableFsRedir: Boolean; const Filename: String): TMD5Digest;
  316. { Gets MD5 sum of the file Filename. An exception will be raised upon
  317. failure. }
  318. var
  319. Buf: array[0..65535] of Byte;
  320. begin
  321. var Context: TMD5Context;
  322. MD5Init(Context);
  323. var F := TFileRedir.Create(DisableFsRedir, Filename, fdOpenExisting, faRead, fsReadWrite);
  324. try
  325. while True do begin
  326. var NumRead := F.Read(Buf, SizeOf(Buf));
  327. if NumRead = 0 then
  328. Break;
  329. MD5Update(Context, Buf, NumRead);
  330. end;
  331. finally
  332. F.Free;
  333. end;
  334. Result := MD5Final(Context);
  335. end;
  336. function GetSHA1OfFile(const DisableFsRedir: Boolean; const Filename: String): TSHA1Digest;
  337. { Gets SHA-1 sum of the file Filename. An exception will be raised upon
  338. failure. }
  339. var
  340. Buf: array[0..65535] of Byte;
  341. begin
  342. var Context: TSHA1Context;
  343. SHA1Init(Context);
  344. var F := TFileRedir.Create(DisableFsRedir, Filename, fdOpenExisting, faRead, fsReadWrite);
  345. try
  346. while True do begin
  347. var NumRead := F.Read(Buf, SizeOf(Buf));
  348. if NumRead = 0 then
  349. Break;
  350. SHA1Update(Context, Buf, NumRead);
  351. end;
  352. finally
  353. F.Free;
  354. end;
  355. Result := SHA1Final(Context);
  356. end;
  357. function GetMD5OfAnsiString(const S: AnsiString): TMD5Digest;
  358. begin
  359. Result := MD5Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  360. end;
  361. function GetMD5OfUnicodeString(const S: UnicodeString): TMD5Digest;
  362. begin
  363. Result := MD5Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  364. end;
  365. function GetSHA1OfAnsiString(const S: AnsiString): TSHA1Digest;
  366. begin
  367. Result := SHA1Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  368. end;
  369. function GetSHA1OfUnicodeString(const S: UnicodeString): TSHA1Digest;
  370. begin
  371. Result := SHA1Buf(Pointer(S)^, Length(S)*SizeOf(S[1]));
  372. end;
  373. procedure ProcessMessagesProc; far;
  374. begin
  375. Application.ProcessMessages;
  376. end;
  377. procedure ExecAndLogOutputLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  378. begin
  379. Log(S);
  380. end;
  381. procedure ExecAndLogOutputLogCustom(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  382. begin
  383. var OnLog := TOnLog(PMethod(Data)^);
  384. OnLog(S, Error, FirstLine);
  385. end;
  386. function CustomMessage(const MsgName: String): String;
  387. begin
  388. if not GetCustomMessageValue(MsgName, Result) then
  389. InternalError(Format('Unknown custom message name "%s"', [MsgName]));
  390. end;
  391. { ExtractRelativePath is not in Delphi 2's SysUtils. Use the one from Delphi 7.01. }
  392. function NewExtractRelativePath(BaseName, DestName: string): string;
  393. var
  394. BasePath, DestPath: string;
  395. BaseLead, DestLead: PChar;
  396. BasePtr, DestPtr: PChar;
  397. function ExtractFilePathNoDrive(const FileName: string): string;
  398. begin
  399. Result := PathExtractPath(FileName);
  400. Delete(Result, 1, Length(PathExtractDrive(FileName)));
  401. end;
  402. function Next(var Lead: PChar): PChar;
  403. begin
  404. Result := Lead;
  405. if Result = nil then Exit;
  406. Lead := PathStrScan(Lead, '\');
  407. if Lead <> nil then
  408. begin
  409. Lead^ := #0;
  410. Inc(Lead);
  411. end;
  412. end;
  413. begin
  414. { For consistency with the PathExtract* functions, normalize slashes so
  415. that forward slashes and multiple slashes work with this function also }
  416. BaseName := PathNormalizeSlashes(BaseName);
  417. DestName := PathNormalizeSlashes(DestName);
  418. if PathCompare(PathExtractDrive(BaseName), PathExtractDrive(DestName)) = 0 then
  419. begin
  420. BasePath := ExtractFilePathNoDrive(BaseName);
  421. UniqueString(BasePath);
  422. DestPath := ExtractFilePathNoDrive(DestName);
  423. UniqueString(DestPath);
  424. BaseLead := Pointer(BasePath);
  425. BasePtr := Next(BaseLead);
  426. DestLead := Pointer(DestPath);
  427. DestPtr := Next(DestLead);
  428. while (BasePtr <> nil) and (DestPtr <> nil) and (PathCompare(BasePtr, DestPtr) = 0) do
  429. begin
  430. BasePtr := Next(BaseLead);
  431. DestPtr := Next(DestLead);
  432. end;
  433. Result := '';
  434. while BaseLead <> nil do
  435. begin
  436. Result := Result + '..\'; { Do not localize }
  437. Next(BaseLead);
  438. end;
  439. if (DestPtr <> nil) and (DestPtr^ <> #0) then
  440. Result := Result + DestPtr + '\';
  441. if DestLead <> nil then
  442. Result := Result + DestLead; // destlead already has a trailing backslash
  443. Result := Result + PathExtractName(DestName);
  444. end
  445. else
  446. Result := DestName;
  447. end;
  448. { Use our own FileSearch function which includes these improvements over
  449. Delphi's version:
  450. - it supports MBCS and uses Path* functions
  451. - it uses NewFileExistsRedir instead of FileExists
  452. - it doesn't search the current directory unless it's told to
  453. - it always returns a fully-qualified path }
  454. function NewFileSearch(const DisableFsRedir: Boolean;
  455. const Name, DirList: String): String;
  456. var
  457. I, P, L: Integer;
  458. begin
  459. { If Name is absolute, drive-relative, or root-relative, don't search DirList }
  460. if PathDrivePartLengthEx(Name, True) <> 0 then begin
  461. Result := PathExpand(Name);
  462. if NewFileExistsRedir(DisableFsRedir, Result) then
  463. Exit;
  464. end
  465. else begin
  466. P := 1;
  467. L := Length(DirList);
  468. while True do begin
  469. while (P <= L) and (DirList[P] = ';') do
  470. Inc(P);
  471. if P > L then
  472. Break;
  473. I := P;
  474. while (P <= L) and (DirList[P] <> ';') do
  475. Inc(P, PathCharLength(DirList, P));
  476. Result := PathExpand(PathCombine(Copy(DirList, I, P - I), Name));
  477. if NewFileExistsRedir(DisableFsRedir, Result) then
  478. Exit;
  479. end;
  480. end;
  481. Result := '';
  482. end;
  483. function GetExceptionMessage(const Caller: TPSExec): String;
  484. var
  485. Code: TPSError;
  486. E: TObject;
  487. begin
  488. Code := Caller.LastEx;
  489. if Code = erNoError then
  490. Result := '(There is no current exception)'
  491. else begin
  492. E := Caller.LastExObject;
  493. if Assigned(E) and (E is Exception) then
  494. Result := Exception(E).Message
  495. else
  496. Result := String(PSErrorToString(Code, Caller.LastExParam));
  497. end;
  498. end;
  499. function GetCodePreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
  500. begin
  501. { do not localize or change the following string }
  502. Result := GetPreviousData(ExpandedAppId, 'Inno Setup CodeFile: ' + ValueName, DefaultValueData);
  503. end;
  504. { Also see RegisterUninstallInfo in Install.pas }
  505. function SetCodePreviousData(const PreviousDataKey: HKEY; const ValueName, ValueData: String): Boolean;
  506. begin
  507. if ValueData <> '' then begin
  508. { do not localize or change the following string }
  509. Result := RegSetValueEx(PreviousDataKey, PChar('Inno Setup CodeFile: ' + ValueName), 0, REG_SZ, PChar(ValueData), (Length(ValueData)+1)*SizeOf(ValueData[1])) = ERROR_SUCCESS
  510. end else
  511. Result := True;
  512. end;
  513. function LoadStringFromFile(const FileName: String; var S: AnsiString;
  514. const Sharing: TFileSharing): Boolean;
  515. var
  516. F: TFile;
  517. N: Cardinal;
  518. begin
  519. try
  520. F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenExisting, faRead, Sharing);
  521. try
  522. N := F.CappedSize;
  523. SetLength(S, N);
  524. F.ReadBuffer(S[1], N);
  525. finally
  526. F.Free;
  527. end;
  528. Result := True;
  529. except
  530. Result := False;
  531. end;
  532. end;
  533. function LoadStringsFromFile(const FileName: String; const Stack: TPSStack;
  534. const ItemNo: Longint; const Sharing: TFileSharing): Boolean;
  535. var
  536. F: TTextFileReader;
  537. begin
  538. try
  539. F := TTextFileReaderRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenExisting, faRead, Sharing);
  540. try
  541. var ArrayBuilder := Stack.InitArrayBuilder(ItemNo);
  542. while not F.Eof do
  543. ArrayBuilder.Add(F.ReadLine);
  544. finally
  545. F.Free;
  546. end;
  547. Result := True;
  548. except
  549. Result := False;
  550. end;
  551. end;
  552. function SaveStringToFile(const FileName: String; const S: AnsiString; Append: Boolean): Boolean;
  553. var
  554. F: TFile;
  555. begin
  556. try
  557. if Append then
  558. F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenAlways, faWrite, fsNone)
  559. else
  560. F := TFileRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
  561. try
  562. F.SeekToEnd;
  563. F.WriteAnsiString(S);
  564. finally
  565. F.Free;
  566. end;
  567. Result := True;
  568. except
  569. Result := False;
  570. end;
  571. end;
  572. function SaveStringsToFile(const FileName: String; const Stack: TPSStack;
  573. const ItemNo: Longint; Append, UTF8, UTF8WithoutBOM: Boolean): Boolean;
  574. var
  575. F: TTextFileWriter;
  576. begin
  577. try
  578. if Append then
  579. F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdOpenAlways, faWrite, fsNone)
  580. else
  581. F := TTextFileWriterRedir.Create(ScriptFuncDisableFsRedir, FileName, fdCreateAlways, faWrite, fsNone);
  582. try
  583. if UTF8 and UTF8WithoutBOM then
  584. F.UTF8WithoutBOM := UTF8WithoutBOM;
  585. var ArrayEnumerator := Stack.InitArrayEnumerator(ItemNo);
  586. while ArrayEnumerator.HasNext do begin
  587. var S := ArrayEnumerator.Next;
  588. if not UTF8 then
  589. F.WriteAnsiLine(AnsiString(S))
  590. else
  591. F.WriteLine(S);
  592. end;
  593. finally
  594. F.Free;
  595. end;
  596. Result := True;
  597. except
  598. Result := False;
  599. end;
  600. end;
  601. var
  602. ASMInliners: array of Pointer;
  603. function CreateCallback(const Caller: TPSExec; const P: PPSVariantProcPtr): LongWord;
  604. var
  605. ProcRec: TPSInternalProcRec;
  606. Method: TMethod;
  607. Inliner: TASMInline;
  608. ParamCount, SwapFirst, SwapLast: Integer;
  609. S: tbtstring;
  610. begin
  611. { ProcNo 0 means nil was passed by the script }
  612. if P.ProcNo = 0 then
  613. InternalError('Invalid Method value');
  614. { Calculate parameter count of our proc, will need this later. }
  615. ProcRec := Caller.GetProcNo(P.ProcNo) as TPSInternalProcRec;
  616. S := ProcRec.ExportDecl;
  617. GRFW(S);
  618. ParamCount := 0;
  619. while S <> '' do begin
  620. Inc(ParamCount);
  621. GRFW(S);
  622. end;
  623. { Turn our proc into a callable TMethod - its Code will point to
  624. ROPS' MyAllMethodsHandler and its Data to a record identifying our proc.
  625. When called, MyAllMethodsHandler will use the record to call our proc. }
  626. Method := MkMethod(Caller, P.ProcNo);
  627. { Wrap our TMethod with a dynamically generated stdcall callback which will
  628. do two things:
  629. -Remember the Data pointer which MyAllMethodsHandler needs.
  630. -Handle the calling convention mismatch.
  631. Based on InnoCallback by Sherlock Software, see
  632. http://www.sherlocksoftware.org/page.php?id=54 and
  633. https://github.com/thenickdude/InnoCallback. }
  634. Inliner := TASMInline.create;
  635. try
  636. Inliner.Pop(EAX); //get the retptr off the stack
  637. SwapFirst := 2;
  638. SwapLast := ParamCount-1;
  639. //Reverse the order of parameters from param3 onwards in the stack
  640. while SwapLast > SwapFirst do begin
  641. Inliner.Mov(ECX, Inliner.Addr(ESP, SwapFirst * 4)); //load the first item of the pair
  642. Inliner.Mov(EDX, Inliner.Addr(ESP, SwapLast * 4)); //load the last item of the pair
  643. Inliner.Mov(Inliner.Addr(ESP, SwapFirst * 4), EDX);
  644. Inliner.Mov(Inliner.Addr(ESP, SwapLast * 4), ECX);
  645. Inc(SwapFirst);
  646. Dec(SwapLast);
  647. end;
  648. if ParamCount >= 1 then
  649. Inliner.Pop(EDX); //load param1
  650. if ParamCount >= 2 then
  651. Inliner.Pop(ECX); //load param2
  652. Inliner.Push(EAX); //put the retptr back onto the stack
  653. Inliner.Mov(EAX, LongWord(Method.Data)); //Load the self ptr
  654. Inliner.Jmp(Method.Code); //jump to the wrapped proc
  655. SetLength(ASMInliners, Length(ASMInliners) + 1);
  656. ASMInliners[High(ASMInliners)] := Inliner.SaveAsMemory;
  657. Result := LongWord(ASMInliners[High(ASMInliners)]);
  658. finally
  659. Inliner.Free;
  660. end;
  661. end;
  662. procedure FreeASMInliners;
  663. var
  664. I: Integer;
  665. begin
  666. for I := 0 to High(ASMInliners) do
  667. FreeMem(ASMInliners[I]);
  668. SetLength(ASMInliners, 0);
  669. end;
  670. { TProgressThrottler }
  671. constructor TProgressThrottler.Create(const OnDownloadProgress: TOnDownloadProgress);
  672. begin
  673. inherited Create;
  674. FOnDownloadProgress := OnDownloadProgress;
  675. end;
  676. constructor TProgressThrottler.Create(const OnExtractionProgress: TOnExtractionProgress);
  677. begin
  678. inherited Create;
  679. FOnExtractionProgress := OnExtractionProgress;
  680. end;
  681. procedure TProgressThrottler.Reset;
  682. begin
  683. FStopWatch.Stop;
  684. end;
  685. function TProgressThrottler.ThrottleOk(const Progress, ProgressMax: Int64): Boolean;
  686. begin
  687. if FStopWatch.IsRunning then begin
  688. Result := ((Progress = ProgressMax) and (FLastOkProgress <> ProgressMax)) or (FStopWatch.ElapsedMilliseconds >= 50);
  689. if Result then
  690. FStopWatch.Reset;
  691. end else begin
  692. Result := True;
  693. FStopWatch := TStopwatch.StartNew;
  694. end;
  695. if Result then
  696. FLastOkProgress := Progress;
  697. end;
  698. function TProgressThrottler.OnDownloadProgress(const Url, BaseName: string; const Progress,
  699. ProgressMax: Int64): Boolean;
  700. begin
  701. if Assigned(FOnDownloadProgress) and ThrottleOk(Progress, ProgressMax) then begin
  702. Result := FOnDownloadProgress(Url, BaseName, Progress, ProgressMax)
  703. end else
  704. Result := True;
  705. end;
  706. function TProgressThrottler.OnExtractionProgress(const ArchiveName, FileName: string;
  707. const Progress, ProgressMax: Int64): Boolean;
  708. begin
  709. if Assigned(FOnExtractionProgress) and ThrottleOk(Progress, ProgressMax) then
  710. Result := FOnExtractionProgress(ArchiveName, FileName, Progress, ProgressMax)
  711. else
  712. Result := True;
  713. end;
  714. initialization
  715. finalization
  716. FreeASMInliners;
  717. end.