Setup.ScriptFunc.HelperFunc.pas 25 KB

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