Setup.ScriptFunc.HelperFunc.pas 22 KB

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