Compiler.ExeUpdateFunc.pas 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079
  1. unit Compiler.ExeUpdateFunc;
  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. PE header and resource update functions used by the compiler only
  8. }
  9. interface
  10. uses
  11. Windows, SysUtils, Shared.FileClass, Shared.VerInfoFunc, Shared.Struct;
  12. type
  13. TUpdateIconsAndStyleFile = (uisfSetupE32, uisfSetupCustomStyleE32, uisfSetupLdrE32);
  14. TUpdateIconsAndStyleOperation = (uisoIcoFileName, uisoWizardDarkStyle, uisoStyleFileName, uisoStyleFileNameDynamicDark, uisoDone);
  15. TOnUpdateIconsAndStyle = procedure(const Operation: TUpdateIconsAndStyleOperation) of object;
  16. function ReadSignatureAndChecksumFields(const F: TCustomFile;
  17. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  18. function ReadSignatureAndChecksumFields64(const F: TCustomFile;
  19. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  20. function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal;
  21. function UpdateSignatureAndChecksumFields(const F: TCustomFile;
  22. const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  23. procedure UpdateSetupPEHeaderFields(const F: TCustomFile;
  24. const IsTSAware, IsDEPCompatible, IsASLRCompatible: Boolean);
  25. procedure UpdateIconsAndStyle(const FileName: String; const Uisf: TUpdateIconsAndStyleFile; const IcoFileName: String;
  26. const WizardDarkStyle: TSetupWizardDarkStyle; const StyleFileName, StyleFileNameDynamicDark: String;
  27. const OnUpdateIconsAndStyle: TOnUpdateIconsAndStyle);
  28. procedure UpdateVersionInfo(const F: TCustomFile;
  29. const NewBinaryFileVersion, NewBinaryProductVersion: TFileVersionNumbers;
  30. const NewCompanyName, NewFileDescription, NewTextFileVersion, NewLegalCopyright,
  31. NewProductName, NewTextProductVersion, NewOriginalFileName: String;
  32. const SetFileVersionAndDescription: Boolean);
  33. procedure PreventCOMCTL32Sideloading(const F: TCustomFile);
  34. implementation
  35. uses
  36. Math;
  37. const
  38. IMAGE_NT_SIGNATURE = $00004550;
  39. IMAGE_NT_OPTIONAL_HDR32_MAGIC = $10b;
  40. IMAGE_NT_OPTIONAL_HDR64_MAGIC = $20b;
  41. IMAGE_NUMBEROF_DIRECTORY_ENTRIES = 16;
  42. IMAGE_SIZEOF_SHORT_NAME = 8;
  43. IMAGE_DIRECTORY_ENTRY_RESOURCE = 2;
  44. IMAGE_DIRECTORY_ENTRY_SECURITY = 4;
  45. type
  46. PImageFileHeader = ^TImageFileHeader;
  47. TImageFileHeader = packed record
  48. Machine: Word;
  49. NumberOfSections: Word;
  50. TimeDateStamp: DWORD;
  51. PointerToSymbolTable: DWORD;
  52. NumberOfSymbols: DWORD;
  53. SizeOfOptionalHeader: Word;
  54. Characteristics: Word;
  55. end;
  56. PImageDataDirectory = ^TImageDataDirectory;
  57. TImageDataDirectory = record
  58. VirtualAddress: DWORD;
  59. Size: DWORD;
  60. end;
  61. PImageOptionalHeader = ^TImageOptionalHeader;
  62. TImageOptionalHeader = packed record
  63. { Standard fields. }
  64. Magic: Word;
  65. MajorLinkerVersion: Byte;
  66. MinorLinkerVersion: Byte;
  67. SizeOfCode: DWORD;
  68. SizeOfInitializedData: DWORD;
  69. SizeOfUninitializedData: DWORD;
  70. AddressOfEntryPoint: DWORD;
  71. BaseOfCode: DWORD;
  72. BaseOfData: DWORD;
  73. { NT additional fields. }
  74. ImageBase: DWORD;
  75. SectionAlignment: DWORD;
  76. FileAlignment: DWORD;
  77. MajorOperatingSystemVersion: Word;
  78. MinorOperatingSystemVersion: Word;
  79. MajorImageVersion: Word;
  80. MinorImageVersion: Word;
  81. MajorSubsystemVersion: Word;
  82. MinorSubsystemVersion: Word;
  83. Win32VersionValue: DWORD;
  84. SizeOfImage: DWORD;
  85. SizeOfHeaders: DWORD;
  86. CheckSum: DWORD;
  87. Subsystem: Word;
  88. DllCharacteristics: Word;
  89. SizeOfStackReserve: DWORD;
  90. SizeOfStackCommit: DWORD;
  91. SizeOfHeapReserve: DWORD;
  92. SizeOfHeapCommit: DWORD;
  93. LoaderFlags: DWORD;
  94. NumberOfRvaAndSizes: DWORD;
  95. DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory;
  96. end;
  97. PImageOptionalHeader64 = ^TImageOptionalHeader64;
  98. TImageOptionalHeader64 = packed record
  99. { Standard fields. }
  100. Magic: Word;
  101. MajorLinkerVersion: Byte;
  102. MinorLinkerVersion: Byte;
  103. SizeOfCode: DWORD;
  104. SizeOfInitializedData: DWORD;
  105. SizeOfUninitializedData: DWORD;
  106. AddressOfEntryPoint: DWORD;
  107. BaseOfCode: DWORD;
  108. { NT additional fields. }
  109. ImageBase: Int64;
  110. SectionAlignment: DWORD;
  111. FileAlignment: DWORD;
  112. MajorOperatingSystemVersion: Word;
  113. MinorOperatingSystemVersion: Word;
  114. MajorImageVersion: Word;
  115. MinorImageVersion: Word;
  116. MajorSubsystemVersion: Word;
  117. MinorSubsystemVersion: Word;
  118. Win32VersionValue: DWORD;
  119. SizeOfImage: DWORD;
  120. SizeOfHeaders: DWORD;
  121. CheckSum: DWORD;
  122. Subsystem: Word;
  123. DllCharacteristics: Word;
  124. SizeOfStackReserve: Int64;
  125. SizeOfStackCommit: Int64;
  126. SizeOfHeapReserve: Int64;
  127. SizeOfHeapCommit: Int64;
  128. LoaderFlags: DWORD;
  129. NumberOfRvaAndSizes: DWORD;
  130. DataDirectory: packed array[0..IMAGE_NUMBEROF_DIRECTORY_ENTRIES-1] of TImageDataDirectory;
  131. end;
  132. TISHMisc = packed record
  133. case Integer of
  134. 0: (PhysicalAddress: DWORD);
  135. 1: (VirtualSize: DWORD);
  136. end;
  137. PImageSectionHeader = ^TImageSectionHeader;
  138. TImageSectionHeader = packed record
  139. Name: packed array[0..IMAGE_SIZEOF_SHORT_NAME-1] of Byte;
  140. Misc: TISHMisc;
  141. VirtualAddress: DWORD;
  142. SizeOfRawData: DWORD;
  143. PointerToRawData: DWORD;
  144. PointerToRelocations: DWORD;
  145. PointerToLinenumbers: DWORD;
  146. NumberOfRelocations: Word;
  147. NumberOfLinenumbers: Word;
  148. Characteristics: DWORD;
  149. end;
  150. TImageResourceDirectory = packed record
  151. Characteristics: DWORD;
  152. TimeDateStamp: DWORD;
  153. MajorVersion: Word;
  154. MinorVersion: Word;
  155. NumberOfNamedEntries: Word;
  156. NumberOfIdEntries: Word;
  157. end;
  158. TImageResourceDirectoryEntry = packed record
  159. Id: DWORD;
  160. Offset: DWORD;
  161. end;
  162. TImageResourceDataEntry = packed record
  163. OffsetToData: DWORD;
  164. Size: DWORD;
  165. CodePage: DWORD;
  166. Reserved: DWORD;
  167. end;
  168. procedure Error(const Msg: String);
  169. begin
  170. raise Exception.Create('Resource update error: ' + Msg);
  171. end;
  172. function SeekToPEHeader(const F: TCustomFile): Boolean;
  173. var
  174. DosHeader: packed record
  175. Sig: array[0..1] of AnsiChar;
  176. Other: array[0..57] of Byte;
  177. PEHeaderOffset: LongWord;
  178. end;
  179. Sig: DWORD;
  180. begin
  181. Result := False;
  182. F.Seek(0);
  183. if F.Read(DosHeader, SizeOf(DosHeader)) = SizeOf(DosHeader) then begin
  184. if (DosHeader.Sig[0] = 'M') and (DosHeader.Sig[1] = 'Z') and
  185. (DosHeader.PEHeaderOffset <> 0) then begin
  186. F.Seek(DosHeader.PEHeaderOffset);
  187. if F.Read(Sig, SizeOf(Sig)) = SizeOf(Sig) then
  188. if Sig = IMAGE_NT_SIGNATURE then
  189. Result := True;
  190. end;
  191. end;
  192. end;
  193. function SeekToAndReadPEOptionalHeader(const F: TCustomFile;
  194. var OptHeader: TImageOptionalHeader; var OptHeaderOffset: Int64): Boolean;
  195. var
  196. Header: TImageFileHeader;
  197. begin
  198. Result := False;
  199. if SeekToPEHeader(F) then begin
  200. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  201. (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin
  202. OptHeaderOffset := F.Position;
  203. if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then
  204. if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR32_MAGIC then
  205. Result := True;
  206. end;
  207. end;
  208. end;
  209. function SeekToAndReadPEOptionalHeader64(const F: TCustomFile;
  210. var OptHeader: TImageOptionalHeader64; var OptHeaderOffset: Int64): Boolean;
  211. var
  212. Header: TImageFileHeader;
  213. begin
  214. Result := False;
  215. if SeekToPEHeader(F) then begin
  216. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  217. (Header.SizeOfOptionalHeader = SizeOf(OptHeader)) then begin
  218. OptHeaderOffset := F.Position;
  219. if F.Read(OptHeader, SizeOf(OptHeader)) = SizeOf(OptHeader) then
  220. if OptHeader.Magic = IMAGE_NT_OPTIONAL_HDR64_MAGIC then
  221. Result := True;
  222. end;
  223. end;
  224. end;
  225. procedure FindResourceSection(const F: TCustomFile;
  226. var SectionVirtualAddr, SectionPhysOffset, SectionPhysSize: Cardinal);
  227. var
  228. EXESig: Word;
  229. PEHeaderOffset, PESig: Cardinal;
  230. PEHeader: TImageFileHeader;
  231. PEOptHeader: TImageOptionalHeader;
  232. PESectionHeader: TImageSectionHeader;
  233. I: Integer;
  234. begin
  235. { Read DOS header }
  236. F.Seek(0);
  237. F.ReadBuffer(EXESig, SizeOf(EXESig));
  238. if EXESig <> $5A4D {'MZ'} then
  239. Error('File isn''t an EXE file (1)');
  240. F.Seek($3C);
  241. F.ReadBuffer(PEHeaderOffset, SizeOf(PEHeaderOffset));
  242. if PEHeaderOffset = 0 then
  243. Error('File isn''t a PE file (1)');
  244. { Read PE header & optional header }
  245. F.Seek(PEHeaderOffset);
  246. F.ReadBuffer(PESig, SizeOf(PESig));
  247. if PESig <> $00004550 {'PE'#0#0} then
  248. Error('File isn''t a PE file (2)');
  249. F.ReadBuffer(PEHeader, SizeOf(PEHeader));
  250. if PEHeader.SizeOfOptionalHeader <> SizeOf(PEOptHeader) then
  251. Error('File isn''t a PE file (3)');
  252. F.ReadBuffer(PEOptHeader, SizeOf(PEOptHeader));
  253. if PEOptHeader.Magic <> IMAGE_NT_OPTIONAL_HDR32_MAGIC then
  254. Error('File isn''t a PE file (4)');
  255. { Scan section headers for resource section }
  256. if (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress = 0) or
  257. (PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = 0) then
  258. Error('No resources (1)');
  259. SectionVirtualAddr := PEOptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  260. SectionPhysOffset := 0;
  261. for I := 0 to PEHeader.NumberOfSections-1 do begin
  262. F.ReadBuffer(PESectionHeader, SizeOf(PESectionHeader));
  263. if (PESectionHeader.VirtualAddress = SectionVirtualAddr) and
  264. (PESectionHeader.SizeOfRawData <> 0) then begin
  265. SectionPhysOffset := PESectionHeader.PointerToRawData;
  266. SectionPhysSize := PESectionHeader.SizeOfRawData;
  267. Break;
  268. end;
  269. end;
  270. if SectionPhysOffset = 0 then
  271. Error('No resources (2)');
  272. end;
  273. function FindResOffset(const F: TCustomFile; const AnyId: Boolean;
  274. const Id: Cardinal; const FindSubdir: Boolean; var Offset: Cardinal): Boolean;
  275. var
  276. Dir: TImageResourceDirectory;
  277. Entry: TImageResourceDirectoryEntry;
  278. I: Integer;
  279. begin
  280. F.ReadBuffer(Dir, SizeOf(Dir));
  281. { Skip over named entries }
  282. for I := 0 to Dir.NumberOfNamedEntries-1 do
  283. F.ReadBuffer(Entry, SizeOf(Entry));
  284. { Now process ID entries }
  285. Result := False;
  286. for I := 0 to Dir.NumberOfIdEntries-1 do begin
  287. F.ReadBuffer(Entry, SizeOf(Entry));
  288. if (AnyId or (Entry.Id = Id)) and
  289. ((Entry.Offset and $80000000 <> 0) = FindSubdir) then begin
  290. Offset := Entry.Offset and $7FFFFFFF;
  291. Result := True;
  292. Break;
  293. end;
  294. end;
  295. end;
  296. function SeekToResourceData(const F: TCustomFile; const ResType, ResId: Cardinal): Cardinal;
  297. { Seeks to the specified resource's data, and returns its size. Raises an
  298. exception if the resource cannot be found. }
  299. var
  300. SectionVirtualAddr, SectionPhysOffset, SectionPhysSize, Ofs: Cardinal;
  301. DataEntry: TImageResourceDataEntry;
  302. begin
  303. FindResourceSection(F, SectionVirtualAddr, SectionPhysOffset, SectionPhysSize);
  304. { Scan the resource directory }
  305. F.Seek(SectionPhysOffset);
  306. if not FindResOffset(F, False, ResType, True, Ofs) then
  307. Error('Can''t find resource (1)');
  308. F.Seek(SectionPhysOffset + Ofs);
  309. if not FindResOffset(F, False, ResId, True, Ofs) then
  310. Error('Can''t find resource (2)');
  311. F.Seek(SectionPhysOffset + Ofs);
  312. if not FindResOffset(F, True, 0, False, Ofs) then
  313. Error('Can''t find resource (3).');
  314. F.Seek(SectionPhysOffset + Ofs);
  315. F.ReadBuffer(DataEntry, SizeOf(DataEntry));
  316. { Sanity check: DataEntry.OffsetToData is an RVA. It's technically possible
  317. for the RVA to point to a different section, but we don't support that. }
  318. if Cardinal(DataEntry.OffsetToData) < SectionVirtualAddr then
  319. Error('Invalid resource (1)');
  320. if Cardinal(DataEntry.OffsetToData - SectionVirtualAddr + DataEntry.Size) > SectionPhysSize then
  321. Error('Invalid resource (2)');
  322. { Seek to the resource }
  323. F.Seek(SectionPhysOffset + (DataEntry.OffsetToData - SectionVirtualAddr));
  324. Result := DataEntry.Size;
  325. end;
  326. function ReadSignatureAndChecksumFields(const F: TCustomFile;
  327. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  328. { Reads the signature and checksum fields in the specified file's header.
  329. If the file is not a valid PE32 executable, False is returned. }
  330. var
  331. OptHeader: TImageOptionalHeader;
  332. OptHeaderOffset: Int64;
  333. begin
  334. Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset);
  335. if Result then begin
  336. ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
  337. ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
  338. AChecksum := OptHeader.CheckSum;
  339. end;
  340. end;
  341. function ReadSignatureAndChecksumFields64(const F: TCustomFile;
  342. var ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  343. { Reads the signature and checksum fields in the specified file's header.
  344. If the file is not a valid PE32+ executable, False is returned. }
  345. var
  346. OptHeader: TImageOptionalHeader64;
  347. OptHeaderOffset: Int64;
  348. begin
  349. Result := SeekToAndReadPEOptionalHeader64(F, OptHeader, OptHeaderOffset);
  350. if Result then begin
  351. ASignatureAddress := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress;
  352. ASignatureSize := OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size;
  353. AChecksum := OptHeader.CheckSum;
  354. end;
  355. end;
  356. function UpdateSignatureAndChecksumFields(const F: TCustomFile;
  357. const ASignatureAddress, ASignatureSize, AChecksum: DWORD): Boolean;
  358. { Sets the signature and checksum fields in the specified file's header.
  359. If the file is not a valid PE32 executable, False is returned. }
  360. var
  361. OptHeader: TImageOptionalHeader;
  362. OptHeaderOffset: Int64;
  363. begin
  364. Result := SeekToAndReadPEOptionalHeader(F, OptHeader, OptHeaderOffset);
  365. if Result then begin
  366. OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].VirtualAddress := ASignatureAddress;
  367. OptHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY].Size := ASignatureSize;
  368. OptHeader.CheckSum := AChecksum;
  369. F.Seek(OptHeaderOffset);
  370. F.WriteBuffer(OptHeader, SizeOf(OptHeader));
  371. end;
  372. end;
  373. procedure UpdateSetupPEHeaderFields(const F: TCustomFile;
  374. const IsTSAware, IsDEPCompatible, IsASLRCompatible: Boolean);
  375. const
  376. IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = $0040;
  377. IMAGE_DLLCHARACTERISTICS_NX_COMPAT = $0100;
  378. IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE = $8000;
  379. OffsetOfDllCharacteristics = $46;
  380. var
  381. Header: TImageFileHeader;
  382. OptMagic, DllChars, OrigDllChars: Word;
  383. begin
  384. if SeekToPEHeader(F) then begin
  385. if (F.Read(Header, SizeOf(Header)) = SizeOf(Header)) and
  386. (Header.SizeOfOptionalHeader = SizeOf(TImageOptionalHeader)) then begin
  387. const Ofs = F.Position;
  388. if (F.Read(OptMagic, SizeOf(OptMagic)) = SizeOf(OptMagic)) and
  389. (OptMagic = IMAGE_NT_OPTIONAL_HDR32_MAGIC) then begin
  390. { Update DllCharacteristics }
  391. F.Seek(Ofs + OffsetOfDllCharacteristics);
  392. if F.Read(DllChars, SizeOf(DllChars)) = SizeOf(DllChars) then begin
  393. OrigDllChars := DllChars;
  394. if IsTSAware then
  395. DllChars := DllChars or IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE
  396. else
  397. DllChars := DllChars and not IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE;
  398. if IsDEPCompatible then
  399. DllChars := DllChars or IMAGE_DLLCHARACTERISTICS_NX_COMPAT
  400. else
  401. DllChars := DllChars and not IMAGE_DLLCHARACTERISTICS_NX_COMPAT;
  402. if IsASLRCompatible then
  403. DllChars := DllChars or IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE
  404. else
  405. DllChars := DllChars and not IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE;
  406. if DllChars <> OrigDllChars then begin
  407. F.Seek(Ofs + OffsetOfDllCharacteristics);
  408. F.WriteBuffer(DllChars, SizeOf(DllChars));
  409. end;
  410. Exit;
  411. end;
  412. end;
  413. end;
  414. end;
  415. raise Exception.Create('UpdateSetupPEHeaderFields failed');
  416. end;
  417. procedure ResUpdateError(const Msg: String; const ResourceName: String = '');
  418. begin
  419. if ResourceName <> '' then
  420. raise Exception.CreateFmt('Resource %s update error: %s', [ResourceName, Msg])
  421. else
  422. raise Exception.CreateFmt('Resource update error: %s', [Msg]);
  423. end;
  424. procedure ResUpdateErrorWithLastError(const Msg: String; const ResourceName: String = '');
  425. begin
  426. ResUpdateError(Msg + ' (' + IntToStr(GetLastError) + ')', ResourceName);
  427. end;
  428. procedure UpdateVersionInfo(const F: TCustomFile;
  429. const NewBinaryFileVersion, NewBinaryProductVersion: TFileVersionNumbers;
  430. const NewCompanyName, NewFileDescription, NewTextFileVersion, NewLegalCopyright,
  431. NewProductName, NewTextProductVersion, NewOriginalFileName: String;
  432. const SetFileVersionAndDescription: Boolean);
  433. function WideStrsEqual(P1, P2: PWideChar): Boolean;
  434. function WideUpCase(C: WideChar): WideChar;
  435. begin
  436. Result := C;
  437. if (Result >= 'a') and (Result <= 'z') then
  438. Dec(Result, Ord('a') - Ord('A'));
  439. end;
  440. begin
  441. while True do begin
  442. if WideUpCase(P1^) <> WideUpCase(P2^) then begin
  443. Result := False;
  444. Exit;
  445. end;
  446. if P1^ = #0 then
  447. Break;
  448. Inc(P1);
  449. Inc(P2);
  450. end;
  451. Result := True;
  452. end;
  453. procedure BumpToDWordBoundary(var P: Pointer);
  454. begin
  455. if Cardinal(P) and 3 <> 0 then
  456. Cardinal(P) := (Cardinal(P) or 3) + 1;
  457. end;
  458. function QueryValue(P: Pointer; Path: PWideChar; var Buf: Pointer;
  459. var BufLen: Cardinal): Boolean;
  460. var
  461. EndP: Pointer;
  462. ValueLength: Cardinal;
  463. begin
  464. Result := False;
  465. Cardinal(EndP) := Cardinal(P) + PWord(P)^;
  466. Inc(PWord(P));
  467. ValueLength := PWord(P)^;
  468. Inc(PWord(P));
  469. Inc(PWord(P));
  470. if WideStrsEqual(PWideChar(P), Path) then begin
  471. Inc(PWideChar(P), lstrlenW(P) + 1);
  472. BumpToDWordBoundary(P);
  473. Inc(Path, lstrlenW(Path) + 1);
  474. if Path^ = #0 then begin
  475. { Found the requested value }
  476. Buf := P;
  477. BufLen := ValueLength;
  478. Result := True;
  479. end
  480. else begin
  481. { Handle children.
  482. Note: Like VerQueryValue, we always treat ValueLength as a byte count
  483. when looking for child nodes. Many resource compilers, including
  484. Borland's, wrongly set ValueLength to a *character* count on string
  485. nodes. But since we never try to query for a child of a string node,
  486. that doesn't matter here. }
  487. Inc(Cardinal(P), ValueLength);
  488. BumpToDWordBoundary(P);
  489. while Cardinal(P) < Cardinal(EndP) do begin
  490. Result := QueryValue(P, Path, Buf, BufLen);
  491. if Result then
  492. Exit;
  493. Inc(Cardinal(P), PWord(P)^);
  494. BumpToDWordBoundary(P);
  495. end;
  496. end;
  497. end;
  498. end;
  499. procedure ReplaceWithRealCopyrightSymbols(const Value: PWideChar);
  500. var
  501. Len, I, J: Integer;
  502. begin
  503. Len := lstrlenW(Value);
  504. for I := 0 to Len-3 do begin
  505. if (Value[I] = '(') and (Value[I+1] = 'C') and (Value[I+2] = ')') then begin
  506. Value[I] := WideChar($00A9);
  507. { Shift back two characters }
  508. for J := I+1 to Len-3 do
  509. Value[J] := Value[J+2];
  510. Value[Len-2] := ' ';
  511. Value[Len-1] := ' ';
  512. end;
  513. end;
  514. end;
  515. procedure UpdateStringValue(P: Pointer; const Path: PWideChar; NewValue: String);
  516. var
  517. Value: PWideChar;
  518. ValueLen: Cardinal;
  519. begin
  520. if not QueryValue(P, Path, Pointer(Value), ValueLen) then
  521. ResUpdateError('Unexpected version resource format (1)');
  522. Move(Pointer(NewValue)^, Value^, (Min(Length(NewValue), lstrlenW(Value)))*SizeOf(Char));
  523. ReplaceWithRealCopyrightSymbols(Value);
  524. end;
  525. procedure UpdateFixedFileInfo(P: Pointer; const Path: PWideChar;
  526. const NewFileVersion, NewProductVersion: TFileVersionNumbers;
  527. const SetFileVersion: Boolean);
  528. var
  529. FixedFileInfo: PVSFixedFileInfo;
  530. ValueLen: Cardinal;
  531. begin
  532. if not QueryValue(P, Path, Pointer(FixedFileInfo), ValueLen) then
  533. ResUpdateError('Unexpected version resource format (2)');
  534. if FixedFileInfo.dwSignature <> $FEEF04BD then
  535. ResUpdateError('Unexpected version resource format (3)');
  536. if SetFileVersion then begin
  537. FixedFileInfo.dwFileVersionLS := NewFileVersion.LS;
  538. FixedFileInfo.dwFileVersionMS := NewFileVersion.MS;
  539. end;
  540. FixedFileInfo.dwProductVersionLS := NewProductVersion.LS;
  541. FixedFileInfo.dwProductVersionMS := NewProductVersion.MS;
  542. end;
  543. var
  544. ResSize: Cardinal;
  545. VersRes: Pointer;
  546. begin
  547. { Locate the resource }
  548. ResSize := SeekToResourceData(F, Cardinal(RT_VERSION), 1);
  549. const ResOffset = F.Position;
  550. GetMem(VersRes, ResSize);
  551. try
  552. { Read the resource }
  553. F.ReadBuffer(VersRes^, ResSize);
  554. { Update the resource }
  555. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'CompanyName'#0, NewCompanyName);
  556. if SetFileVersionAndDescription then begin
  557. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'FileDescription'#0, NewFileDescription);
  558. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'FileVersion'#0, NewTextFileVersion);
  559. end;
  560. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'LegalCopyright'#0, NewLegalCopyright);
  561. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'ProductName'#0, NewProductName);
  562. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'OriginalFileName'#0, NewOriginalFileName);
  563. UpdateStringValue(VersRes, 'VS_VERSION_INFO'#0'StringFileInfo'#0'000004b0'#0'ProductVersion'#0, NewTextProductVersion);
  564. UpdateFixedFileInfo(VersRes, 'VS_VERSION_INFO'#0, NewBinaryFileVersion, NewBinaryProductVersion, SetFileVersionAndDescription);
  565. { Write the updated resource }
  566. F.Seek(ResOffset);
  567. F.WriteBuffer(VersRes^, ResSize);
  568. finally
  569. FreeMem(VersRes);
  570. end;
  571. end;
  572. function EnumLangsFunc(hModule: Cardinal; lpType, lpName: PAnsiChar; wLanguage: Word; lParam: Integer): BOOL; stdcall;
  573. begin
  574. PWord(lParam)^ := wLanguage;
  575. Result := False;
  576. end;
  577. function GetResourceLanguage(hModule: Cardinal; lpType, lpName: PChar; var wLanguage: Word): Boolean;
  578. begin
  579. wLanguage := 0;
  580. EnumResourceLanguages(hModule, lpType, lpName, @EnumLangsFunc, Integer(@wLanguage));
  581. Result := True;
  582. end;
  583. procedure UpdateIconsAndStyle(const FileName: String; const Uisf: TUpdateIconsAndStyleFile; const IcoFileName: String;
  584. const WizardDarkStyle: TSetupWizardDarkStyle; const StyleFileName, StyleFileNameDynamicDark: String;
  585. const OnUpdateIconsAndStyle: TOnUpdateIconsAndStyle);
  586. type
  587. PIcoItemHeader = ^TIcoItemHeader;
  588. TIcoItemHeader = packed record
  589. Width: Byte;
  590. Height: Byte;
  591. Colors: Byte;
  592. Reserved: Byte;
  593. Planes: Word;
  594. BitCount: Word;
  595. ImageSize: DWORD;
  596. end;
  597. PIcoItem = ^TIcoItem;
  598. TIcoItem = packed record
  599. Header: TIcoItemHeader;
  600. Offset: DWORD;
  601. end;
  602. PIcoHeader = ^TIcoHeader;
  603. TIcoHeader = packed record
  604. Reserved: Word;
  605. Typ: Word;
  606. ItemCount: Word;
  607. Items: array [0..MaxInt shr 4 - 1] of TIcoItem;
  608. end;
  609. PGroupIconDirItem = ^TGroupIconDirItem;
  610. TGroupIconDirItem = packed record
  611. Header: TIcoItemHeader;
  612. Id: Word;
  613. end;
  614. PGroupIconDir = ^TGroupIconDir;
  615. TGroupIconDir = packed record
  616. Reserved: Word;
  617. Typ: Word;
  618. ItemCount: Word;
  619. Items: array [0..MaxInt shr 4 - 1] of TGroupIconDirItem;
  620. end;
  621. procedure TriggerOnUpdateIconsAndStyle(const Operation: TUpdateIconsAndStyleOperation);
  622. begin
  623. if Assigned(OnUpdateIconsAndStyle) then
  624. OnUpdateIconsAndStyle(Operation);
  625. end;
  626. function LoadFileIntoMemory(const FileName: String; var P: Pointer): Cardinal;
  627. begin
  628. const F = TFile.Create(FileName, fdOpenExisting, faRead, fsRead);
  629. try
  630. const N = F.CappedSize;
  631. if Cardinal(N) > Cardinal($100000) then { sanity check }
  632. ResUpdateError('File is too large');
  633. GetMem(P, N);
  634. F.ReadBuffer(P^, N);
  635. Result := N;
  636. finally
  637. F.Free;
  638. end;
  639. end;
  640. function IsValidIcon(P: Pointer; Size: Cardinal): Boolean;
  641. var
  642. ItemCount: Cardinal;
  643. begin
  644. Result := False;
  645. if Size < Cardinal(SizeOf(Word) * 3) then
  646. Exit;
  647. if (PChar(P)[0] = 'M') and (PChar(P)[1] = 'Z') then
  648. Exit;
  649. ItemCount := PIcoHeader(P).ItemCount;
  650. if Size < Cardinal((SizeOf(Word) * 3) + (ItemCount * SizeOf(TIcoItem))) then
  651. Exit;
  652. P := @PIcoHeader(P).Items;
  653. while ItemCount > Cardinal(0) do begin
  654. if (Cardinal(PIcoItem(P).Offset + PIcoItem(P).Header.ImageSize) < Cardinal(PIcoItem(P).Offset)) or
  655. (Cardinal(PIcoItem(P).Offset + PIcoItem(P).Header.ImageSize) > Cardinal(Size)) then
  656. Exit;
  657. Inc(PIcoItem(P));
  658. Dec(ItemCount);
  659. end;
  660. Result := True;
  661. end;
  662. function LoadResourcePointer(const M: HMODULE; const ResourceType, ResourceName: PChar;
  663. const WantSize: Boolean; out Size: DWORD): Pointer; overload;
  664. begin
  665. var R := FindResource(M, ResourceName, ResourceType);
  666. if R = 0 then
  667. ResUpdateErrorWithLastError('FindResource failed (1)', ResourceName);
  668. if WantSize then begin
  669. Size := SizeofResource(M, R);
  670. if Size = 0 then
  671. ResUpdateErrorWithLastError('SizeofResource failed (1)', ResourceName);
  672. end;
  673. var Res := LoadResource(M, R);
  674. if Res = 0 then
  675. ResUpdateErrorWithLastError('LoadResource failed (1)', ResourceName);
  676. Result := LockResource(Res);
  677. if Result = nil then
  678. ResUpdateErrorWithLastError('LockResource failed (1)', ResourceName);
  679. end;
  680. function LoadResourcePointer(const M: HMODULE; const ResourceType, ResourceName: PChar): Pointer; overload;
  681. begin
  682. var Dummy: DWORD;
  683. Result := LoadResourcePointer(M, ResourceType, ResourceName, False, Dummy);
  684. end;
  685. procedure DeleteResource(const H: THandle; const M: HMODULE; const ResourceType, ResourceName: PChar);
  686. var
  687. wLanguage: Word;
  688. begin
  689. if not GetResourceLanguage(M, ResourceType, ResourceName, wLanguage) then
  690. ResUpdateError('GetResourceLanguage failed (1)', ResourceName);
  691. if not UpdateResource(H, ResourceType, ResourceName, wLanguage, nil, 0) then
  692. ResUpdateErrorWithLastError('UpdateResource failed (1)', ResourceName);
  693. end;
  694. procedure RenameResource(const H: THandle; const M: HMODULE; const ResourceType, OldResourceName, NewResourceName: PChar);
  695. var
  696. Size: DWORD;
  697. P: Pointer;
  698. wLanguage: Word;
  699. begin
  700. { Load the resource }
  701. P := LoadResourcePointer(M, ResourceType, OldResourceName, True, Size);
  702. { Create a copy resource with the new name }
  703. if not GetResourceLanguage(M, ResourceType, OldResourceName, wLanguage) then
  704. ResUpdateError('GetResourceLanguage failed (2)', OldResourceName);
  705. if not UpdateResource(H, ResourceType, NewResourceName, wLanguage, P, Size) then
  706. ResUpdateErrorWithLastError('UpdateResource failed (2)', NewResourceName);
  707. { Delete the old resource }
  708. if not UpdateResource(H, ResourceType, OldResourceName, wLanguage, nil, 0) then
  709. ResUpdateErrorWithLastError('UpdateResource failed (3)', OldResourceName);
  710. end;
  711. function DeleteIcon(const H: THandle; const M: HMODULE; const ResourceName: PChar): PGroupIconDir;
  712. var
  713. GroupIconDir: PGroupIconDir;
  714. I: Integer;
  715. wLanguage: Word;
  716. begin
  717. { Load the group icon resource }
  718. GroupIconDir := LoadResourcePointer(M, RT_GROUP_ICON, ResourceName);
  719. { Delete the group icon resource }
  720. DeleteResource(H, M, RT_GROUP_ICON, ResourceName);
  721. { Delete the icon resources that belonged to the group }
  722. for I := 0 to GroupIconDir.ItemCount-1 do begin
  723. if not GetResourceLanguage(M, RT_ICON, MakeIntResource(GroupIconDir.Items[I].Id), wLanguage) then
  724. ResUpdateError('GetResourceLanguage failed (3)', ResourceName);
  725. if not UpdateResource(H, RT_ICON, MakeIntResource(GroupIconDir.Items[I].Id), wLanguage, nil, 0) then
  726. ResUpdateErrorWithLastError('UpdateResource failed (4)', ResourceName);
  727. end;
  728. Result := GroupIconDir;
  729. end;
  730. function DeleteIconIfExists(const H: THandle; const M: HMODULE; const ResourceName: PChar): PGroupIconDir;
  731. begin
  732. if FindResource(M, ResourceName, RT_GROUP_ICON) <> 0 then
  733. Result := DeleteIcon(H, M, ResourceName)
  734. else
  735. Result := nil;
  736. end;
  737. function RenameIconWithOverwrite(const H: THandle; const M: HMODULE; const OldResourceName, NewResourceName: PChar): PGroupIconDir;
  738. var
  739. GroupIconDir: PGroupIconDir;
  740. GroupIconDirSize: DWORD;
  741. wLanguage: Word;
  742. begin
  743. DeleteIconIfExists(H, M, NewResourceName);
  744. { Load the group icon resource }
  745. GroupIconDir := LoadResourcePointer(M, RT_GROUP_ICON, OldResourceName);
  746. GroupIconDirSize := Sizeof(Word)*3 + GroupIconDir.ItemCount*Sizeof(TGroupIconDirItem);
  747. { Create a copy group icon resource with the new name - existing icon resources will belong to
  748. it automatically }
  749. if not GetResourceLanguage(M, RT_GROUP_ICON, OldResourceName, wLanguage) then
  750. ResUpdateError('GetResourceLanguage failed (4)', OldResourceName);
  751. if not UpdateResource(H, RT_GROUP_ICON, NewResourceName, wLanguage, GroupIconDir, GroupIconDirSize) then
  752. ResUpdateErrorWithLastError('UpdateResource failed (5)', NewResourceName);
  753. { Delete the old group icon resource }
  754. if not UpdateResource(H, RT_GROUP_ICON, OldResourceName, wLanguage, nil, 0) then
  755. ResUpdateErrorWithLastError('UpdateResource failed (6)', OldResourceName);
  756. Result := GroupIconDir;
  757. end;
  758. function HandleBuiltinStyle(const M: HMODULE; const StyleFileName: String; var Vsf: Pointer; var VsfSize: Cardinal; const Dark: Boolean): Boolean;
  759. begin
  760. { Also see DeleteResource calls below }
  761. var StyleName: PChar := nil;
  762. if SameText(StyleFileName, 'builtin:polar') then begin
  763. if Dark then
  764. StyleName := 'POLAR_DARK'
  765. else
  766. StyleName := 'POLAR_LIGHT';
  767. end else if SameText(StyleFileName, 'builtin:windows11') then begin
  768. if Dark then
  769. StyleName := 'WINDOWS11_DARK'
  770. else
  771. StyleName := 'WINDOWS11_LIGHT';
  772. end else if SameText(StyleFileName, 'builtin:slate') then
  773. StyleName := 'SLATECLASSICO'
  774. else if SameText(StyleFileName, 'builtin:zircon') then
  775. StyleName := 'ZIRCON';
  776. Result := StyleName <> nil;
  777. if Result then
  778. Vsf := LoadResourcePointer(M, 'VCLSTYLE', StyleName, True, VsfSize)
  779. end;
  780. var
  781. H: THandle;
  782. M: HMODULE;
  783. OldGroupIconDir, NewGroupIconDir: PGroupIconDir;
  784. I: Integer;
  785. NewGroupIconDirSize: LongInt;
  786. begin
  787. var Ico: PIcoHeader := nil;
  788. var Vsf := nil;
  789. var ShouldFreeVsf := False;
  790. var VsfDynamicDark := nil;
  791. var ShouldFreeVsfDynamicDark := False;
  792. try
  793. if IcoFileName <> '' then begin
  794. TriggerOnUpdateIconsAndStyle(uisoIcoFileName);
  795. { Load the icons }
  796. var P: Pointer;
  797. const IcoSize = LoadFileIntoMemory(IcoFileName, P);
  798. Ico := P;
  799. { Ensure the icon is valid }
  800. if not IsValidIcon(Ico, IcoSize) then
  801. ResUpdateError('Icon file is invalid');
  802. end;
  803. { Update the resources }
  804. var ChangedMainIcon := False;
  805. H := BeginUpdateResource(PChar(FileName), False);
  806. if H = 0 then
  807. ResUpdateErrorWithLastError('BeginUpdateResource failed (1)');
  808. try
  809. M := LoadLibraryEx(PChar(FileName), 0, LOAD_LIBRARY_AS_DATAFILE);
  810. if M = 0 then
  811. ResUpdateErrorWithLastError('LoadLibraryEx failed (1)');
  812. try
  813. { Load the styles. Could be checked using TStyleManager.IsValidStyle but that requires using VCL units. }
  814. var VsfSize: Cardinal := 0;
  815. if StyleFileName <> '' then begin
  816. TriggerOnUpdateIconsAndStyle(uisoStyleFileName);
  817. if not HandleBuiltinStyle(M, StyleFileName, Vsf, VsfSize, WizardDarkStyle = wdsDark) then begin
  818. VsfSize := LoadFileIntoMemory(StyleFileName, Vsf);
  819. ShouldFreeVsf := True;
  820. end;
  821. end;
  822. var VsfSizeDynamicDark: Cardinal := 0;
  823. if StyleFileNameDynamicDark <> '' then begin
  824. TriggerOnUpdateIconsAndStyle(uisoStyleFileNameDynamicDark);
  825. if not HandleBuiltinStyle(M, StyleFileNameDynamicDark, VsfDynamicDark, VsfSizeDynamicDark, True) then begin
  826. VsfSizeDynamicDark := LoadFileIntoMemory(StyleFileNameDynamicDark, VsfDynamicDark);
  827. ShouldFreeVsfDynamicDark := True;
  828. end;
  829. end;
  830. { All of the following changes must be independent because updates are not immediate. For
  831. example, if you call DeleteIcon followed by FindResource then resource will still be found,
  832. until you call EndUpdateResource *and* reload the file using LoadLibrary }
  833. if IcoFileName <> '' then begin
  834. TriggerOnUpdateIconsAndStyle(uisoIcoFileName);
  835. const ResourceName = 'MAINICON';
  836. { Delete default icons }
  837. OldGroupIconDir := DeleteIcon(H, M, PChar(ResourceName));
  838. DeleteIconIfExists(H, M, PChar(ResourceName + '_DARK'));
  839. { Build the new group icon resource }
  840. NewGroupIconDirSize := 3*SizeOf(Word)+Ico.ItemCount*SizeOf(TGroupIconDirItem);
  841. GetMem(NewGroupIconDir, NewGroupIconDirSize);
  842. try
  843. { Build the new group icon resource }
  844. NewGroupIconDir.Reserved := OldGroupIconDir.Reserved;
  845. NewGroupIconDir.Typ := OldGroupIconDir.Typ;
  846. NewGroupIconDir.ItemCount := Ico.ItemCount;
  847. for I := 0 to NewGroupIconDir.ItemCount-1 do begin
  848. NewGroupIconDir.Items[I].Header := Ico.Items[I].Header;
  849. NewGroupIconDir.Items[I].Id := I+100; //start at 100 to avoid overwriting other icons that may exist
  850. end;
  851. { Update 'MAINICON' }
  852. for I := 0 to NewGroupIconDir.ItemCount-1 do
  853. if not UpdateResource(H, RT_ICON, MakeIntResource(NewGroupIconDir.Items[I].Id), 1033, Pointer(DWORD(Ico) + Ico.Items[I].Offset), Ico.Items[I].Header.ImageSize) then
  854. ResUpdateErrorWithLastError('UpdateResource failed (7)', ResourceName);
  855. { Update the icons }
  856. if not UpdateResource(H, RT_GROUP_ICON, 'MAINICON', 1033, NewGroupIconDir, NewGroupIconDirSize) then
  857. ResUpdateErrorWithLastError('UpdateResource failed (8)', ResourceName);
  858. ChangedMainIcon := True;
  859. finally
  860. FreeMem(NewGroupIconDir);
  861. end;
  862. end else begin
  863. if WizardDarkStyle <> wdsDynamic then begin
  864. TriggerOnUpdateIconsAndStyle(uisoWizardDarkStyle);
  865. if WizardDarkStyle = wdsLight then begin
  866. { Forced light: remove dark main icon }
  867. DeleteIconIfExists(H, M, 'MAINICON_DARK')
  868. end else begin
  869. { Forced dark: rename dark main icon to be the regular main icon }
  870. RenameIconWithOverwrite(H, M, 'MAINICON_DARK', 'MAINICON');
  871. ChangedMainIcon := True;
  872. end;
  873. end; { Else keep both main icons }
  874. end;
  875. if Uisf in [uisfSetupE32, uisfSetupCustomStyleE32] then begin
  876. const DeleteUninstallIcon = IcoFileName <> '';
  877. if DeleteUninstallIcon then begin
  878. TriggerOnUpdateIconsAndStyle(uisoIcoFileName);
  879. { Make UninstallProgressForm use the custom icon }
  880. DeleteIcon(H, M, 'Z_UNINSTALLICON');
  881. DeleteIconIfExists(H, M, 'Z_UNINSTALLICON_DARK');
  882. end;
  883. if WizardDarkStyle <> wdsDynamic then begin
  884. TriggerOnUpdateIconsAndStyle(uisoWizardDarkStyle);
  885. { Unlike for MAINICON (for which we don't have the choice) here it always uses DeleteIcon
  886. instead of also using RenameIcon, to avoid issues with Windows' icon cache }
  887. var Postfix := '';
  888. if WizardDarkStyle = wdsLight then
  889. Postfix := '_DARK';
  890. { Delete the icons we don't need: either the light ones or the dark ones }
  891. DeleteIconIfExists(H, M, PChar('Z_GROUPICON' + Postfix));
  892. if not DeleteUninstallIcon then
  893. DeleteIconIfExists(H, M, PChar('Z_UNINSTALLICON' + Postfix));
  894. end;
  895. if Uisf = uisfSetupCustomStyleE32 then begin
  896. if Vsf <> nil then begin
  897. TriggerOnUpdateIconsAndStyle(uisoStyleFileName);
  898. { Add the regular custom style, used by forced light, forced dark and dynamic light }
  899. if not UpdateResource(H, 'VCLSTYLE', 'MYSTYLE1', 1033, Vsf, VsfSize) then
  900. ResUpdateErrorWithLastError('UpdateResource failed (9)', 'MYSTYLE1');
  901. end;
  902. if VsfDynamicDark <> nil then begin
  903. TriggerOnUpdateIconsAndStyle(uisoStyleFileNameDynamicDark);
  904. { Add the dark custom style, used by dynamic dark only }
  905. if not UpdateResource(H, 'VCLSTYLE', 'MYSTYLE1_DARK', 1033, VsfDynamicDark, VsfSizeDynamicDark) then
  906. ResUpdateErrorWithLastError('UpdateResource failed (10)', 'MYSTYLE1_DARK');
  907. end;
  908. { See if we need to keep the built-in dark style }
  909. if (Vsf = nil) and (WizardDarkStyle = wdsDark) then begin
  910. TriggerOnUpdateIconsAndStyle(uisoWizardDarkStyle);
  911. { Forced dark without a custom style: make the built-in dark style the regular one }
  912. RenameResource(H, M, 'VCLSTYLE', 'WINDOWS11_DARK', 'MYSTYLE1');
  913. end else if (VsfDynamicDark = nil) and (WizardDarkStyle = wdsDynamic) then begin
  914. TriggerOnUpdateIconsAndStyle(uisoWizardDarkStyle);
  915. { Dynamic without a custom dark style: make the built-in dark style the dark one }
  916. RenameResource(H, M, 'VCLSTYLE', 'WINDOWS11_DARK', 'MYSTYLE1_DARK');
  917. end else begin
  918. TriggerOnUpdateIconsAndStyle(uisoWizardDarkStyle);
  919. { Forced dark with a custom style: delete the built-in dark style
  920. Or, dynamic with a custom dark style: same
  921. Or, forced light with or without a custom style: same
  922. Note: forced light without a custom style doesn't actually use SetupCustomStyle.e32 at the moment so won't get here }
  923. DeleteResource(H, M, 'VCLSTYLE', 'WINDOWS11_DARK');
  924. end;
  925. { Delete additional styles - they are handled above }
  926. DeleteResource(H, M, 'VCLSTYLE', 'WINDOWS11_LIGHT');
  927. DeleteResource(H, M, 'VCLSTYLE', 'POLAR_LIGHT');
  928. DeleteResource(H, M, 'VCLSTYLE', 'POLAR_DARK');
  929. DeleteResource(H, M, 'VCLSTYLE', 'SLATECLASSICO');
  930. DeleteResource(H, M, 'VCLSTYLE', 'ZIRCON');
  931. end;
  932. end;
  933. TriggerOnUpdateIconsAndStyle(uisoDone);
  934. finally
  935. FreeLibrary(M);
  936. end;
  937. except
  938. EndUpdateResource(H, True); { discard changes }
  939. raise;
  940. end;
  941. if not EndUpdateResource(H, False) then
  942. if ChangedMainIcon then { Only allow errors (likely from faulty AV software) if the update actually is important }
  943. ResUpdateErrorWithLastError('EndUpdateResource failed, try excluding the Output folder from your antivirus software');
  944. finally
  945. if ShouldFreeVsfDynamicDark then
  946. FreeMem(VsfDynamicDark);
  947. if ShouldFreeVsf then
  948. FreeMem(Vsf);
  949. if Ico <> nil then
  950. FreeMem(Ico);
  951. end;
  952. end;
  953. { Replaces the entire comctl32 dependency section of the manifest with spaces, then inserts a
  954. comctl32 file entry before the other entries. Intended for SetupLdr only. Note: The exact number
  955. of spaces is calculated to allow seamless in-place editing. }
  956. procedure PreventCOMCTL32Sideloading(const F: TCustomFile);
  957. const
  958. DependencyStartTag: AnsiString = '<dependency>';
  959. DependencyEndTag: AnsiString = '</dependency>';
  960. FileStartTag: AnsiString = '<file name="';
  961. COMCTL32Entry: AnsiString = '<file name="comctl32.dll" loadFrom="%SystemRoot%\system32\" />'#13#10;
  962. var
  963. S: AnsiString;
  964. P,Q,R: Integer;
  965. begin
  966. { Read the manifest resource into a string }
  967. SetString(S, nil, SeekToResourceData(F, 24, 1));
  968. var Offset := F.Position;
  969. F.ReadBuffer(S[1], Length(S));
  970. { Locate and update the <dependency> tag }
  971. P := Pos(DependencyStartTag, S);
  972. if P = 0 then
  973. ResUpdateError('<dependency> tag not found');
  974. Q := Pos(DependencyEndTag, S);
  975. if Q <= P then
  976. ResUpdateError('<dependency> end tag not found');
  977. Q := Q+Length(DependencyEndTag);
  978. if Length(COMCTL32Entry) > Q-P then
  979. ResUpdateError('<dependency> tag shorter than replacement');
  980. R := Pos(FileStartTag, S);
  981. if R <= Q then
  982. ResUpdateError('<dependency> end tag after <file>?');
  983. Inc(Offset, P-1);
  984. F.Seek(Offset);
  985. F.WriteAnsiString(AnsiString(Format('%*s', [Q-P-Length(COMCTL32Entry), ' '])));
  986. F.WriteAnsiString(AnsiString(Copy(S, Q, R-Q)));
  987. F.WriteAnsiString(COMCTL32Entry);
  988. end;
  989. end.