Setup.ScriptFunc.HelperFunc.pas 23 KB

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