Setup.MainFunc.pas 145 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820
  1. unit Setup.MainFunc;
  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. Setup main functions and global variables
  8. }
  9. interface
  10. uses
  11. Windows, SysUtils, Messages, Classes, Graphics, Controls, Forms, Dialogs,
  12. StdCtrls, Shared.Struct, Shared.DebugStruct, Shared.Int64Em, Shared.CommonFunc.Vcl, Shared.CommonFunc,
  13. Shared.SetupTypes, Setup.ScriptRunner, RestartManager;
  14. type
  15. TEntryType = (seLanguage, seCustomMessage, sePermission, seType, seComponent,
  16. seTask, seDir, seFile, seFileLocation, seIcon, seIni, seRegistry,
  17. seInstallDelete, seUninstallDelete, seRun, seUninstallRun);
  18. TShellFolderID = (sfDesktop, sfStartMenu, sfPrograms, sfStartup, sfSendTo, //these have common and user versions
  19. sfFonts, sfAppData, sfDocs, sfTemplates, //
  20. sfFavorites, sfLocalAppData, sfUserProgramFiles, sfUserCommonFiles, sfUserSavedGames); //these only have user versions
  21. const
  22. EntryStrings: array[TEntryType] of Integer = (SetupLanguageEntryStrings,
  23. SetupCustomMessageEntryStrings, SetupPermissionEntryStrings,
  24. SetupTypeEntryStrings, SetupComponentEntryStrings, SetupTaskEntryStrings,
  25. SetupDirEntryStrings, SetupFileEntryStrings, SetupFileLocationEntryStrings,
  26. SetupIconEntryStrings, SetupIniEntryStrings, SetupRegistryEntryStrings,
  27. SetupDeleteEntryStrings, SetupDeleteEntryStrings, SetupRunEntryStrings,
  28. SetupRunEntryStrings);
  29. EntryAnsiStrings: array[TEntryType] of Integer = (SetupLanguageEntryAnsiStrings,
  30. SetupCustomMessageEntryAnsiStrings, SetupPermissionEntryAnsiStrings,
  31. SetupTypeEntryAnsiStrings, SetupComponentEntryAnsiStrings, SetupTaskEntryAnsiStrings,
  32. SetupDirEntryAnsiStrings, SetupFileEntryAnsiStrings, SetupFileLocationEntryAnsiStrings,
  33. SetupIconEntryAnsiStrings, SetupIniEntryAnsiStrings, SetupRegistryEntryAnsiStrings,
  34. SetupDeleteEntryAnsiStrings, SetupDeleteEntryAnsiStrings, SetupRunEntryAnsiStrings,
  35. SetupRunEntryAnsiStrings);
  36. { Exit codes that are assigned to the SetupExitCode variable.
  37. Note: SetupLdr also returns exit codes with the same numbers. }
  38. ecInitializationError = 1; { Setup failed to initialize. }
  39. ecCancelledBeforeInstall = 2; { User clicked Cancel before the actual
  40. installation started. }
  41. ecNextStepError = 3; { A fatal exception occurred while moving to
  42. the next step. }
  43. ecInstallationError = 4; { A fatal exception occurred during
  44. installation. }
  45. ecInstallationCancelled = 5; { User clicked Cancel during installation,
  46. or clicked Abort at an Abort-Retry-Ignore
  47. dialog. }
  48. ecKilledByDebugger = 6; { User killed the Setup process from within
  49. the debugger. }
  50. ecPrepareToInstallFailed = 7; { Stopped on Preparing to Install page;
  51. restart not needed. }
  52. ecPrepareToInstallFailedRestartNeeded = 8;
  53. { Stopped on Preparing to Install page;
  54. restart needed. }
  55. CodeRunnerNamingAttribute = 'Event';
  56. var
  57. { Variables for command line parameters }
  58. SetupLdrMode: Boolean;
  59. SetupLdrOriginalFilename: String;
  60. SetupLdrOffset0, SetupLdrOffset1: Longint;
  61. SetupNotifyWndPresent: Boolean;
  62. SetupNotifyWnd: HWND;
  63. InitLang: String;
  64. InitDir, InitProgramGroup: String;
  65. InitLoadInf, InitSaveInf: String;
  66. InitNoIcons, InitSilent, InitVerySilent, InitNoRestart, InitCloseApplications,
  67. InitNoCloseApplications, InitForceCloseApplications, InitNoForceCloseApplications,
  68. InitLogCloseApplications, InitRestartApplications, InitNoRestartApplications,
  69. InitNoCancel: Boolean;
  70. InitSetupType: String;
  71. InitComponents, InitTasks: TStringList;
  72. InitComponentsSpecified: Boolean;
  73. InitDeselectAllTasks: Boolean;
  74. InitPassword: String;
  75. InitRestartExitCode: Integer;
  76. InitPrivilegesRequired: TSetupPrivilegesRequired;
  77. HasInitPrivilegesRequired: Boolean;
  78. InitSuppressMsgBoxes: Boolean;
  79. DetachedUninstMsgFile: Boolean;
  80. NewParamsForCode: TStringList;
  81. { Debugger }
  82. OriginalEntryIndexes: array[TEntryType] of TList;
  83. { 'Constants' }
  84. SourceDir, TempInstallDir, WinDir, WinSystemDir, WinSysWow64Dir, WinSysNativeDir, SystemDrive,
  85. ProgramFiles32Dir, CommonFiles32Dir, ProgramFiles64Dir, CommonFiles64Dir,
  86. CmdFilename, SysUserInfoName,
  87. SysUserInfoOrg, UninstallExeFilename: String;
  88. { Uninstall 'constants' }
  89. UninstallExpandedAppId, UninstallExpandedApp, UninstallExpandedGroup,
  90. UninstallExpandedGroupName, UninstallExpandedLanguage: String;
  91. UninstallSilent: Boolean;
  92. { Variables read in from the SETUP.0 file }
  93. SetupHeader: TSetupHeader;
  94. LangOptions: TSetupLanguageEntry;
  95. Entries: array[TEntryType] of TList;
  96. WizardImages: TList;
  97. WizardSmallImages: TList;
  98. CloseApplicationsFilterList, CloseApplicationsFilterExcludesList: TStringList;
  99. { User options }
  100. ActiveLanguage: Integer = -1;
  101. ActiveLicenseText, ActiveInfoBeforeText, ActiveInfoAfterText: AnsiString;
  102. WizardUserInfoName, WizardUserInfoOrg, WizardUserInfoSerial, WizardDirValue, WizardGroupValue: String;
  103. WizardNoIcons, WizardPreparingYesRadio: Boolean;
  104. WizardSetupType: PSetupTypeEntry;
  105. WizardComponents, WizardDeselectedComponents, WizardTasks, WizardDeselectedTasks: TStringList;
  106. NeedToAbortInstall: Boolean;
  107. { Check/BeforeInstall/AfterInstall 'constants' }
  108. CheckOrInstallCurrentFilename, CheckOrInstallCurrentSourceFilename: String;
  109. { RestartManager API state.
  110. Note: the handle and key might change while running, see TWizardForm.QueryRestartManager. }
  111. RmSessionStarted, RmFoundApplications, RmDoRestart: Boolean;
  112. RmSessionHandle: DWORD;
  113. RmSessionKey: array[0..CCH_RM_SESSION_KEY] of WideChar;
  114. RmRegisteredFilesCount: Integer;
  115. { Other }
  116. ShowLanguageDialog, MatchedLangParameter: Boolean;
  117. InstallMode: (imNormal, imSilent, imVerySilent);
  118. HasIcons, IsWin64, Is64BitInstallMode, IsAdmin, IsPowerUserOrAdmin, IsAdminInstallMode,
  119. NeedPassword, NeedSerial, NeedsRestart, RestartSystem,
  120. IsUninstaller, AllowUninstallerShutdown, AcceptedQueryEndSessionInProgress: Boolean;
  121. InstallDefaultDisableFsRedir, ScriptFuncDisableFsRedir: Boolean;
  122. InstallDefaultRegView: TRegView = rvDefault;
  123. HasCustomType, HasComponents, HasTasks: Boolean;
  124. ProcessorArchitecture: TSetupProcessorArchitecture = paUnknown;
  125. MachineTypesSupportedBySystem: TSetupProcessorArchitectures;
  126. WindowsVersion: Cardinal;
  127. NTServicePackLevel: Word;
  128. WindowsProductType: Byte;
  129. WindowsSuiteMask: Word;
  130. MinimumSpace: Integer64;
  131. DeleteFilesAfterInstallList, DeleteDirsAfterInstallList: TStringList;
  132. ExpandedAppName, ExpandedAppVerName, ExpandedAppCopyright, ExpandedAppMutex: String;
  133. DisableCodeConsts: Integer;
  134. SetupExitCode: Integer;
  135. CreatedIcon: Boolean;
  136. RestartInitiatedByThisProcess, DownloadTemporaryFileOrExtract7ZipArchiveProcessMessages: Boolean;
  137. InstallModeRootKey: HKEY;
  138. CodeRunner: TScriptRunner;
  139. procedure CodeRunnerOnLog(const S: String);
  140. procedure CodeRunnerOnLogFmt(const S: String; const Args: array of const);
  141. function CodeRunnerOnDebug(const Position: LongInt;
  142. var ContinueStepOver: Boolean): Boolean;
  143. function CodeRunnerOnDebugIntermediate(const Position: LongInt;
  144. var ContinueStepOver: Boolean): Boolean;
  145. procedure CodeRunnerOnDllImport(var DllName: String; var ForceDelayLoad: Boolean);
  146. procedure CodeRunnerOnException(const Exception: AnsiString; const Position: LongInt);
  147. procedure CreateTempInstallDirAndExtract64BitHelper;
  148. procedure DebugNotifyEntry(EntryType: TEntryType; Number: Integer);
  149. procedure DeinitSetup(const AllowCustomSetupExitCode: Boolean);
  150. function ExitSetupMsgBox: Boolean;
  151. function ExpandConst(const S: String): String;
  152. function ExpandConstEx(const S: String; const CustomConsts: array of String): String;
  153. function ExpandConstEx2(const S: String; const CustomConsts: array of String;
  154. const DoExpandIndividualConst: Boolean): String;
  155. function ExpandConstIfPrefixed(const S: String): String;
  156. function GetCustomMessageValue(const AName: String; var AValue: String): Boolean;
  157. function GetShellFolder(const Common: Boolean; const ID: TShellFolderID): String;
  158. function GetShellFolderByCSIDL(Folder: Integer; const Create: Boolean): String;
  159. function GetUninstallRegKeyBaseName(const ExpandedAppId: String): String;
  160. function GetUninstallRegSubkeyName(const UninstallRegKeyBaseName: String): String;
  161. function GetPreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
  162. function GetPreviousLanguage(const ExpandedAppID: String): Integer;
  163. procedure InitializeAdminInstallMode(const AAdminInstallMode: Boolean);
  164. procedure Initialize64BitInstallMode(const A64BitInstallMode: Boolean);
  165. procedure Log64BitInstallMode;
  166. procedure InitializeCommonVars;
  167. procedure InitializeSetup;
  168. procedure InitializeWizard;
  169. procedure InitMainNonSHFolderConsts;
  170. function InstallOnThisVersion(const MinVersion: TSetupVersionData;
  171. const OnlyBelowVersion: TSetupVersionData): TInstallOnThisVersionResult;
  172. function IsRecurseableDirectory(const FindData: TWin32FindData): Boolean;
  173. procedure LoadSHFolderDLL;
  174. function LoggedAppMessageBox(const Text, Caption: PChar; const Flags: Longint;
  175. const Suppressible: Boolean; const Default: Integer): Integer;
  176. function LoggedMsgBox(const Text, Caption: String; const Typ: TMsgBoxType;
  177. const Buttons: Cardinal; const Suppressible: Boolean; const Default: Integer): Integer;
  178. function LoggedTaskDialogMsgBox(const Icon, Instruction, Text, Caption: String;
  179. const Typ: TMsgBoxType; const Buttons: Cardinal; const ButtonLabels: array of String;
  180. const ShieldButton: Integer; const Suppressible: Boolean; const Default: Integer;
  181. const VerificationText: String = ''; const pfVerificationFlagChecked: PBOOL = nil): Integer;
  182. procedure LogWindowsVersion;
  183. procedure NotifyAfterInstallEntry(const AfterInstall: String);
  184. procedure NotifyAfterInstallFileEntry(const FileEntry: PSetupFileEntry);
  185. procedure NotifyBeforeInstallEntry(const BeforeInstall: String);
  186. procedure NotifyBeforeInstallFileEntry(const FileEntry: PSetupFileEntry);
  187. function PreviousInstallCompleted(const WizardComponents, WizardTasks: TStringList): Boolean;
  188. function CodeRegisterExtraCloseApplicationsResource(const DisableFsRedir: Boolean; const AFilename: String): Boolean;
  189. procedure RegisterResourcesWithRestartManager(const WizardComponents, WizardTasks: TStringList);
  190. procedure RemoveTempInstallDir;
  191. procedure SaveInf(const FileName: String);
  192. procedure SaveResourceToTempFile(const ResName, Filename: String);
  193. procedure SetActiveLanguage(const I: Integer);
  194. procedure ShellExecuteAsOriginalUser(hWnd: HWND; Operation, FileName, Parameters, Directory: LPWSTR; ShowCmd: Integer); stdcall;
  195. function ShouldDisableFsRedirForFileEntry(const FileEntry: PSetupFileEntry): Boolean;
  196. function ShouldDisableFsRedirForRunEntry(const RunEntry: PSetupRunEntry): Boolean;
  197. procedure ProcessRunEntry(const RunEntry: PSetupRunEntry);
  198. function EvalArchitectureIdentifier(const Name: String): Boolean;
  199. function EvalDirectiveCheck(const Expression: String): Boolean;
  200. function ShouldProcessEntry(const WizardComponents, WizardTasks: TStringList;
  201. const Components, Tasks, Languages, Check: String): Boolean;
  202. function ShouldProcessFileEntry(const WizardComponents, WizardTasks: TStringList;
  203. const FileEntry: PSetupFileEntry; const IgnoreCheck: Boolean): Boolean;
  204. function ShouldProcessIconEntry(const WizardComponents, WizardTasks: TStringList;
  205. const WizardNoIcons: Boolean; const IconEntry: PSetupIconEntry): Boolean;
  206. function ShouldProcessRunEntry(const WizardComponents, WizardTasks: TStringList;
  207. const RunEntry: PSetupRunEntry): Boolean;
  208. function TestPassword(const EncryptionKey: TSetupEncryptionKey): Boolean;
  209. procedure UnloadSHFolderDLL;
  210. function WindowsVersionAtLeast(const AMajor, AMinor: Byte; const ABuild: Word = 0): Boolean;
  211. function IsWindows8: Boolean;
  212. function IsWindows10: Boolean;
  213. function IsWindows11: Boolean;
  214. implementation
  215. uses
  216. ShellAPI, ShlObj, StrUtils, ActiveX, RegStr, ChaCha20,
  217. SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.Install, SetupLdrAndSetup.InstFunc,
  218. Setup.InstFunc, SetupLdrAndSetup.RedirFunc, PathFunc,
  219. Compression.Base, Compression.Zlib, Compression.bzlib, Compression.LZMADecompressor,
  220. Shared.SetupEntFunc, Setup.SelectLanguageForm,
  221. Setup.WizardForm, Setup.DebugClient, Shared.VerInfoFunc, Setup.FileExtractor,
  222. Shared.FileClass, Setup.LoggingFunc,
  223. SimpleExpression, Setup.Helper, Setup.SpawnClient, Setup.SpawnServer,
  224. Setup.DotNetFunc, Shared.TaskDialogFunc, Setup.MainForm;
  225. var
  226. ShellFolders: array[Boolean, TShellFolderID] of String;
  227. ShellFoldersRead: array[Boolean, TShellFolderID] of Boolean;
  228. SHFolderDLLHandle: HMODULE;
  229. SHGetFolderPathFunc: function(hwndOwner: HWND; nFolder: Integer;
  230. hToken: THandle; dwFlags: DWORD; pszPath: PChar): HRESULT; stdcall;
  231. SHGetKnownFolderPathFunc: function(const rfid: TGUID; dwFlags: DWORD; hToken: THandle;
  232. var ppszPath: PWideChar): HRESULT; stdcall;
  233. DecompressorDLLHandle: HMODULE;
  234. type
  235. TDummyClass = class
  236. public
  237. class function ExpandCheckOrInstallConstant(Sender: TSimpleExpression;
  238. const Constant: String): String;
  239. class function EvalInstallIdentifier(Sender: TSimpleExpression;
  240. const Name: String; const Parameters: array of const): Boolean;
  241. class function EvalArchitectureIdentifier(Sender: TSimpleExpression;
  242. const Name: String; const Parameters: array of const): Boolean;
  243. class function EvalComponentOrTaskIdentifier(Sender: TSimpleExpression;
  244. const Name: String; const Parameters: array of const): Boolean;
  245. class function EvalLanguageIdentifier(Sender: TSimpleExpression;
  246. const Name: String; const Parameters: array of const): Boolean;
  247. class function EvalCheckIdentifier(Sender: TSimpleExpression;
  248. const Name: String; const Parameters: array of const): Boolean;
  249. end;
  250. { Misc. functions }
  251. function WindowsVersionAtLeast(const AMajor, AMinor: Byte; const ABuild: Word): Boolean;
  252. begin
  253. Result := WindowsVersion >= Cardinal((AMajor shl 24) or (AMinor shl 16) or ABuild);
  254. end;
  255. function IsWindows8: Boolean;
  256. begin
  257. Result := WindowsVersionAtLeast(6, 2);
  258. end;
  259. function IsWindows10: Boolean;
  260. begin
  261. Result := WindowsVersionAtLeast(10, 0);
  262. end;
  263. function IsWindows11: Boolean;
  264. begin
  265. Result := WindowsVersionAtLeast(10, 0, 22000);
  266. end;
  267. function GetUninstallRegKeyBaseName(const ExpandedAppId: String): String;
  268. var
  269. UseAnsiCRC32: Boolean;
  270. S: AnsiString;
  271. I: Integer;
  272. begin
  273. { Set uninstall registry key base name }
  274. Result := ExpandedAppId;
  275. { Uninstall registry keys can only be up to 63 characters, otherwise Win95
  276. ignores them. Limit to 57 since Setup will add _isXXX to the end later. }
  277. if Length(Result) > 57 then begin
  278. { Only keep the first 48 characters, then add an tilde and the CRC
  279. of the original string (to make the trimmed string unique). The
  280. resulting string is 57 characters long. On Unicode, only do this if we
  281. can get a CRC32 compatible with ANSI versions, else there's no point
  282. in shortening since Unicode doesn't run on Win95. }
  283. UseAnsiCRC32 := True;
  284. for I := 1 to Length(Result) do begin
  285. if Ord(Result[I]) > 126 then begin
  286. UseAnsiCRC32 := False;
  287. Break;
  288. end;
  289. end;
  290. if UseAnsiCRC32 then begin
  291. S := AnsiString(Result);
  292. FmtStr(Result, '%.48s~%.8x', [Result, GetCRC32(S[1], Length(S)*SizeOf(S[1]))]);
  293. end;
  294. end;
  295. end;
  296. function GetUninstallRegSubkeyName(const UninstallRegKeyBaseName: String): String;
  297. begin
  298. Result := Format('%s\%s_is1', [REGSTR_PATH_UNINSTALL, UninstallRegKeyBaseName]);
  299. end;
  300. { Based on FindPreviousData in Wizard.pas }
  301. function GetPreviousData(const ExpandedAppID, ValueName, DefaultValueData: String): String;
  302. var
  303. H: HKEY;
  304. begin
  305. Result := DefaultValueData;
  306. if ExpandedAppId <> '' then begin
  307. if RegOpenKeyExView(InstallDefaultRegView, InstallModeRootKey,
  308. PChar(GetUninstallRegSubkeyName(GetUninstallRegKeyBaseName(ExpandedAppId))),
  309. 0, KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
  310. try
  311. RegQueryStringValue (H, PChar(ValueName), Result);
  312. finally
  313. RegCloseKey (H);
  314. end;
  315. end;
  316. end;
  317. end;
  318. function GetPreviousLanguage(const ExpandedAppID: String): Integer;
  319. var
  320. PrevLang: String;
  321. I: Integer;
  322. begin
  323. { do not localize or change the following string }
  324. PrevLang := GetPreviousData(ExpandConst(SetupHeader.AppId), 'Inno Setup: Language', '');
  325. if PrevLang <> '' then begin
  326. for I := 0 to Entries[seLanguage].Count-1 do begin
  327. if CompareText(PrevLang, PSetupLanguageEntry(Entries[seLanguage][I]).Name) = 0 then begin
  328. Result := I;
  329. Exit;
  330. end;
  331. end;
  332. end;
  333. Result := -1;
  334. end;
  335. { This function assumes EncryptionKey is based on the password }
  336. function TestPassword(const EncryptionKey: TSetupEncryptionKey): Boolean;
  337. begin
  338. { Do same as compiler did in GeneratePasswordTest and compare results }
  339. var Nonce := SetupHeader.EncryptionBaseNonce;
  340. Nonce.RandomXorFirstSlice := Nonce.RandomXorFirstSlice xor -1;
  341. var Context: TChaCha20Context;
  342. XChaCha20Init(Context, EncryptionKey[0], Length(EncryptionKey), Nonce, SizeOf(Nonce), 0);
  343. var PasswordTest := 0;
  344. XChaCha20Crypt(Context, PasswordTest, PasswordTest, SizeOf(PasswordTest));
  345. Result := PasswordTest = SetupHeader.PasswordTest;
  346. end;
  347. class function TDummyClass.ExpandCheckOrInstallConstant(Sender: TSimpleExpression;
  348. const Constant: String): String;
  349. begin
  350. Result := ExpandConst(Constant);
  351. end;
  352. class function TDummyClass.EvalInstallIdentifier(Sender: TSimpleExpression;
  353. const Name: String; const Parameters: array of const): Boolean;
  354. begin
  355. CodeRunner.RunProcedure(AnsiString(Name), Parameters, True);
  356. Result := True; { Result doesn't matter }
  357. end;
  358. procedure NotifyInstallEntry(const Install: String);
  359. procedure EvalInstall(const Expression: String);
  360. var
  361. SimpleExpression: TSimpleExpression;
  362. begin
  363. try
  364. SimpleExpression := TSimpleExpression.Create;
  365. try
  366. SimpleExpression.Expression := Expression;
  367. SimpleExpression.OnEvalIdentifier := TDummyClass.EvalInstallIdentifier;
  368. SimpleExpression.OnExpandConstant := TDummyClass.ExpandCheckOrInstallConstant;
  369. SimpleExpression.ParametersAllowed := True;
  370. SimpleExpression.SingleIdentifierMode := True;
  371. SimpleExpression.Eval;
  372. finally
  373. SimpleExpression.Free;
  374. end;
  375. except
  376. InternalError(Format('Expression error ''%s''', [GetExceptMessage]));
  377. end;
  378. end;
  379. begin
  380. if Install <> '' then begin
  381. try
  382. if CodeRunner = nil then
  383. InternalError('"BeforeInstall" or "AfterInstall" parameter with no CodeRunner');
  384. EvalInstall(Install);
  385. except
  386. { Don't allow exceptions raised by Before/AfterInstall functions to be propagated out }
  387. Application.HandleException(nil);
  388. end;
  389. end;
  390. end;
  391. procedure NotifyBeforeInstallEntry(const BeforeInstall: String);
  392. begin
  393. NotifyInstallEntry(BeforeInstall);
  394. end;
  395. procedure NotifyBeforeInstallFileEntry(const FileEntry: PSetupFileEntry);
  396. begin
  397. CheckOrInstallCurrentFilename := FileEntry.DestName;
  398. CheckOrInstallCurrentSourceFilename := FileEntry.SourceFilename;
  399. NotifyInstallEntry(FileEntry.BeforeInstall);
  400. CheckOrInstallCurrentFilename := '';
  401. CheckOrInstallCurrentSourceFilename := '';
  402. end;
  403. procedure NotifyAfterInstallEntry(const AfterInstall: String);
  404. begin
  405. NotifyInstallEntry(AfterInstall);
  406. end;
  407. procedure NotifyAfterInstallFileEntry(const FileEntry: PSetupFileEntry);
  408. begin
  409. CheckOrInstallCurrentFilename := FileEntry.DestName;
  410. CheckOrInstallCurrentSourceFilename := FileEntry.SourceFilename;
  411. NotifyInstallEntry(FileEntry.AfterInstall);
  412. CheckOrInstallCurrentFilename := '';
  413. CheckOrInstallCurrentSourceFilename := '';
  414. end;
  415. function EvalArchitectureIdentifier(const Name: String): Boolean;
  416. type
  417. TArchIdentifierRec = record
  418. Name: String;
  419. Arch: TSetupProcessorArchitecture;
  420. Compatible: Boolean;
  421. end;
  422. const
  423. { Valid identifier 'win64' is not in this list but treated specially below }
  424. ArchIdentifiers: array[0..7] of TArchIdentifierRec = (
  425. (Name: 'arm32compatible'; Arch: paArm32; Compatible: True),
  426. (Name: 'arm64'; Arch: paArm64; Compatible: False),
  427. (Name: 'x64'; Arch: paX64; Compatible: False),
  428. (Name: 'x64os'; Arch: paX64; Compatible: False),
  429. (Name: 'x64compatible'; Arch: paX64; Compatible: True),
  430. (Name: 'x86'; Arch: paX86; Compatible: False),
  431. (Name: 'x86os'; Arch: paX86; Compatible: False),
  432. (Name: 'x86compatible'; Arch: paX86; Compatible: True));
  433. begin
  434. if Name = 'win64' then
  435. Exit(IsWin64);
  436. for var ArchIdentifier in ArchIdentifiers do
  437. if ArchIdentifier.Name = Name then begin
  438. if ArchIdentifier.Compatible then
  439. Exit(ArchIdentifier.Arch in MachineTypesSupportedBySystem)
  440. else { An exact match is requested instead of anything compatible, perhaps
  441. for a driver install or something similar }
  442. Exit(ProcessorArchitecture = ArchIdentifier.Arch);
  443. end;
  444. raise Exception.CreateFmt('Unknown architecture ''%s''', [Name]);
  445. end;
  446. class function TDummyClass.EvalArchitectureIdentifier(Sender: TSimpleExpression;
  447. const Name: String; const Parameters: array of const): Boolean;
  448. begin
  449. Result := Setup.MainFunc.EvalArchitectureIdentifier(Name);
  450. end;
  451. class function TDummyClass.EvalComponentOrTaskIdentifier(Sender: TSimpleExpression;
  452. const Name: String; const Parameters: array of const): Boolean;
  453. var
  454. WizardItems: TStringList;
  455. begin
  456. WizardItems := TStringList(Sender.Tag);
  457. Result := ListContains(WizardItems, Name);
  458. end;
  459. class function TDummyClass.EvalLanguageIdentifier(Sender: TSimpleExpression;
  460. const Name: String; const Parameters: array of const): Boolean;
  461. begin
  462. Result := CompareText(PSetupLanguageEntry(Entries[seLanguage][ActiveLanguage]).Name, Name) = 0;
  463. end;
  464. class function TDummyClass.EvalCheckIdentifier(Sender: TSimpleExpression;
  465. const Name: String; const Parameters: array of const): Boolean;
  466. begin
  467. Result := CodeRunner.RunBooleanFunction(AnsiString(Name), Parameters, True, False);
  468. end;
  469. function EvalCheck(const Expression: String): Boolean;
  470. var
  471. SimpleExpression: TSimpleExpression;
  472. begin
  473. try
  474. SimpleExpression := TSimpleExpression.Create;
  475. try
  476. SimpleExpression.Lazy := True;
  477. SimpleExpression.Expression := Expression;
  478. SimpleExpression.OnEvalIdentifier := TDummyClass.EvalCheckIdentifier;
  479. SimpleExpression.OnExpandConstant := TDummyClass.ExpandCheckOrInstallConstant;
  480. SimpleExpression.ParametersAllowed := True;
  481. SimpleExpression.SilentOrAllowed := False;
  482. SimpleExpression.SingleIdentifierMode := False;
  483. Result := SimpleExpression.Eval;
  484. finally
  485. SimpleExpression.Free;
  486. end;
  487. except
  488. InternalError(Format('Expression error ''%s''', [GetExceptMessage]));
  489. Result := False;
  490. end;
  491. end;
  492. function EvalDirectiveCheck(const Expression: String): Boolean;
  493. begin
  494. if not TryStrToBoolean(Expression, Result) then
  495. Result := EvalCheck(Expression);
  496. end;
  497. function EvalExpression(const Expression: String;
  498. OnEvalIdentifier: TSimpleExpressionOnEvalIdentifier; Tag: LongInt = 0): Boolean;
  499. var
  500. SimpleExpression: TSimpleExpression;
  501. begin
  502. try
  503. SimpleExpression := TSimpleExpression.Create;
  504. try
  505. SimpleExpression.Lazy := True;
  506. SimpleExpression.Expression := Expression;
  507. SimpleExpression.OnEvalIdentifier := OnEvalIdentifier;
  508. SimpleExpression.ParametersAllowed := False;
  509. SimpleExpression.SilentOrAllowed := True;
  510. SimpleExpression.SingleIdentifierMode := False;
  511. SimpleExpression.Tag := Tag;
  512. Result := SimpleExpression.Eval;
  513. finally
  514. SimpleExpression.Free;
  515. end;
  516. except
  517. InternalError(Format('Expression error ''%s''', [GetExceptMessage]));
  518. Result := False;
  519. end;
  520. end;
  521. function ShouldProcessEntry(const WizardComponents, WizardTasks: TStringList;
  522. const Components, Tasks, Languages, Check: String): Boolean;
  523. var
  524. ProcessComponent, ProcessTask, ProcessLanguage: Boolean;
  525. begin
  526. if (Components <> '') or (Tasks <> '') or (Languages <> '') or (Check <> '') then begin
  527. if (Components <> '') and (WizardComponents <> nil) then
  528. ProcessComponent := EvalExpression(Components, TDummyClass.EvalComponentOrTaskIdentifier, LongInt(WizardComponents))
  529. else
  530. ProcessComponent := True;
  531. if (Tasks <> '') and (WizardTasks <> nil) then
  532. ProcessTask := EvalExpression(Tasks, TDummyClass.EvalComponentOrTaskIdentifier, LongInt(WizardTasks))
  533. else
  534. ProcessTask := True;
  535. if Languages <> '' then
  536. ProcessLanguage := EvalExpression(Languages, TDummyClass.EvalLanguageIdentifier)
  537. else
  538. ProcessLanguage := True;
  539. Result := ProcessComponent and ProcessTask and ProcessLanguage;
  540. if Result and (Check <> '') then begin
  541. try
  542. if CodeRunner = nil then
  543. InternalError('"Check" parameter with no CodeRunner');
  544. Result := EvalCheck(Check);
  545. except
  546. { Don't allow exceptions raised by Check functions to be propagated out }
  547. Application.HandleException(nil);
  548. Result := False;
  549. end;
  550. end;
  551. end else
  552. Result := True;
  553. end;
  554. function ShouldProcessFileEntry(const WizardComponents, WizardTasks: TStringList;
  555. const FileEntry: PSetupFileEntry; const IgnoreCheck: Boolean): Boolean;
  556. begin
  557. if foDontCopy in FileEntry.Options then begin
  558. Result := False;
  559. Exit;
  560. end;
  561. CheckOrInstallCurrentFilename := FileEntry.DestName;
  562. CheckOrInstallCurrentSourceFilename := FileEntry.SourceFilename;
  563. if IgnoreCheck then
  564. Result := ShouldProcessEntry(WizardComponents, WizardTasks, FileEntry.Components, FileEntry.Tasks, FileEntry.Languages, '')
  565. else
  566. Result := ShouldProcessEntry(WizardComponents, WizardTasks, FileEntry.Components, FileEntry.Tasks, FileEntry.Languages, FileEntry.Check);
  567. CheckOrInstallCurrentFilename := '';
  568. CheckOrInstallCurrentSourceFilename := '';
  569. end;
  570. function ShouldProcessRunEntry(const WizardComponents, WizardTasks: TStringList;
  571. const RunEntry: PSetupRunEntry): Boolean;
  572. begin
  573. if (InstallMode <> imNormal) and (roSkipIfSilent in RunEntry.Options) then
  574. Result := False
  575. else if (InstallMode = imNormal) and (roSkipIfNotSilent in RunEntry.Options) then
  576. Result := False
  577. else
  578. Result := ShouldProcessEntry(WizardComponents, WizardTasks, RunEntry.Components, RunEntry.Tasks, RunEntry.Languages, RunEntry.Check);
  579. end;
  580. function ShouldProcessIconEntry(const WizardComponents, WizardTasks: TStringList;
  581. const WizardNoIcons: Boolean; const IconEntry: PSetupIconEntry): Boolean;
  582. begin
  583. if WizardNoIcons and (IconEntry.Tasks = '') and
  584. (Copy(IconEntry.IconName, 1, 8) = '{group}\') then
  585. Result := False
  586. else
  587. Result := ShouldProcessEntry(WizardComponents, WizardTasks, IconEntry.Components, IconEntry.Tasks, IconEntry.Languages, IconEntry.Check);
  588. end;
  589. function ShouldDisableFsRedirForFileEntry(const FileEntry: PSetupFileEntry): Boolean;
  590. begin
  591. Result := InstallDefaultDisableFsRedir;
  592. if fo32Bit in FileEntry.Options then
  593. Result := False;
  594. if fo64Bit in FileEntry.Options then begin
  595. if not IsWin64 then
  596. InternalError('Cannot install files to 64-bit locations on this version of Windows');
  597. Result := True;
  598. end;
  599. end;
  600. function SlashesToBackslashes(const S: String): String;
  601. var
  602. I: Integer;
  603. begin
  604. Result := S;
  605. for I := 1 to Length(Result) do
  606. if Result[I] = '/' then
  607. Result[I] := '\';
  608. end;
  609. procedure LoadInf(const FileName: String; var WantToSuppressMsgBoxes: Boolean);
  610. const
  611. Section = 'Setup';
  612. var
  613. S: String;
  614. begin
  615. //saved infs
  616. InitLang := GetIniString(Section, 'Lang', InitLang, FileName);
  617. InitDir := GetIniString(Section, 'Dir', InitDir, FileName);
  618. InitProgramGroup := GetIniString(Section, 'Group', InitProgramGroup, FileName);
  619. InitNoIcons := GetIniBool(Section, 'NoIcons', InitNoIcons, FileName);
  620. InitSetupType := GetIniString(Section, 'SetupType', InitSetupType, FileName);
  621. S := GetIniString(Section, 'Components', '$', FileName);
  622. if S <> '$' then begin
  623. InitComponentsSpecified := True;
  624. SetStringsFromCommaString(InitComponents, SlashesToBackslashes(S));
  625. end;
  626. S := GetIniString(Section, 'Tasks', '$', FileName);
  627. if S <> '$' then begin
  628. InitDeselectAllTasks := True;
  629. SetStringsFromCommaString(InitTasks, SlashesToBackslashes(S));
  630. end;
  631. //non saved infs (=non user settable)
  632. InitSilent := GetIniBool(Section, 'Silent', InitSilent, FileName);
  633. InitVerySilent := GetIniBool(Section, 'VerySilent', InitVerySilent, FileName);
  634. InitNoRestart := GetIniBool(Section, 'NoRestart', InitNoRestart, FileName);
  635. InitCloseApplications := GetIniBool(Section, 'CloseApplications', InitCloseApplications, FileName);
  636. InitNoCloseApplications := GetIniBool(Section, 'NoCloseApplications', InitNoCloseApplications, FileName);
  637. InitForceCloseApplications := GetIniBool(Section, 'ForceCloseApplications', InitForceCloseApplications, FileName);
  638. InitNoForceCloseApplications := GetIniBool(Section, 'NoForceCloseApplications', InitNoForceCloseApplications, FileName);
  639. InitLogCloseApplications := GetIniBool(Section, 'LogCloseApplications', InitLogCloseApplications, FileName);
  640. InitRestartApplications := GetIniBool(Section, 'RestartApplications', InitRestartApplications, FileName);
  641. InitNoRestartApplications := GetIniBool(Section, 'NoRestartApplications', InitNoRestartApplications, FileName);
  642. InitNoCancel := GetIniBool(Section, 'NoCancel', InitNoCancel, FileName);
  643. InitPassword := GetIniString(Section, 'Password', InitPassword, FileName);
  644. InitRestartExitCode := GetIniInt(Section, 'RestartExitCode', InitRestartExitCode, 0, 0, FileName);
  645. WantToSuppressMsgBoxes := GetIniBool(Section, 'SuppressMsgBoxes', WantToSuppressMsgBoxes, FileName);
  646. InitSaveInf := GetIniString(Section, 'SaveInf', InitSaveInf, FileName);
  647. end;
  648. procedure SaveInf(const FileName: String);
  649. const
  650. Section = 'Setup';
  651. begin
  652. SetIniString(Section, 'Lang',
  653. PSetupLanguageEntry(Entries[seLanguage][ActiveLanguage]).Name, FileName);
  654. SetIniString(Section, 'Dir', WizardDirValue, FileName);
  655. SetIniString(Section, 'Group', WizardGroupValue, FileName);
  656. SetIniBool(Section, 'NoIcons', WizardNoIcons, FileName);
  657. if WizardSetupType <> nil then begin
  658. SetIniString(Section, 'SetupType', WizardSetupType.Name, FileName);
  659. SetIniString(Section, 'Components', StringsToCommaString(WizardComponents), FileName);
  660. end
  661. else begin
  662. DeleteIniEntry(Section, 'SetupType', FileName);
  663. DeleteIniEntry(Section, 'Components', FileName);
  664. end;
  665. SetIniString(Section, 'Tasks', StringsToCommaString(WizardTasks), FileName);
  666. end;
  667. function GetCustomMessageValue(const AName: String; var AValue: String): Boolean;
  668. var
  669. I: Integer;
  670. begin
  671. Result := False;
  672. for I := 0 to Entries[seCustomMessage].Count-1 do begin
  673. with PSetupCustomMessageEntry(Entries[seCustomMessage][I])^ do begin
  674. if (CompareText(Name, AName) = 0) and
  675. ((LangIndex = -1) or (LangIndex = ActiveLanguage)) then begin
  676. Result := True;
  677. AValue := Value;
  678. { don't stop looping, last item counts }
  679. end;
  680. end;
  681. end;
  682. end;
  683. function ExpandIndividualConst(Cnst: String;
  684. const CustomConsts: array of String): String;
  685. { Cnst must be the name of a single constant, without the braces.
  686. For example: app
  687. IsPath is set to True if the result is a path which needs special trailing-
  688. backslash handling. }
  689. procedure HandleAutoConstants(var Cnst: String);
  690. const
  691. Actual: array [Boolean] of String = ('user', 'common');
  692. begin
  693. if Copy(Cnst, 1, 4) = 'auto' then begin
  694. StringChange(Cnst, 'auto', Actual[IsAdminInstallMode]);
  695. if (Cnst = 'userpf32') or (Cnst = 'userpf64') or
  696. (Cnst = 'usercf32') or (Cnst = 'usercf64') then
  697. Delete(Cnst, Length(Cnst)-1, 2);
  698. end;
  699. end;
  700. procedure NoUninstallConstError(const C: String);
  701. begin
  702. InternalError(Format('Cannot evaluate "%s" constant during Uninstall', [C]));
  703. end;
  704. function ExpandEnvConst(C: String): String;
  705. var
  706. I: Integer;
  707. VarName, Default: String;
  708. begin
  709. Delete(C, 1, 1);
  710. I := ConstPos('|', C); { check for 'default' value }
  711. if I = 0 then
  712. I := Length(C)+1;
  713. VarName := Copy(C, 1, I-1);
  714. Default := Copy(C, I+1, Maxint);
  715. Result := '';
  716. if ConvertConstPercentStr(VarName) and ConvertConstPercentStr(Default) then begin
  717. Result := GetEnv(ExpandConstEx(VarName, CustomConsts));
  718. if Result = '' then
  719. Result := ExpandConstEx(Default, CustomConsts);
  720. end;
  721. end;
  722. function ExpandRegConst(C: String): String;
  723. { Expands a registry-value constant in the form:
  724. reg:HKxx\SubkeyName,ValueName|DefaultValue }
  725. type
  726. TKeyNameConst = packed record
  727. KeyName: String;
  728. KeyConst: HKEY;
  729. end;
  730. const
  731. KeyNameConsts: array[0..5] of TKeyNameConst = (
  732. (KeyName: 'HKA'; KeyConst: HKEY_AUTO),
  733. (KeyName: 'HKCR'; KeyConst: HKEY_CLASSES_ROOT),
  734. (KeyName: 'HKCU'; KeyConst: HKEY_CURRENT_USER),
  735. (KeyName: 'HKLM'; KeyConst: HKEY_LOCAL_MACHINE),
  736. (KeyName: 'HKU'; KeyConst: HKEY_USERS),
  737. (KeyName: 'HKCC'; KeyConst: HKEY_CURRENT_CONFIG));
  738. var
  739. Z, Subkey, Value, Default: String;
  740. I, J, L: Integer;
  741. RegView: TRegView;
  742. RootKey: HKEY;
  743. K: HKEY;
  744. begin
  745. Delete(C, 1, 4); { skip past 'reg:' }
  746. I := ConstPos('\', C);
  747. if I <> 0 then begin
  748. Z := Copy(C, 1, I-1);
  749. if Z <> '' then begin
  750. RegView := InstallDefaultRegView;
  751. L := Length(Z);
  752. if L >= 2 then begin
  753. { Check for '32' or '64' suffix }
  754. if (Z[L-1] = '3') and (Z[L] = '2') then begin
  755. RegView := rv32Bit;
  756. SetLength(Z, L-2);
  757. end
  758. else if (Z[L-1] = '6') and (Z[L] = '4') then begin
  759. if not IsWin64 then
  760. InternalError('Cannot access a 64-bit key in a "reg" constant on this version of Windows');
  761. RegView := rv64Bit;
  762. SetLength(Z, L-2);
  763. end;
  764. end;
  765. RootKey := 0;
  766. for J := Low(KeyNameConsts) to High(KeyNameConsts) do
  767. if CompareText(KeyNameConsts[J].KeyName, Z) = 0 then begin
  768. RootKey := KeyNameConsts[J].KeyConst;
  769. if RootKey = HKEY_AUTO then
  770. RootKey := InstallModeRootKey;
  771. Break;
  772. end;
  773. if RootKey <> 0 then begin
  774. Z := Copy(C, I+1, Maxint);
  775. I := ConstPos('|', Z); { check for a 'default' data }
  776. if I = 0 then
  777. I := Length(Z)+1;
  778. Default := Copy(Z, I+1, Maxint);
  779. SetLength(Z, I-1);
  780. I := ConstPos(',', Z); { comma separates subkey and value }
  781. if I <> 0 then begin
  782. Subkey := Copy(Z, 1, I-1);
  783. Value := Copy(Z, I+1, Maxint);
  784. if ConvertConstPercentStr(Subkey) and ConvertConstPercentStr(Value) and
  785. ConvertConstPercentStr(Default) then begin
  786. Result := ExpandConstEx(Default, CustomConsts);
  787. if RegOpenKeyExView(RegView, RootKey,
  788. PChar(ExpandConstEx(Subkey, CustomConsts)),
  789. 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
  790. RegQueryStringValue(K, PChar(ExpandConstEx(Value, CustomConsts)),
  791. Result, True); { also allows REG_DWORD }
  792. RegCloseKey(K);
  793. end;
  794. Exit;
  795. end;
  796. end;
  797. end;
  798. end;
  799. end;
  800. { it will only reach here if there was a parsing error }
  801. InternalError('Failed to parse "reg" constant');
  802. end;
  803. function ExpandIniConst(C: String): String;
  804. { Expands an INI-value constant in the form:
  805. filename,section,key|defaultvalue }
  806. var
  807. Z, Filename, Section, Key, Default: String;
  808. I: Integer;
  809. begin
  810. Delete(C, 1, 4); { skip past 'ini:' }
  811. I := ConstPos(',', C);
  812. if I <> 0 then begin
  813. Z := Copy(C, 1, I-1);
  814. if Z <> '' then begin
  815. Filename := Z;
  816. Z := Copy(C, I+1, Maxint);
  817. I := ConstPos('|', Z); { check for a 'default' data }
  818. if I = 0 then
  819. I := Length(Z)+1;
  820. Default := Copy(Z, I+1, Maxint);
  821. SetLength(Z, I-1);
  822. I := ConstPos(',', Z); { comma separates section and key }
  823. if I <> 0 then begin
  824. Section := Copy(Z, 1, I-1);
  825. Key := Copy(Z, I+1, Maxint);
  826. if ConvertConstPercentStr(Filename) and ConvertConstPercentStr(Section) and ConvertConstPercentStr(Key) and
  827. ConvertConstPercentStr(Default) then begin
  828. Filename := ExpandConstEx(Filename, CustomConsts);
  829. Section := ExpandConstEx(Section, CustomConsts);
  830. Key := ExpandConstEx(Key, CustomConsts);
  831. Default := ExpandConstEx(Default, CustomConsts);
  832. Result := GetIniString(Section, Key, Default, Filename);
  833. Exit;
  834. end;
  835. end;
  836. end;
  837. end;
  838. { it will only reach here if there was a parsing error }
  839. InternalError('Failed to parse "ini" constant');
  840. end;
  841. function ExpandParamConst(C: String): String;
  842. { Expands an commandline-parameter-value constant in the form:
  843. parametername|defaultvalue }
  844. function GetParamString(const Param, Default: String): String;
  845. var
  846. I, PCount: Integer;
  847. Z: String;
  848. begin
  849. PCount := NewParamCount();
  850. for I := 1 to PCount do begin
  851. Z := NewParamStr(I);
  852. if StrLIComp(PChar(Z), PChar('/'+Param+'='), Length(Param)+2) = 0 then begin
  853. Delete(Z, 1, Length(Param)+2);
  854. Result := Z;
  855. Exit;
  856. end;
  857. end;
  858. Result := Default;
  859. end;
  860. var
  861. Z, Param, Default: String;
  862. I: Integer;
  863. begin
  864. Delete(C, 1, 6); { skip past 'param:' }
  865. Z := C;
  866. I := ConstPos('|', Z); { check for a 'default' data }
  867. if I = 0 then
  868. I := Length(Z)+1;
  869. Default := Copy(Z, I+1, Maxint);
  870. SetLength(Z, I-1);
  871. Param := Z;
  872. if ConvertConstPercentStr(Param) and ConvertConstPercentStr(Default) then begin
  873. Param := ExpandConstEx(Param, CustomConsts);
  874. Default := ExpandConstEx(Default, CustomConsts);
  875. Result := GetParamString(Param, Default);
  876. Exit;
  877. end;
  878. { it will only reach here if there was a parsing error }
  879. InternalError('Failed to parse "param" constant');
  880. end;
  881. function ExpandCodeConst(C: String): String;
  882. { Expands an Pascal-script-value constant in the form:
  883. parametername|defaultvalue }
  884. function GetCodeString(const ScriptFunc, Default: String): String;
  885. begin
  886. if (CodeRunner <> nil) then
  887. Result := CodeRunner.RunStringFunction(AnsiString(ScriptFunc), [Default], True, Default)
  888. else begin
  889. InternalError('"code" constant with no CodeRunner');
  890. Result := '';
  891. end;
  892. end;
  893. var
  894. Z, ScriptFunc, Default: String;
  895. I: Integer;
  896. begin
  897. if DisableCodeConsts <> 0 then
  898. raise Exception.Create('Cannot evaluate "code" constant because of possible side effects');
  899. Delete(C, 1, 5); { skip past 'code:' }
  900. Z := C;
  901. I := ConstPos('|', Z); { check for a 'default' data }
  902. if I = 0 then
  903. I := Length(Z)+1;
  904. Default := Copy(Z, I+1, Maxint);
  905. SetLength(Z, I-1);
  906. ScriptFunc := Z;
  907. if ConvertConstPercentStr(ScriptFunc) and ConvertConstPercentStr(Default) then begin
  908. Default := ExpandConstEx(Default, CustomConsts);
  909. Result := GetCodeString(ScriptFunc, Default);
  910. Exit;
  911. end;
  912. { it will only reach here if there was a parsing error }
  913. InternalError('Failed to parse "code" constant');
  914. end;
  915. function ExpandDriveConst(C: String): String;
  916. begin
  917. Delete(C, 1, 6); { skip past 'drive:' }
  918. if ConvertConstPercentStr(C) then begin
  919. Result := PathExtractDrive(ExpandConstEx(C, CustomConsts));
  920. Exit;
  921. end;
  922. { it will only reach here if there was a parsing error }
  923. InternalError('Failed to parse "drive" constant');
  924. end;
  925. function ExpandCustomMessageConst(C: String): String;
  926. var
  927. I, ArgCount: Integer;
  928. MsgName: String;
  929. ArgValues: array[0..8] of String; { %1 through %9 }
  930. begin
  931. Delete(C, 1, 3); { skip past 'cm:' }
  932. I := ConstPos(',', C);
  933. if I = 0 then
  934. MsgName := C
  935. else
  936. MsgName := Copy(C, 1, I-1);
  937. { Prepare arguments. Excess arguments are ignored. }
  938. ArgCount := 0;
  939. while (I > 0) and (ArgCount <= High(ArgValues)) do begin
  940. Delete(C, 1, I);
  941. I := ConstPos(',', C);
  942. if I = 0 then
  943. ArgValues[ArgCount] := C
  944. else
  945. ArgValues[ArgCount] := Copy(C, 1, I-1);
  946. if not ConvertConstPercentStr(ArgValues[ArgCount]) then
  947. InternalError('Failed to parse "cm" constant');
  948. ArgValues[ArgCount] := ExpandConstEx(ArgValues[ArgCount], CustomConsts);
  949. Inc(ArgCount);
  950. end;
  951. { Look up the message value }
  952. if not GetCustomMessageValue(MsgName, Result) then
  953. InternalError(Format('Unknown custom message name "%s" in "cm" constant', [MsgName]));
  954. { Expand the message }
  955. Result := FmtMessage(PChar(Result), Slice(ArgValues, ArgCount));
  956. end;
  957. const
  958. FolderConsts: array[Boolean, TShellFolderID] of String = (
  959. { Also see FolderIDs }
  960. { User }
  961. ('userdesktop', 'userstartmenu', 'userprograms', 'userstartup',
  962. 'usersendto', 'commonfonts', 'userappdata', 'userdocs', 'usertemplates',
  963. 'userfavorites', 'localappdata', 'userpf', 'usercf', 'usersavedgames'),
  964. { Common }
  965. ('commondesktop', 'commonstartmenu', 'commonprograms', 'commonstartup',
  966. 'usersendto', 'commonfonts', 'commonappdata', 'commondocs', 'commontemplates',
  967. 'commonfavorites' { not accepted anymore by the compiler }, '', '', '', ''));
  968. NoUninstallConsts: array[0..6] of String =
  969. ('src', 'srcexe', 'userinfoname', 'userinfoorg', 'userinfoserial', 'hwnd',
  970. 'wizardhwnd');
  971. var
  972. OriginalCnst, ShellFolder: String;
  973. Common: Boolean;
  974. ShellFolderID: TShellFolderID;
  975. I: Integer;
  976. begin
  977. OriginalCnst := Cnst;
  978. HandleRenamedConstants(Cnst, nil);
  979. HandleAutoConstants(Cnst);
  980. if IsUninstaller then
  981. for I := Low(NoUninstallConsts) to High(NoUninstallConsts) do
  982. if NoUninstallConsts[I] = Cnst then
  983. NoUninstallConstError(NoUninstallConsts[I]);
  984. if Cnst = '\' then Result := '\'
  985. else if Cnst = 'app' then begin
  986. if IsUninstaller then begin
  987. if UninstallExpandedApp = '' then
  988. InternalError('An attempt was made to expand the "' + OriginalCnst + '" constant but Setup didn''t create the "app" dir');
  989. Result := UninstallExpandedApp;
  990. end else begin
  991. if WizardDirValue = '' then
  992. InternalError('An attempt was made to expand the "' + OriginalCnst + '" constant before it was initialized');
  993. Result := WizardDirValue;
  994. end;
  995. end
  996. else if Cnst = 'win' then Result := WinDir
  997. else if Cnst = 'sys' then Result := WinSystemDir
  998. else if Cnst = 'syswow64' then begin
  999. if WinSysWow64Dir <> '' then
  1000. Result := WinSysWow64Dir
  1001. else begin
  1002. if IsWin64 then { sanity check }
  1003. InternalError('Cannot expand "' + OriginalCnst + '" constant because there is no SysWOW64 directory');
  1004. Result := WinSystemDir;
  1005. end;
  1006. end
  1007. else if Cnst = 'sysnative' then begin
  1008. if WinSysNativeDir <> '' then
  1009. Result := WinSysNativeDir
  1010. else
  1011. Result := WinSystemDir;
  1012. end
  1013. else if Cnst = 'src' then Result := SourceDir
  1014. else if Cnst = 'srcexe' then Result := SetupLdrOriginalFilename
  1015. else if Cnst = 'tmp' then Result := TempInstallDir
  1016. else if Cnst = 'sd' then Result := SystemDrive
  1017. else if Cnst = 'commonpf' then begin
  1018. if Is64BitInstallMode then
  1019. Result := ProgramFiles64Dir
  1020. else
  1021. Result := ProgramFiles32Dir;
  1022. end
  1023. else if Cnst = 'commoncf' then begin
  1024. if Is64BitInstallMode then
  1025. Result := CommonFiles64Dir
  1026. else
  1027. Result := CommonFiles32Dir;
  1028. end
  1029. else if Cnst = 'commonpf32' then Result := ProgramFiles32Dir
  1030. else if Cnst = 'commoncf32' then Result := CommonFiles32Dir
  1031. else if Cnst = 'commonpf64' then begin
  1032. if IsWin64 then
  1033. Result := ProgramFiles64Dir
  1034. else
  1035. InternalError('Cannot expand "' + OriginalCnst + '" constant on this version of Windows');
  1036. end
  1037. else if Cnst = 'commoncf64' then begin
  1038. if IsWin64 then
  1039. Result := CommonFiles64Dir
  1040. else
  1041. InternalError('Cannot expand "' + OriginalCnst + '" constant on this version of Windows');
  1042. end
  1043. else if Cnst = 'userfonts' then Result := ExpandConst('{localappdata}\Microsoft\Windows\Fonts') { supported by Windows 10 Version 1803 and newer. doesn't have a KNOWNFOLDERID. }
  1044. else if Cnst = 'dao' then Result := ExpandConst('{cf}\Microsoft Shared\DAO')
  1045. else if Cnst = 'cmd' then Result := CmdFilename
  1046. else if Cnst = 'computername' then Result := GetComputerNameString
  1047. else if Cnst = 'username' then Result := GetUserNameString
  1048. else if Cnst = 'groupname' then begin
  1049. if IsUninstaller then begin
  1050. if UninstallExpandedGroupName = '' then
  1051. InternalError('Cannot expand "' + OriginalCnst + '" constant because it was not available at install time');
  1052. Result := UninstallExpandedGroupName;
  1053. end
  1054. else begin
  1055. if WizardGroupValue = '' then
  1056. InternalError('An attempt was made to expand the "' + OriginalCnst + '" constant before it was initialized');
  1057. Result := WizardGroupValue;
  1058. end;
  1059. end
  1060. else if Cnst = 'sysuserinfoname' then Result := SysUserInfoName
  1061. else if Cnst = 'sysuserinfoorg' then Result := SysUserInfoOrg
  1062. else if Cnst = 'userinfoname' then Result := WizardUserInfoName
  1063. else if Cnst = 'userinfoorg' then Result := WizardUserInfoOrg
  1064. else if Cnst = 'userinfoserial' then Result := WizardUserInfoSerial
  1065. else if Cnst = 'uninstallexe' then Result := UninstallExeFilename
  1066. else if Cnst = 'group' then begin
  1067. if IsUninstaller then begin
  1068. if UninstallExpandedGroup = '' then
  1069. InternalError('Cannot expand "' + OriginalCnst + '" constant because it was not available at install time');
  1070. Result := UninstallExpandedGroup;
  1071. end
  1072. else begin
  1073. if WizardGroupValue = '' then
  1074. InternalError('An attempt was made to expand the "' + OriginalCnst + '" constant before it was initialized');
  1075. ShellFolder := GetShellFolder(not(shAlwaysUsePersonalGroup in SetupHeader.Options) and IsAdminInstallMode,
  1076. sfPrograms);
  1077. if ShellFolder = '' then
  1078. InternalError('Failed to expand "' + OriginalCnst + '" constant');
  1079. Result := AddBackslash(ShellFolder) + WizardGroupValue;
  1080. end;
  1081. end
  1082. else if Cnst = 'language' then begin
  1083. if IsUninstaller then
  1084. Result := UninstallExpandedLanguage
  1085. else
  1086. Result := PSetupLanguageEntry(Entries[seLanguage][ActiveLanguage]).Name
  1087. end
  1088. else if Cnst = 'wizardhwnd' then begin
  1089. if Assigned(WizardForm) then
  1090. Result := IntToStr(WizardForm.Handle)
  1091. else
  1092. Result := '0';
  1093. end
  1094. else if Cnst = 'log' then Result := GetLogFileName
  1095. else if Cnst = 'dotnet11' then Result := GetDotNetVersionInstallRoot(rv32Bit, netbase11)
  1096. else if Cnst = 'dotnet20' then Result := GetDotNetVersionInstallRoot(InstallDefaultRegView, netbase20)
  1097. else if Cnst = 'dotnet2032' then Result := GetDotNetVersionInstallRoot(rv32Bit, netbase20)
  1098. else if Cnst = 'dotnet2064' then begin
  1099. if IsWin64 then
  1100. Result := GetDotNetVersionInstallRoot(rv64Bit, netbase20)
  1101. else
  1102. InternalError('Cannot expand "' + OriginalCnst + '" constant on this version of Windows');
  1103. end
  1104. else if Cnst = 'dotnet40' then Result := GetDotNetVersionInstallRoot(InstallDefaultRegView, netbase40)
  1105. else if Cnst = 'dotnet4032' then Result := GetDotNetVersionInstallRoot(rv32Bit, netbase40)
  1106. else if Cnst = 'dotnet4064' then begin
  1107. if IsWin64 then
  1108. Result := GetDotNetVersionInstallRoot(rv64Bit, netbase40)
  1109. else
  1110. InternalError('Cannot expand "' + OriginalCnst + '" constant on this version of Windows');
  1111. end
  1112. else if (Cnst <> '') and (Cnst[1] = '%') then Result := ExpandEnvConst(Cnst)
  1113. else if StrLComp(PChar(Cnst), 'reg:', 4) = 0 then Result := ExpandRegConst(Cnst)
  1114. else if StrLComp(PChar(Cnst), 'ini:', 4) = 0 then Result := ExpandIniConst(Cnst)
  1115. else if StrLComp(PChar(Cnst), 'param:', 6) = 0 then Result := ExpandParamConst(Cnst)
  1116. else if StrLComp(PChar(Cnst), 'code:', 5) = 0 then Result := ExpandCodeConst(Cnst)
  1117. else if StrLComp(PChar(Cnst), 'drive:', 6) = 0 then Result := ExpandDriveConst(Cnst)
  1118. else if StrLComp(PChar(Cnst), 'cm:', 3) = 0 then Result := ExpandCustomMessageConst(Cnst)
  1119. else begin
  1120. { Shell folder constants }
  1121. if Cnst <> '' then
  1122. for Common := False to True do
  1123. for ShellFolderID := Low(ShellFolderID) to High(ShellFolderID) do
  1124. if Cnst = FolderConsts[Common, ShellFolderID] then begin
  1125. ShellFolder := GetShellFolder(Common, ShellFolderID);
  1126. if ShellFolder = '' then
  1127. InternalError(Format('Failed to expand shell folder constant "%s"', [OriginalCnst]));
  1128. Result := ShellFolder;
  1129. Exit;
  1130. end;
  1131. { Custom constants }
  1132. if Cnst <> '' then begin
  1133. I := 0;
  1134. while I < High(CustomConsts) do begin
  1135. if Cnst = CustomConsts[I] then begin
  1136. Result := CustomConsts[I+1];
  1137. Exit;
  1138. end;
  1139. Inc(I, 2);
  1140. end;
  1141. end;
  1142. { Unknown constant }
  1143. InternalError(Format('Unknown constant "%s"', [OriginalCnst]));
  1144. end;
  1145. end;
  1146. function ExpandConst(const S: String): String;
  1147. begin
  1148. Result := ExpandConstEx2(S, [''], True);
  1149. end;
  1150. function ExpandConstEx(const S: String; const CustomConsts: array of String): String;
  1151. begin
  1152. Result := ExpandConstEx2(S, CustomConsts, True);
  1153. end;
  1154. function ExpandConstEx2(const S: String; const CustomConsts: array of String;
  1155. const DoExpandIndividualConst: Boolean): String;
  1156. var
  1157. I, Start: Integer;
  1158. Cnst, ReplaceWith: String;
  1159. begin
  1160. Result := S;
  1161. I := 1;
  1162. while I <= Length(Result) do begin
  1163. if Result[I] = '{' then begin
  1164. if (I < Length(Result)) and (Result[I+1] = '{') then begin
  1165. { Change '{{' to '{' if not in an embedded constant }
  1166. Inc(I);
  1167. Delete(Result, I, 1);
  1168. end
  1169. else begin
  1170. Start := I;
  1171. { Find the closing brace, skipping over any embedded constants }
  1172. I := SkipPastConst(Result, I);
  1173. if I = 0 then { unclosed constant? }
  1174. InternalError('Unclosed constant');
  1175. Dec(I); { 'I' now points to the closing brace }
  1176. if DoExpandIndividualConst then begin
  1177. { Now translate the constant }
  1178. Cnst := Copy(Result, Start+1, I-(Start+1));
  1179. ReplaceWith := ExpandIndividualConst(Cnst, CustomConsts);
  1180. Delete(Result, Start, (I+1)-Start);
  1181. Insert(ReplaceWith, Result, Start);
  1182. I := Start + Length(ReplaceWith);
  1183. if (ReplaceWith <> '') and (PathLastChar(ReplaceWith)^ = '\') and
  1184. (I <= Length(Result)) and (Result[I] = '\') then
  1185. Delete(Result, I, 1);
  1186. end else
  1187. Inc(I); { Skip closing brace }
  1188. end;
  1189. end
  1190. else
  1191. Inc(I);
  1192. end;
  1193. end;
  1194. function ExpandConstIfPrefixed(const S: String): String;
  1195. const
  1196. ExpandPrefix = 'expand:';
  1197. begin
  1198. if Pos(ExpandPrefix, S) = 1 then begin
  1199. Inc(DisableCodeConsts);
  1200. try
  1201. Result := ExpandConst(Copy(S, Length(ExpandPrefix)+1, Maxint));
  1202. finally
  1203. Dec(DisableCodeConsts);
  1204. end;
  1205. end
  1206. else
  1207. Result := S;
  1208. end;
  1209. procedure InitMainNonSHFolderConsts;
  1210. function GetPath(const RegView: TRegView; const Name: PChar): String;
  1211. var
  1212. H: HKEY;
  1213. begin
  1214. if RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, REGSTR_PATH_SETUP, 0,
  1215. KEY_QUERY_VALUE, H) = ERROR_SUCCESS then begin
  1216. if not RegQueryStringValue(H, Name, Result) then
  1217. Result := '';
  1218. RegCloseKey(H);
  1219. end
  1220. else
  1221. Result := '';
  1222. end;
  1223. procedure ReadSysUserInfo;
  1224. var
  1225. RegView: TRegView;
  1226. K: HKEY;
  1227. begin
  1228. { Windows 7 x64 (and later?) is bugged: the owner and organization
  1229. are set to "Microsoft" on the 32-bit key. So on 64-bit Windows, read
  1230. from the 64-bit key. (The bug doesn't exist on 64-bit XP or Server 2003,
  1231. but it's safe to read the 64-bit key on those versions too.) }
  1232. if IsWin64 then
  1233. RegView := rv64Bit
  1234. else
  1235. RegView := rvDefault;
  1236. if RegOpenKeyExView(RegView, HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\Windows NT\CurrentVersion',
  1237. 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
  1238. RegQueryStringValue(K, 'RegisteredOwner', SysUserInfoName);
  1239. RegQueryStringValue(K, 'RegisteredOrganization', SysUserInfoOrg);
  1240. RegCloseKey(K);
  1241. end;
  1242. end;
  1243. begin
  1244. { Read Windows and Windows System dirs }
  1245. WinDir := GetWinDir;
  1246. WinSystemDir := GetSystemDir;
  1247. WinSysWow64Dir := GetSysWow64Dir;
  1248. WinSysNativeDir := GetSysNativeDir(IsWin64);
  1249. { Get system drive }
  1250. SystemDrive := GetEnv('SystemDrive'); {don't localize}
  1251. if SystemDrive = '' then begin
  1252. SystemDrive := PathExtractDrive(WinDir);
  1253. if SystemDrive = '' then
  1254. { In some rare case that PathExtractDrive failed, just default to C }
  1255. SystemDrive := 'C:';
  1256. end;
  1257. { Get 32-bit Program Files and Common Files dirs }
  1258. ProgramFiles32Dir := GetPath(rv32Bit, 'ProgramFilesDir');
  1259. if ProgramFiles32Dir = '' then
  1260. ProgramFiles32Dir := SystemDrive + '\Program Files'; {don't localize}
  1261. CommonFiles32Dir := GetPath(rv32Bit, 'CommonFilesDir');
  1262. if CommonFiles32Dir = '' then
  1263. CommonFiles32Dir := AddBackslash(ProgramFiles32Dir) + 'Common Files'; {don't localize}
  1264. { Get 64-bit Program Files and Common Files dirs }
  1265. if IsWin64 then begin
  1266. ProgramFiles64Dir := GetPath(rv64Bit, 'ProgramFilesDir');
  1267. if ProgramFiles64Dir = '' then
  1268. InternalError('Failed to get path of 64-bit Program Files directory');
  1269. CommonFiles64Dir := GetPath(rv64Bit, 'CommonFilesDir');
  1270. if CommonFiles64Dir = '' then
  1271. InternalError('Failed to get path of 64-bit Common Files directory');
  1272. end;
  1273. { Get path of command interpreter }
  1274. CmdFilename := AddBackslash(WinSystemDir) + 'cmd.exe';
  1275. { Get user info from system }
  1276. ReadSysUserInfo;
  1277. end;
  1278. procedure SaveStreamToTempFile(const Strm: TCustomMemoryStream;
  1279. const Filename: String);
  1280. var
  1281. ErrorCode: DWORD;
  1282. begin
  1283. try
  1284. Strm.SaveToFile(Filename);
  1285. except
  1286. { Display more useful error message than 'Stream write error' etc. }
  1287. on EStreamError do begin
  1288. ErrorCode := GetLastError;
  1289. raise Exception.Create(FmtSetupMessage(msgLastErrorMessage,
  1290. [SetupMessages[msgLdrCannotCreateTemp], IntToStr(ErrorCode),
  1291. Win32ErrorString(ErrorCode)]));
  1292. end;
  1293. end;
  1294. end;
  1295. procedure SaveResourceToTempFile(const ResName, Filename: String);
  1296. var
  1297. ResStrm: TResourceStream;
  1298. begin
  1299. ResStrm := TResourceStream.Create(HInstance, ResName, RT_RCDATA);
  1300. try
  1301. SaveStreamToTempFile(ResStrm, Filename);
  1302. finally
  1303. ResStrm.Free;
  1304. end;
  1305. end;
  1306. procedure CreateTempInstallDirAndExtract64BitHelper;
  1307. { Initializes TempInstallDir and extracts the 64-bit helper into it if needed.
  1308. This is called by Setup, Uninstall, and RegSvr. }
  1309. begin
  1310. var Protected: Boolean;
  1311. TempInstallDir := CreateTempDir(IsAdmin and not Debugging, Protected);
  1312. LogFmt('Created %stemporary directory: %s', [IfThen(Protected, 'protected ', ''), TempInstallDir]);
  1313. if Debugging then
  1314. DebugNotifyTempDir(TempInstallDir);
  1315. { Create _isetup subdirectory to hold our internally-used files to ensure
  1316. they won't use any DLLs the install creator might've dumped into
  1317. TempInstallDir }
  1318. var Subdir := AddBackslash(TempInstallDir) + '_isetup';
  1319. if not CreateDirectory(PChar(Subdir), nil) then begin
  1320. var ErrorCode := GetLastError;
  1321. raise Exception.Create(FmtSetupMessage(msgLastErrorMessage,
  1322. [FmtSetupMessage1(msgErrorCreatingDir, Subdir), IntToStr(ErrorCode),
  1323. Win32ErrorString(ErrorCode)]));
  1324. end;
  1325. { Extract 64-bit helper EXE, if one is available for the current processor
  1326. architecture }
  1327. var ResName := GetHelperResourceName;
  1328. if ResName <> '' then begin
  1329. var Filename := Subdir + '\_setup64.tmp';
  1330. SaveResourceToTempFile(ResName, Filename);
  1331. SetHelperExeFilename(Filename);
  1332. end;
  1333. end;
  1334. function TempDeleteFileProc(const DisableFsRedir: Boolean;
  1335. const FileName: String; const Param: Pointer): Boolean;
  1336. var
  1337. Elapsed: DWORD;
  1338. label Retry;
  1339. begin
  1340. Retry:
  1341. Result := DeleteFileRedir(DisableFsRedir, FileName);
  1342. if not Result and
  1343. (GetLastError <> ERROR_FILE_NOT_FOUND) and
  1344. (GetLastError <> ERROR_PATH_NOT_FOUND) then begin
  1345. { If we get here, the file is probably still in use. On an SMP machine,
  1346. it's possible for an EXE to remain locked by Windows for a short time
  1347. after it terminates, causing DeleteFile to fail with ERROR_ACCESS_DENIED.
  1348. (I'm not sure this issue can really be seen here in practice; I could
  1349. only reproduce it consistently by calling DeleteFile() *immediately*
  1350. after waiting on the process handle.)
  1351. Retry if fewer than 2 seconds have passed since DelTree started,
  1352. otherwise assume the error must be permanent and give up. 2 seconds
  1353. ought to be more than enough for the SMP case. }
  1354. Elapsed := GetTickCount - DWORD(Param);
  1355. if Cardinal(Elapsed) < Cardinal(2000) then begin
  1356. Sleep(50);
  1357. goto Retry;
  1358. end;
  1359. end;
  1360. end;
  1361. procedure RemoveTempInstallDir;
  1362. { Removes TempInstallDir and all its contents. Stops the 64-bit helper first
  1363. if necessary. }
  1364. begin
  1365. { Stop 64-bit helper if it's running }
  1366. StopHelper(False);
  1367. SetHelperExeFilename('');
  1368. if TempInstallDir <> '' then begin
  1369. if Debugging then
  1370. DebugNotifyTempDir('');
  1371. if not DelTree(False, TempInstallDir, True, True, True, False, nil,
  1372. TempDeleteFileProc, Pointer(GetTickCount())) then
  1373. Log('Failed to remove temporary directory: ' + TempInstallDir);
  1374. end;
  1375. end;
  1376. procedure LoadSHFolderDLL;
  1377. var
  1378. Filename: String;
  1379. const
  1380. shfolder = 'shfolder.dll';
  1381. begin
  1382. Filename := AddBackslash(GetSystemDir) + shfolder;
  1383. { Ensure shell32.dll is pre-loaded so it isn't loaded/freed for each
  1384. individual SHGetFolderPath call }
  1385. SafeLoadLibrary(AddBackslash(GetSystemDir) + shell32, SEM_NOOPENFILEERRORBOX);
  1386. SHFolderDLLHandle := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
  1387. if SHFolderDLLHandle = 0 then
  1388. InternalError(Format('Failed to load DLL "%s"', [Filename]));
  1389. @SHGetFolderPathFunc := GetProcAddress(SHFolderDLLHandle, 'SHGetFolderPathW');
  1390. if @SHGetFolderPathFunc = nil then
  1391. InternalError('Failed to get address of SHGetFolderPath function');
  1392. end;
  1393. procedure UnloadSHFolderDLL;
  1394. begin
  1395. @SHGetFolderPathFunc := nil;
  1396. if SHFolderDLLHandle <> 0 then begin
  1397. FreeLibrary(SHFolderDLLHandle);
  1398. SHFolderDLLHandle := 0;
  1399. end;
  1400. end;
  1401. function GetShellFolderByCSIDL(Folder: Integer; const Create: Boolean): String;
  1402. const
  1403. CSIDL_FLAG_CREATE = $8000;
  1404. SHGFP_TYPE_CURRENT = 0;
  1405. var
  1406. Res: HRESULT;
  1407. Buf: array[0..MAX_PATH-1] of Char;
  1408. begin
  1409. { Note: Must pass Create=True or else SHGetFolderPath fails if the
  1410. specified CSIDL is valid but doesn't currently exist. }
  1411. if Create then
  1412. Folder := Folder or CSIDL_FLAG_CREATE;
  1413. { Work around a nasty bug in Windows Vista and Windows Server 2008 and maybe
  1414. later versions also: When a folder ID resolves to the root directory of a
  1415. drive ('X:\') and the CSIDL_FLAG_CREATE flag is passed, SHGetFolderPath
  1416. fails with code 0x80070005.
  1417. So, first try calling the function without CSIDL_FLAG_CREATE.
  1418. If and only if that fails, call it again with the flag.
  1419. Note: The calls *must* be issued in this order; if it's called with the
  1420. flag first, it seems to permanently cache the failure code, causing future
  1421. calls that don't include the flag to fail as well. }
  1422. if Folder and CSIDL_FLAG_CREATE <> 0 then
  1423. Res := SHGetFolderPathFunc(0, Folder and not CSIDL_FLAG_CREATE, 0,
  1424. SHGFP_TYPE_CURRENT, Buf)
  1425. else
  1426. Res := E_FAIL; { always issue the call below }
  1427. if Res <> S_OK then
  1428. Res := SHGetFolderPathFunc(0, Folder, 0, SHGFP_TYPE_CURRENT, Buf);
  1429. if Res = S_OK then
  1430. Result := RemoveBackslashUnlessRoot(PathExpand(Buf))
  1431. else begin
  1432. Result := '';
  1433. LogFmt('Warning: SHGetFolderPath failed with code 0x%.8x on folder 0x%.4x',
  1434. [Res, Folder]);
  1435. end;
  1436. end;
  1437. function GetShellFolderByGUID(Folder: TGUID; const Create: Boolean): String;
  1438. begin
  1439. if Assigned(SHGetKnownFolderPathFunc) then begin
  1440. var dwFlags: DWORD := 0;
  1441. if Create then
  1442. dwFlags := dwFlags or KF_FLAG_CREATE;
  1443. var Path: PWideChar;
  1444. { Note: Must pass Create=True or else SHGetKnownFolderPath fails if the
  1445. specified GUID is valid but doesn't currently exist. }
  1446. var Res := SHGetKnownFolderPathFunc(Folder, dwFlags, 0, Path);
  1447. if Res = S_OK then begin
  1448. Result := WideCharToString(Path);
  1449. CoTaskMemFree(Path);
  1450. end else begin
  1451. Result := '';
  1452. LogFmt('Warning: SHGetKnownFolderPath failed with code 0x%.8x', [Res]);
  1453. end;
  1454. end else
  1455. Result := '';
  1456. end;
  1457. function GetShellFolder(const Common: Boolean; const ID: TShellFolderID): String;
  1458. const
  1459. CSIDL_COMMON_STARTMENU = $0016;
  1460. CSIDL_COMMON_PROGRAMS = $0017;
  1461. CSIDL_COMMON_STARTUP = $0018;
  1462. CSIDL_COMMON_DESKTOPDIRECTORY = $0019;
  1463. CSIDL_APPDATA = $001A;
  1464. CSIDL_LOCAL_APPDATA = $001C;
  1465. CSIDL_COMMON_FAVORITES = $001F;
  1466. CSIDL_COMMON_APPDATA = $0023;
  1467. CSIDL_COMMON_TEMPLATES = $002D;
  1468. CSIDL_COMMON_DOCUMENTS = $002E;
  1469. FolderIDs: array[Boolean, TShellFolderID] of Integer = (
  1470. { Values must match FolderConsts }
  1471. { User }
  1472. (CSIDL_DESKTOPDIRECTORY, CSIDL_STARTMENU, CSIDL_PROGRAMS, CSIDL_STARTUP,
  1473. CSIDL_SENDTO, CSIDL_FONTS, CSIDL_APPDATA, CSIDL_PERSONAL,
  1474. CSIDL_TEMPLATES, CSIDL_FAVORITES, CSIDL_LOCAL_APPDATA, 0, 0, 0),
  1475. { Common }
  1476. (CSIDL_COMMON_DESKTOPDIRECTORY, CSIDL_COMMON_STARTMENU, CSIDL_COMMON_PROGRAMS, CSIDL_COMMON_STARTUP,
  1477. CSIDL_SENDTO, CSIDL_FONTS, CSIDL_COMMON_APPDATA, CSIDL_COMMON_DOCUMENTS,
  1478. CSIDL_COMMON_TEMPLATES, CSIDL_COMMON_FAVORITES, 0, 0, 0, 0));
  1479. FOLDERID_UserProgramFiles: TGUID = (D1:$5CD7AEE2; D2:$2219; D3:$4A67; D4:($B8,$5D,$6C,$9C,$E1,$56,$60,$CB));
  1480. FOLDERID_UserProgramFilesCommon: TGUID = (D1:$BCBD3057; D2:$CA5C; D3:$4622; D4:($B4,$2D,$BC,$56,$DB,$0A,$E5,$16));
  1481. FOLDERID_SavedGames: TGUID = (D1:$4C5C32FF; D2:$BB9D; D3:$43B0; D4:($B5,$B4,$2D,$72,$E5,$4E,$AA,$A4));
  1482. var
  1483. ShellFolder: String;
  1484. begin
  1485. if not ShellFoldersRead[Common, ID] then begin
  1486. if ID = sfUserProgramFiles then
  1487. ShellFolder := GetShellFolderByGUID(FOLDERID_UserProgramFiles, True)
  1488. else if ID = sfUserCommonFiles then
  1489. ShellFolder := GetShellFolderByGUID(FOLDERID_UserProgramFilesCommon, True)
  1490. else if ID = sfUserSavedGames then
  1491. ShellFolder := GetShellFolderByGUID(FOLDERID_SavedGames, True)
  1492. else
  1493. ShellFolder := GetShellFolderByCSIDL(FolderIDs[Common, ID], True);
  1494. ShellFolders[Common, ID] := ShellFolder;
  1495. ShellFoldersRead[Common, ID] := True;
  1496. end;
  1497. Result := ShellFolders[Common, ID];
  1498. end;
  1499. function InstallOnThisVersion(const MinVersion: TSetupVersionData;
  1500. const OnlyBelowVersion: TSetupVersionData): TInstallOnThisVersionResult;
  1501. var
  1502. Ver, Ver2, MinVer, OnlyBelowVer: Cardinal;
  1503. begin
  1504. Ver := WindowsVersion;
  1505. MinVer := MinVersion.NTVersion;
  1506. OnlyBelowVer := OnlyBelowVersion.NTVersion;
  1507. Result := irInstall;
  1508. if MinVer = 0 then
  1509. Result := irNotOnThisPlatform
  1510. else begin
  1511. if Ver < MinVer then
  1512. Result := irVersionTooLow
  1513. else if (LongRec(Ver).Hi = LongRec(MinVer).Hi) and
  1514. (NTServicePackLevel < MinVersion.NTServicePack) then
  1515. Result := irServicePackTooLow
  1516. else begin
  1517. if OnlyBelowVer <> 0 then begin
  1518. Ver2 := Ver;
  1519. { A build number of 0 on OnlyBelowVersion means 'match any build' }
  1520. if LongRec(OnlyBelowVer).Lo = 0 then
  1521. Ver2 := Ver2 and $FFFF0000; { set build number to zero on Ver2 also }
  1522. { Note: When OnlyBelowVersion includes a service pack level, the
  1523. version number test changes from a "<" to "<=" operation. Thus,
  1524. on Windows 2000 SP4, 5.0 and 5.0.2195 will fail, but 5.0sp5 and
  1525. 5.0.2195sp5 will pass. }
  1526. if (Ver2 > OnlyBelowVer) or
  1527. ((Ver2 = OnlyBelowVer) and
  1528. (OnlyBelowVersion.NTServicePack = 0)) or
  1529. ((LongRec(Ver).Hi = LongRec(OnlyBelowVer).Hi) and
  1530. (OnlyBelowVersion.NTServicePack <> 0) and
  1531. (NTServicePackLevel >= OnlyBelowVersion.NTServicePack)) then
  1532. Result := irVerTooHigh;
  1533. end;
  1534. end;
  1535. end;
  1536. end;
  1537. function GetSizeOfComponent(const ComponentName: String; const ExtraDiskSpaceRequired: Integer64): Integer64;
  1538. var
  1539. ComponentNameAsList: TStringList;
  1540. FileEntry: PSetupFileEntry;
  1541. I: Integer;
  1542. begin
  1543. Result := ExtraDiskSpaceRequired;
  1544. ComponentNameAsList := TStringList.Create();
  1545. try
  1546. ComponentNameAsList.Add(ComponentName);
  1547. for I := 0 to Entries[seFile].Count-1 do begin
  1548. FileEntry := PSetupFileEntry(Entries[seFile][I]);
  1549. with FileEntry^ do begin
  1550. if (Components <> '') and
  1551. ((Tasks = '') and (Check = '')) then begin {don't count tasks or scripted entries}
  1552. if ShouldProcessFileEntry(ComponentNameAsList, nil, FileEntry, True) then begin
  1553. if LocationEntry <> -1 then
  1554. Inc6464(Result, PSetupFileLocationEntry(Entries[seFileLocation][LocationEntry])^.OriginalSize)
  1555. else
  1556. Inc6464(Result, ExternalSize);
  1557. end;
  1558. end;
  1559. end;
  1560. end;
  1561. finally
  1562. ComponentNameAsList.Free();
  1563. end;
  1564. end;
  1565. function GetSizeOfType(const TypeName: String; const IsCustom: Boolean): Integer64;
  1566. var
  1567. ComponentTypes: TStringList;
  1568. I: Integer;
  1569. begin
  1570. Result.Hi := 0;
  1571. Result.Lo := 0;
  1572. ComponentTypes := TStringList.Create();
  1573. for I := 0 to Entries[seComponent].Count-1 do begin
  1574. with PSetupComponentEntry(Entries[seComponent][I])^ do begin
  1575. SetStringsFromCommaString(ComponentTypes, Types);
  1576. { For custom types, only count fixed components. Otherwise count all. }
  1577. if IsCustom then begin
  1578. if (coFixed in Options) and ListContains(ComponentTypes, TypeName) then
  1579. Inc6464(Result, Size);
  1580. end else begin
  1581. if ListContains(ComponentTypes, TypeName) then
  1582. Inc6464(Result, Size);
  1583. end;
  1584. end;
  1585. end;
  1586. ComponentTypes.Free();
  1587. end;
  1588. function IsRecurseableDirectory(const FindData: TWin32FindData): Boolean;
  1589. { Returns True if FindData is a directory that may be recursed into.
  1590. Intended only for use when processing external+recursesubdirs file entries. }
  1591. begin
  1592. Result :=
  1593. (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY <> 0) and
  1594. (FindData.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN = 0) and
  1595. (StrComp(FindData.cFileName, '.') <> 0) and
  1596. (StrComp(FindData.cFileName, '..') <> 0);
  1597. end;
  1598. type
  1599. TEnumFilesProc = function(const DisableFsRedir: Boolean; const Filename: String;
  1600. const Param: Pointer): Boolean;
  1601. function DummyDeleteDirProc(const DisableFsRedir: Boolean; const Filename: String;
  1602. const Param: Pointer): Boolean;
  1603. begin
  1604. { We don't actually want to delete the dir, so just return success. }
  1605. Result := True;
  1606. end;
  1607. { Enumerates the files we're going to install and delete. Returns True on success.
  1608. Likewise EnumFilesProc should return True on success and return False
  1609. to break the enum and to cause EnumFiles to return False instead of True. }
  1610. function EnumFiles(const EnumFilesProc: TEnumFilesProc;
  1611. const WizardComponents, WizardTasks: TStringList; const Param: Pointer): Boolean;
  1612. function RecurseExternalFiles(const DisableFsRedir: Boolean;
  1613. const SearchBaseDir, SearchSubDir, SearchWildcard: String;
  1614. const SourceIsWildcard: Boolean; const CurFile: PSetupFileEntry): Boolean;
  1615. var
  1616. SearchFullPath, DestName: String;
  1617. H: THandle;
  1618. FindData: TWin32FindData;
  1619. begin
  1620. SearchFullPath := SearchBaseDir + SearchSubDir + SearchWildcard;
  1621. Result := True;
  1622. H := FindFirstFileRedir(DisableFsRedir, SearchFullPath, FindData);
  1623. if H <> INVALID_HANDLE_VALUE then begin
  1624. try
  1625. repeat
  1626. if FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then begin
  1627. if SourceIsWildcard then
  1628. if FindData.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN <> 0 then
  1629. Continue;
  1630. DestName := ExpandConst(CurFile^.DestName);
  1631. if not(foCustomDestName in CurFile^.Options) then
  1632. DestName := DestName + SearchSubDir + FindData.cFileName
  1633. else if SearchSubDir <> '' then
  1634. DestName := PathExtractPath(DestName) + SearchSubDir + PathExtractName(DestName);
  1635. if not EnumFilesProc(DisableFsRedir, DestName, Param) then begin
  1636. Result := False;
  1637. Exit;
  1638. end;
  1639. end;
  1640. until not FindNextFile(H, FindData);
  1641. finally
  1642. Windows.FindClose(H);
  1643. end;
  1644. end;
  1645. if foRecurseSubDirsExternal in CurFile^.Options then begin
  1646. H := FindFirstFileRedir(DisableFsRedir, SearchBaseDir + SearchSubDir + '*', FindData);
  1647. if H <> INVALID_HANDLE_VALUE then begin
  1648. try
  1649. repeat
  1650. if IsRecurseableDirectory(FindData) then
  1651. if not RecurseExternalFiles(DisableFsRedir, SearchBaseDir,
  1652. SearchSubDir + FindData.cFileName + '\', SearchWildcard,
  1653. SourceIsWildcard, CurFile) then begin
  1654. Result := False;
  1655. Exit;
  1656. end;
  1657. until not FindNextFile(H, FindData);
  1658. finally
  1659. Windows.FindClose(H);
  1660. end;
  1661. end;
  1662. end;
  1663. end;
  1664. var
  1665. I: Integer;
  1666. CurFile: PSetupFileEntry;
  1667. DisableFsRedir: Boolean;
  1668. SourceWildcard: String;
  1669. begin
  1670. Result := True;
  1671. { [Files] }
  1672. for I := 0 to Entries[seFile].Count-1 do begin
  1673. CurFile := PSetupFileEntry(Entries[seFile][I]);
  1674. if (CurFile^.FileType = ftUserFile) and
  1675. ShouldProcessFileEntry(WizardComponents, WizardTasks, CurFile, False) then begin
  1676. DisableFsRedir := ShouldDisableFsRedirForFileEntry(CurFile);
  1677. if CurFile^.LocationEntry <> -1 then begin
  1678. { Non-external file }
  1679. if not EnumFilesProc(DisableFsRedir, ExpandConst(CurFile^.DestName), Param) then begin
  1680. Result := False;
  1681. Exit;
  1682. end;
  1683. end
  1684. else begin
  1685. { External file }
  1686. SourceWildcard := ExpandConst(CurFile^.SourceFilename);
  1687. if not RecurseExternalFiles(DisableFsRedir, PathExtractPath(SourceWildcard), '',
  1688. PathExtractName(SourceWildcard), IsWildcard(SourceWildcard), CurFile) then begin
  1689. Result := False;
  1690. Exit;
  1691. end;
  1692. end;
  1693. end;
  1694. end;
  1695. { [InstallDelete] }
  1696. for I := 0 to Entries[seInstallDelete].Count-1 do
  1697. with PSetupDeleteEntry(Entries[seInstallDelete][I])^ do
  1698. if ShouldProcessEntry(WizardComponents, WizardTasks, Components, Tasks, Languages, Check) then begin
  1699. case DeleteType of
  1700. dfFiles, dfFilesAndOrSubdirs:
  1701. if not DelTree(InstallDefaultDisableFsRedir, ExpandConst(Name), False, True, DeleteType = dfFilesAndOrSubdirs, True,
  1702. DummyDeleteDirProc, EnumFilesProc, Param) then begin
  1703. Result := False;
  1704. Exit;
  1705. end;
  1706. dfDirIfEmpty:
  1707. if not DelTree(InstallDefaultDisableFsRedir, ExpandConst(Name), True, False, False, True,
  1708. DummyDeleteDirProc, EnumFilesProc, Param) then begin
  1709. Result := False;
  1710. Exit;
  1711. end;
  1712. end;
  1713. end;
  1714. end;
  1715. procedure EnumProc(const Filename: String; Param: Pointer);
  1716. begin
  1717. TStringList(Param).Add(PathLowercase(Filename));
  1718. end;
  1719. var
  1720. CheckForFileSL: TStringList;
  1721. function CheckForFile(const DisableFsRedir: Boolean; const AFilename: String;
  1722. const Param: Pointer): Boolean;
  1723. var
  1724. Filename: String;
  1725. J: Integer;
  1726. begin
  1727. Filename := AFilename;
  1728. if not DisableFsRedir then
  1729. Filename := ReplaceSystemDirWithSysWow64(Filename);
  1730. Filename := PathLowercase(Filename);
  1731. for J := 0 to CheckForFileSL.Count-1 do begin
  1732. if CheckForFileSL[J] = Filename then begin
  1733. LogFmt('Found pending rename or delete that matches one of our files: %s', [Filename]);
  1734. Result := False; { Break the enum, just need to know if any matches }
  1735. Exit;
  1736. end;
  1737. end;
  1738. Result := True; { Success! }
  1739. end;
  1740. { Checks if no file we're going to install or delete has a pending rename or delete. }
  1741. function PreviousInstallCompleted(const WizardComponents, WizardTasks: TStringList): Boolean;
  1742. begin
  1743. Result := True;
  1744. if Entries[seFile].Count = 0 then
  1745. Exit;
  1746. CheckForFileSL := TStringList.Create;
  1747. try
  1748. EnumFileReplaceOperationsFilenames(EnumProc, CheckForFileSL);
  1749. if CheckForFileSL.Count = 0 then
  1750. Exit;
  1751. Result := EnumFiles(CheckForFile, WizardComponents, WizardTasks, nil);
  1752. finally
  1753. CheckForFileSL.Free;
  1754. end;
  1755. end;
  1756. type
  1757. TArrayOfPWideChar = array[0..(MaxInt div SizeOf(PWideChar))-1] of PWideChar;
  1758. PArrayOfPWideChar = ^TArrayOfPWideChar;
  1759. var
  1760. RegisterFileBatchFilenames: PArrayOfPWideChar;
  1761. RegisterFileFilenamesBatchMax, RegisterFileFilenamesBatchCount: Integer;
  1762. function RegisterFile(const DisableFsRedir: Boolean; const AFilename: String;
  1763. const Param: Pointer): Boolean;
  1764. var
  1765. Filename, Text: String;
  1766. I, Len: Integer;
  1767. CheckFilter, Match: Boolean;
  1768. begin
  1769. Filename := AFilename;
  1770. { First: check filters and self. }
  1771. if Filename <> '' then begin
  1772. CheckFilter := Boolean(Param);
  1773. if CheckFilter then begin
  1774. Match := False;
  1775. Text := PathLowercase(PathExtractName(Filename));
  1776. for I := 0 to CloseApplicationsFilterList.Count-1 do begin
  1777. if WildcardMatch(PChar(Text), PChar(CloseApplicationsFilterList[I])) then begin
  1778. Match := True;
  1779. Break;
  1780. end;
  1781. end;
  1782. if Match then begin
  1783. for I := 0 to CloseApplicationsFilterExcludesList.Count-1 do begin
  1784. if WildcardMatch(PChar(Text), PChar(CloseApplicationsFilterExcludesList[I])) then begin
  1785. Match := False;
  1786. Break;
  1787. end;
  1788. end;
  1789. end;
  1790. if not Match then begin
  1791. { No match with filter so exit but don't return an error. }
  1792. Result := True;
  1793. Exit;
  1794. end;
  1795. end;
  1796. if PathCompare(Filename, SetupLdrOriginalFilename) = 0 then begin
  1797. { Don't allow self to be registered but don't return an error. }
  1798. Result := True;
  1799. Exit;
  1800. end;
  1801. end;
  1802. { Secondly: check if we need to register this batch, either because the batch is full
  1803. or because we're done scanning and have leftovers. }
  1804. if ((Filename <> '') and (RegisterFileFilenamesBatchCount = RegisterFileFilenamesBatchMax)) or
  1805. ((Filename = '') and (RegisterFileFilenamesBatchCount > 0)) then begin
  1806. if RmRegisterResources(RmSessionHandle, RegisterFileFilenamesBatchCount, RegisterFileBatchFilenames, 0, nil, 0, nil) = ERROR_SUCCESS then begin
  1807. for I := 0 to RegisterFileFilenamesBatchCount-1 do
  1808. FreeMem(RegisterFileBatchFilenames[I]);
  1809. RegisterFileFilenamesBatchCount := 0;
  1810. end else begin
  1811. RmEndSession(RmSessionHandle);
  1812. RmSessionStarted := False;
  1813. end;
  1814. end;
  1815. { Finally: add this file to the batch. }
  1816. if RmSessionStarted and (FileName <> '') then begin
  1817. { From MSDN: "Installers should not disable file system redirection before calling
  1818. the Restart Manager API. This means that a 32-bit installer run on 64-bit Windows
  1819. is unable register a file in the %windir%\system32 directory." This is incorrect,
  1820. we can register such files by using the Sysnative alias. }
  1821. if DisableFsRedir then
  1822. Filename := ReplaceSystemDirWithSysNative(Filename, IsWin64);
  1823. if InitLogCloseApplications then
  1824. LogFmt('Found a file to register with RestartManager: %s', [Filename]);
  1825. Len := Length(Filename);
  1826. GetMem(RegisterFileBatchFilenames[RegisterFileFilenamesBatchCount], (Len + 1) * SizeOf(RegisterFileBatchFilenames[RegisterFileFilenamesBatchCount][0]));
  1827. StrPCopy(RegisterFileBatchFilenames[RegisterFileFilenamesBatchCount], Filename);
  1828. Inc(RegisterFileFilenamesBatchCount);
  1829. Inc(RmRegisteredFilesCount);
  1830. end;
  1831. Result := RmSessionStarted; { Break the enum if there was an error, else continue. }
  1832. end;
  1833. { Helper function for [Code] to register extra files. }
  1834. var
  1835. AllowCodeRegisterExtraCloseApplicationsResource: Boolean;
  1836. function CodeRegisterExtraCloseApplicationsResource(const DisableFsRedir: Boolean; const AFilename: String): Boolean;
  1837. begin
  1838. if AllowCodeRegisterExtraCloseApplicationsResource then
  1839. Result := RegisterFile(DisableFsRedir, AFilename, Pointer(False))
  1840. else begin
  1841. InternalError('Cannot call "RegisterExtraCloseApplicationsResource" function at this time');
  1842. Result := False;
  1843. end;
  1844. end;
  1845. { Register all files we're going to install or delete. Ends RmSession on errors. }
  1846. procedure RegisterResourcesWithRestartManager(const WizardComponents, WizardTasks: TStringList);
  1847. var
  1848. I: Integer;
  1849. begin
  1850. { Note: MSDN says we shouldn't call RmRegisterResources for each file because of speed, but calling
  1851. it once for all files adds extra memory usage, so calling it in batches. }
  1852. RegisterFileFilenamesBatchMax := 1000;
  1853. GetMem(RegisterFileBatchFilenames, RegisterFileFilenamesBatchMax * SizeOf(RegisterFileBatchFilenames[0]));
  1854. try
  1855. { Register our files. }
  1856. RmRegisteredFilesCount := 0;
  1857. EnumFiles(RegisterFile, WizardComponents, WizardTasks, Pointer(True));
  1858. { Ask [Code] for more files. }
  1859. if CodeRunner <> nil then begin
  1860. AllowCodeRegisterExtraCloseApplicationsResource := True;
  1861. try
  1862. try
  1863. CodeRunner.RunProcedures('RegisterExtraCloseApplicationsResources', [''], False);
  1864. except
  1865. Log('RegisterExtraCloseApplicationsResources raised an exception.');
  1866. Application.HandleException(nil);
  1867. end;
  1868. finally
  1869. AllowCodeRegisterExtraCloseApplicationsResource := False;
  1870. end;
  1871. end;
  1872. { Don't forget to register leftovers. }
  1873. if RmSessionStarted then
  1874. RegisterFile(False, '', nil);
  1875. finally
  1876. for I := 0 to RegisterFileFilenamesBatchCount-1 do
  1877. FreeMem(RegisterFileBatchFilenames[I]);
  1878. FreeMem(RegisterFileBatchFilenames);
  1879. end;
  1880. end;
  1881. procedure DebugNotifyEntry(EntryType: TEntryType; Number: Integer);
  1882. var
  1883. Kind: TDebugEntryKind;
  1884. B: Boolean;
  1885. begin
  1886. if not Debugging then Exit;
  1887. case EntryType of
  1888. seDir: Kind := deDir;
  1889. seFile: Kind := deFile;
  1890. seIcon: Kind := deIcon;
  1891. seIni: Kind := deIni;
  1892. seRegistry: Kind := deRegistry;
  1893. seInstallDelete: Kind := deInstallDelete;
  1894. seUninstallDelete: Kind := deUninstallDelete;
  1895. seRun: Kind := deRun;
  1896. seUninstallRun: Kind := deUninstallRun;
  1897. else
  1898. Exit;
  1899. end;
  1900. DebugNotify(Kind, Integer(OriginalEntryIndexes[EntryType][Number]), B);
  1901. end;
  1902. procedure CodeRunnerOnLog(const S: String);
  1903. begin
  1904. Log(S);
  1905. end;
  1906. procedure CodeRunnerOnLogFmt(const S: String; const Args: array of const);
  1907. begin
  1908. LogFmt(S, Args);
  1909. end;
  1910. procedure CodeRunnerOnDllImport(var DllName: String; var ForceDelayLoad: Boolean);
  1911. var
  1912. S, BaseName, FullName: String;
  1913. FirstFile: Boolean;
  1914. P: Integer;
  1915. begin
  1916. while True do begin
  1917. if Pos('setup:', DllName) = 1 then begin
  1918. if IsUninstaller then begin
  1919. DllName := '';
  1920. ForceDelayLoad := True;
  1921. Exit;
  1922. end;
  1923. Delete(DllName, 1, Length('setup:'));
  1924. end
  1925. else if Pos('uninstall:', DllName) = 1 then begin
  1926. if not IsUninstaller then begin
  1927. DllName := '';
  1928. ForceDelayLoad := True;
  1929. Exit;
  1930. end;
  1931. Delete(DllName, 1, Length('uninstall:'));
  1932. end
  1933. else
  1934. Break;
  1935. end;
  1936. if Pos('files:', DllName) = 1 then begin
  1937. if IsUninstaller then begin
  1938. { Uninstall doesn't do 'files:' }
  1939. DllName := '';
  1940. ForceDelayLoad := True;
  1941. end
  1942. else begin
  1943. S := Copy(DllName, Length('files:')+1, Maxint);
  1944. FirstFile := True;
  1945. repeat
  1946. P := ConstPos(',', S);
  1947. if P = 0 then
  1948. BaseName := S
  1949. else begin
  1950. BaseName := Copy(S, 1, P-1);
  1951. Delete(S, 1, P);
  1952. end;
  1953. BaseName := ExpandConst((BaseName));
  1954. FullName := AddBackslash(TempInstallDir) + BaseName;
  1955. if not NewFileExists(FullName) then
  1956. ExtractTemporaryFile(BaseName);
  1957. if FirstFile then begin
  1958. DllName := FullName;
  1959. FirstFile := False;
  1960. end;
  1961. until P = 0;
  1962. end;
  1963. end
  1964. else
  1965. DllName := ExpandConst(DllName);
  1966. end;
  1967. function CodeRunnerOnDebug(const Position: LongInt;
  1968. var ContinueStepOver: Boolean): Boolean;
  1969. begin
  1970. Result := DebugNotify(deCodeLine, Position, ContinueStepOver, CodeRunner.GetCallStack);
  1971. end;
  1972. function CodeRunnerOnDebugIntermediate(const Position: LongInt;
  1973. var ContinueStepOver: Boolean): Boolean;
  1974. begin
  1975. Result := DebugNotifyIntermediate(deCodeLine, Position, ContinueStepOver);
  1976. end;
  1977. procedure CodeRunnerOnException(const Exception: AnsiString; const Position: LongInt);
  1978. begin
  1979. if Debugging then
  1980. DebugNotifyException(String(Exception), deCodeLine, Position);
  1981. end;
  1982. procedure SetActiveLanguage(const I: Integer);
  1983. { Activates the specified language }
  1984. var
  1985. LangEntry: PSetupLanguageEntry;
  1986. J: Integer;
  1987. begin
  1988. if ActiveLanguage = I then
  1989. Exit;
  1990. LangEntry := Entries[seLanguage][I];
  1991. AssignSetupMessages(LangEntry.Data[1], Length(LangEntry.Data));
  1992. { Remove outdated < and > markers from the Back and Next buttons. Done here for now to avoid a Default.isl change. }
  1993. StringChange(SetupMessages[msgButtonBack], '< ', '');
  1994. StringChange(SetupMessages[msgButtonNext], ' >', '');
  1995. ActiveLanguage := I;
  1996. Finalize(LangOptions); { prevent leak on D2 }
  1997. LangOptions := LangEntry^;
  1998. if LangEntry.LicenseText <> '' then
  1999. ActiveLicenseText := LangEntry.LicenseText
  2000. else
  2001. ActiveLicenseText := SetupHeader.LicenseText;
  2002. if LangEntry.InfoBeforeText <> '' then
  2003. ActiveInfoBeforeText := LangEntry.InfoBeforeText
  2004. else
  2005. ActiveInfoBeforeText := SetupHeader.InfoBeforeText;
  2006. if LangEntry.InfoAfterText <> '' then
  2007. ActiveInfoAfterText := LangEntry.InfoAfterText
  2008. else
  2009. ActiveInfoAfterText := SetupHeader.InfoAfterText;
  2010. SetMessageBoxRightToLeft(LangOptions.RightToLeft);
  2011. SetMessageBoxCaption(mbInformation, PChar(SetupMessages[msgInformationTitle]));
  2012. SetMessageBoxCaption(mbConfirmation, PChar(SetupMessages[msgConfirmTitle]));
  2013. SetMessageBoxCaption(mbError, PChar(SetupMessages[msgErrorTitle]));
  2014. SetMessageBoxCaption(mbCriticalError, PChar(SetupMessages[msgErrorTitle]));
  2015. Application.Title := SetupMessages[msgSetupAppTitle];
  2016. for J := 0 to Entries[seType].Count-1 do begin
  2017. with PSetupTypeEntry(Entries[seType][J])^ do begin
  2018. case Typ of
  2019. ttDefaultFull: Description := SetupMessages[msgFullInstallation];
  2020. ttDefaultCompact: Description := SetupMessages[msgCompactInstallation];
  2021. ttDefaultCustom: Description := SetupMessages[msgCustomInstallation];
  2022. end;
  2023. end;
  2024. end;
  2025. { Tell the first instance to change its language too. (It's possible for
  2026. the first instance to display messages after Setup terminates, e.g. if it
  2027. fails to restart the computer.) }
  2028. if SetupNotifyWndPresent then
  2029. SendNotifyMessage(SetupNotifyWnd, WM_USER + 150, 10001, I);
  2030. end;
  2031. function GetLanguageEntryProc(Index: Integer; var Entry: PSetupLanguageEntry): Boolean;
  2032. begin
  2033. Result := False;
  2034. if Index < Entries[seLanguage].Count then begin
  2035. Entry := Entries[seLanguage][Index];
  2036. Result := True;
  2037. end;
  2038. end;
  2039. procedure ActivateDefaultLanguage;
  2040. { Auto-detects the most appropriate language and activates it.
  2041. Also initializes the ShowLanguageDialog and MatchedLangParameter variables.
  2042. Note: A like-named version of this function is also present in SetupLdr.dpr. }
  2043. var
  2044. I: Integer;
  2045. begin
  2046. MatchedLangParameter := False;
  2047. case DetermineDefaultLanguage(GetLanguageEntryProc,
  2048. SetupHeader.LanguageDetectionMethod, InitLang, I) of
  2049. ddNoMatch: ShowLanguageDialog := (SetupHeader.ShowLanguageDialog <> slNo);
  2050. ddMatch: ShowLanguageDialog := (SetupHeader.ShowLanguageDialog = slYes);
  2051. else
  2052. begin
  2053. { ddMatchLangParameter }
  2054. ShowLanguageDialog := False;
  2055. MatchedLangParameter := True;
  2056. end;
  2057. end;
  2058. SetActiveLanguage(I);
  2059. end;
  2060. procedure LogCompatibilityMode;
  2061. var
  2062. S: String;
  2063. begin
  2064. S := GetEnv('__COMPAT_LAYER');
  2065. if S <> '' then
  2066. LogFmt('Compatibility mode: %s (%s)', [SYesNo[True], S]);
  2067. end;
  2068. procedure LogWindowsVersion;
  2069. function ArchitecturesToStr(const Architectures: TSetupProcessorArchitectures;
  2070. const Separator: String): String;
  2071. procedure AppendArchitecture(var S: String; const Separator, L: String);
  2072. begin
  2073. if S <> '' then
  2074. S := S + Separator + L
  2075. else
  2076. S := L;
  2077. end;
  2078. var
  2079. I: TSetupProcessorArchitecture;
  2080. begin
  2081. Result := '';
  2082. for I := Low(I) to High(I) do
  2083. if I in Architectures then
  2084. AppendArchitecture(Result, Separator, SetupProcessorArchitectureNames[I]);
  2085. end;
  2086. var
  2087. SP: String;
  2088. begin
  2089. if NTServicePackLevel <> 0 then begin
  2090. SP := ' SP' + IntToStr(Hi(NTServicePackLevel));
  2091. if Lo(NTServicePackLevel) <> 0 then
  2092. SP := SP + '.' + IntToStr(Lo(NTServicePackLevel));
  2093. end;
  2094. LogFmt('Windows version: %u.%u.%u%s', [WindowsVersion shr 24,
  2095. (WindowsVersion shr 16) and $FF, WindowsVersion and $FFFF, SP]);
  2096. var Bits := 32;
  2097. if IsWin64 then
  2098. Bits := 64;
  2099. LogFmt('Windows architecture: %s (%d-bit)', [SetupProcessorArchitectureNames[ProcessorArchitecture], Bits]);
  2100. LogFmt('Machine types supported by system: %s', [ArchitecturesToStr(MachineTypesSupportedBySystem, ' ')]);
  2101. if IsAdmin then
  2102. Log('User privileges: Administrative')
  2103. else if IsPowerUserOrAdmin then
  2104. Log('User privileges: Power User')
  2105. else
  2106. Log('User privileges: None');
  2107. end;
  2108. function GetMessageBoxResultText(const AResult: Integer): String;
  2109. begin
  2110. case AResult of
  2111. IDOK: Result := 'OK';
  2112. IDCANCEL: Result := 'Cancel';
  2113. IDABORT: Result := 'Abort';
  2114. IDRETRY: Result := 'Retry';
  2115. IDIGNORE: Result := 'Ignore';
  2116. IDYES: Result := 'Yes';
  2117. IDNO: Result := 'No';
  2118. IDTRYAGAIN: Result := 'Try Again';
  2119. IDCONTINUE: Result := 'Continue';
  2120. else
  2121. Result := IntToStr(AResult);
  2122. end;
  2123. end;
  2124. function GetButtonsText(const Buttons: Cardinal): String;
  2125. const
  2126. { We don't use this type, but end users are liable to in [Code] }
  2127. MB_CANCELTRYCONTINUE = $00000006;
  2128. begin
  2129. case Buttons and MB_TYPEMASK of
  2130. MB_OK: Result := 'OK';
  2131. MB_OKCANCEL: Result := 'OK/Cancel';
  2132. MB_ABORTRETRYIGNORE: Result := 'Abort/Retry/Ignore';
  2133. MB_YESNOCANCEL: Result := 'Yes/No/Cancel';
  2134. MB_YESNO: Result := 'Yes/No';
  2135. MB_RETRYCANCEL: Result := 'Retry/Cancel';
  2136. MB_CANCELTRYCONTINUE: Result := 'Cancel/Try Again/Continue';
  2137. else
  2138. Result := IntToStr(Buttons and MB_TYPEMASK);
  2139. end;
  2140. end;
  2141. procedure LogSuppressedMessageBox(const Text: PChar; const Buttons: Cardinal;
  2142. const Default: Integer);
  2143. begin
  2144. Log(Format('Defaulting to %s for suppressed message box (%s):' + SNewLine,
  2145. [GetMessageBoxResultText(Default), GetButtonsText(Buttons)]) + Text);
  2146. end;
  2147. procedure LogMessageBox(const Text: PChar; const Buttons: Cardinal);
  2148. begin
  2149. Log(Format('Message box (%s):' + SNewLine,
  2150. [GetButtonsText(Buttons)]) + Text);
  2151. end;
  2152. function LoggedAppMessageBox(const Text, Caption: PChar; const Flags: Longint;
  2153. const Suppressible: Boolean; const Default: Integer): Integer;
  2154. begin
  2155. if InitSuppressMsgBoxes and Suppressible then begin
  2156. LogSuppressedMessageBox(Text, Flags, Default);
  2157. Result := Default;
  2158. end else begin
  2159. LogMessageBox(Text, Flags);
  2160. Result := AppMessageBox(Text, Caption, Flags);
  2161. if Result <> 0 then
  2162. LogFmt('User chose %s.', [GetMessageBoxResultText(Result)])
  2163. else
  2164. Log('AppMessageBox failed.');
  2165. end;
  2166. end;
  2167. function LoggedMsgBox(const Text, Caption: String; const Typ: TMsgBoxType;
  2168. const Buttons: Cardinal; const Suppressible: Boolean; const Default: Integer): Integer;
  2169. begin
  2170. if InitSuppressMsgBoxes and Suppressible then begin
  2171. LogSuppressedMessageBox(PChar(Text), Buttons, Default);
  2172. Result := Default;
  2173. end else begin
  2174. LogMessageBox(PChar(Text), Buttons);
  2175. Result := MsgBox(Text, Caption, Typ, Buttons);
  2176. if Result <> 0 then
  2177. LogFmt('User chose %s.', [GetMessageBoxResultText(Result)])
  2178. else
  2179. Log('MsgBox failed.');
  2180. end;
  2181. end;
  2182. function LoggedTaskDialogMsgBox(const Icon, Instruction, Text, Caption: String;
  2183. const Typ: TMsgBoxType; const Buttons: Cardinal; const ButtonLabels: array of String;
  2184. const ShieldButton: Integer; const Suppressible: Boolean; const Default: Integer;
  2185. const VerificationText: String = ''; const pfVerificationFlagChecked: PBOOL = nil): Integer;
  2186. begin
  2187. if InitSuppressMsgBoxes and Suppressible then begin
  2188. LogSuppressedMessageBox(PChar(Text), Buttons, Default);
  2189. Result := Default;
  2190. end else begin
  2191. LogMessageBox(PChar(Text), Buttons);
  2192. Result := TaskDialogMsgBox(Icon, Instruction, Text,
  2193. Caption, Typ, Buttons, ButtonLabels, ShieldButton, VerificationText, pfVerificationFlagChecked);
  2194. if Result <> 0 then begin
  2195. LogFmt('User chose %s.', [GetMessageBoxResultText(Result)]);
  2196. if pfVerificationFlagChecked <> nil then
  2197. LogFmt('User chose %s for the verification.', [SYesNo[pfVerificationFlagChecked^]]);
  2198. end else
  2199. Log('TaskDialogMsgBox failed.');
  2200. end;
  2201. end;
  2202. procedure RestartComputerFromThisProcess;
  2203. begin
  2204. RestartInitiatedByThisProcess := True;
  2205. { Note: Depending on the OS, RestartComputer may not return if successful }
  2206. if not RestartComputer then begin
  2207. LoggedMsgBox(SetupMessages[msgErrorRestartingComputer], '', mbError,
  2208. MB_OK, True, IDOK);
  2209. end;
  2210. end;
  2211. procedure RespawnSetupElevated(const AParams: String);
  2212. { Starts a new, elevated Setup(Ldr) process and waits until it terminates.
  2213. Does not return; either calls Halt or raises an exception. }
  2214. var
  2215. Cancelled: Boolean;
  2216. Server: TSpawnServer;
  2217. ParamNotifyWnd: HWND;
  2218. RespawnResults: record
  2219. ExitCode: DWORD;
  2220. NotifyRestartRequested: Boolean;
  2221. NotifyNewLanguage: Integer;
  2222. end;
  2223. begin
  2224. Cancelled := False;
  2225. try
  2226. Server := TSpawnServer.Create;
  2227. try
  2228. if SetupNotifyWndPresent then
  2229. ParamNotifyWnd := SetupNotifyWnd
  2230. else
  2231. ParamNotifyWnd := Server.Wnd;
  2232. RespawnSelfElevated(SetupLdrOriginalFilename,
  2233. Format('/SPAWNWND=$%x /NOTIFYWND=$%x ', [Server.Wnd, ParamNotifyWnd]) +
  2234. AParams, RespawnResults.ExitCode);
  2235. RespawnResults.NotifyRestartRequested := Server.NotifyRestartRequested;
  2236. RespawnResults.NotifyNewLanguage := Server.NotifyNewLanguage;
  2237. finally
  2238. Server.Free;
  2239. end;
  2240. except
  2241. { If the user clicked Cancel on the dialog, halt with special exit code }
  2242. if ExceptObject is EAbort then
  2243. Cancelled := True
  2244. else
  2245. raise;
  2246. end;
  2247. if Cancelled then
  2248. Halt(ecCancelledBeforeInstall);
  2249. if not SetupNotifyWndPresent then begin
  2250. { In the UseSetupLdr=no case, there is no notify window handle to pass to
  2251. RespawnSelfElevated, so it hosts one itself. Process the results. }
  2252. try
  2253. if (RespawnResults.NotifyNewLanguage >= 0) and
  2254. (RespawnResults.NotifyNewLanguage < Entries[seLanguage].Count) then
  2255. SetActiveLanguage(RespawnResults.NotifyNewLanguage);
  2256. if RespawnResults.NotifyRestartRequested then begin
  2257. { Note: Depending on the OS, this may not return if successful }
  2258. RestartComputerFromThisProcess;
  2259. end;
  2260. except
  2261. { In the unlikely event that something above raises an exception, handle
  2262. it here so the right exit code will still be returned below }
  2263. Application.HandleException(nil);
  2264. end;
  2265. end;
  2266. Halt(RespawnResults.ExitCode);
  2267. end;
  2268. procedure InitializeCommonVars;
  2269. { Initializes variables shared between Setup and Uninstall }
  2270. begin
  2271. IsAdmin := IsAdminLoggedOn;
  2272. IsPowerUserOrAdmin := IsAdmin or IsPowerUserLoggedOn;
  2273. Randomize;
  2274. end;
  2275. procedure InitializeAdminInstallMode(const AAdminInstallMode: Boolean);
  2276. { Initializes IsAdminInstallMode and other global variables that depend on it }
  2277. const
  2278. RootKeys: array[Boolean] of HKEY = (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE);
  2279. begin
  2280. LogFmt('Administrative install mode: %s', [SYesNo[AAdminInstallMode]]);
  2281. IsAdminInstallMode := AAdminInstallMode;
  2282. InstallModeRootKey := RootKeys[AAdminInstallMode];
  2283. LogFmt('Install mode root key: %s', [GetRegRootKeyName(InstallModeRootKey)]);
  2284. end;
  2285. procedure Initialize64BitInstallMode(const A64BitInstallMode: Boolean);
  2286. { Initializes Is64BitInstallMode and other global variables that depend on it }
  2287. begin
  2288. Is64BitInstallMode := A64BitInstallMode;
  2289. InstallDefaultDisableFsRedir := A64BitInstallMode;
  2290. ScriptFuncDisableFsRedir := A64BitInstallMode;
  2291. if A64BitInstallMode then
  2292. InstallDefaultRegView := rv64Bit
  2293. else
  2294. InstallDefaultRegView := rv32Bit;
  2295. end;
  2296. procedure Log64BitInstallMode;
  2297. begin
  2298. LogFmt('64-bit install mode: %s', [SYesNo[Is64BitInstallMode]]);
  2299. end;
  2300. procedure InitializeSetup;
  2301. { Initializes various vars used by the setup. This is called in the project
  2302. source. }
  2303. var
  2304. DecompressorDLL: TMemoryStream;
  2305. function ExtractLongWord(var S: String): LongWord;
  2306. var
  2307. P: Integer;
  2308. begin
  2309. P := PathPos(',', S);
  2310. if P = 0 then
  2311. raise Exception.Create('ExtractLongWord: Missing comma');
  2312. Result := LongWord(StrToInt(Copy(S, 1, P-1)));
  2313. Delete(S, 1, P);
  2314. end;
  2315. procedure AbortInit(const Msg: TSetupMessageID);
  2316. begin
  2317. LoggedMsgBox(SetupMessages[Msg], '', mbCriticalError, MB_OK, True, IDOK);
  2318. Abort;
  2319. end;
  2320. procedure AbortInitFmt1(const Msg: TSetupMessageID; const Arg1: String);
  2321. begin
  2322. LoggedMsgBox(FmtSetupMessage(Msg, [Arg1]), '', mbCriticalError, MB_OK, True, IDOK);
  2323. Abort;
  2324. end;
  2325. procedure AbortInitServicePackRequired(const ServicePack: Word);
  2326. begin
  2327. LoggedMsgBox(FmtSetupMessage(msgWindowsServicePackRequired, ['Windows',
  2328. IntToStr(Hi(ServicePack))]), '', mbCriticalError, MB_OK, True, IDOK);
  2329. Abort;
  2330. end;
  2331. procedure ReadFileIntoStream(const Stream: TStream;
  2332. const R: TCompressedBlockReader);
  2333. type
  2334. PBuffer = ^TBuffer;
  2335. TBuffer = array[0..8191] of Byte;
  2336. var
  2337. Buf: PBuffer;
  2338. BytesLeft, Bytes: Longint;
  2339. begin
  2340. New(Buf);
  2341. try
  2342. R.Read(BytesLeft, SizeOf(BytesLeft));
  2343. while BytesLeft > 0 do begin
  2344. Bytes := BytesLeft;
  2345. if Bytes > SizeOf(Buf^) then Bytes := SizeOf(Buf^);
  2346. R.Read(Buf^, Bytes);
  2347. Stream.WriteBuffer(Buf^, Bytes);
  2348. Dec(BytesLeft, Bytes);
  2349. end;
  2350. finally
  2351. Dispose(Buf);
  2352. end;
  2353. end;
  2354. function ReadWizardImage(const R: TCompressedBlockReader): TBitmap;
  2355. var
  2356. MemStream: TMemoryStream;
  2357. begin
  2358. MemStream := TMemoryStream.Create;
  2359. try
  2360. ReadFileIntoStream(MemStream, R);
  2361. MemStream.Seek(0, soFromBeginning);
  2362. Result := TBitmap.Create;
  2363. Result.AlphaFormat := TAlphaFormat(SetupHeader.WizardImageAlphaFormat);
  2364. Result.LoadFromStream(MemStream);
  2365. finally
  2366. MemStream.Free;
  2367. end;
  2368. end;
  2369. procedure LoadDecompressorDLL;
  2370. var
  2371. Filename: String;
  2372. begin
  2373. Filename := AddBackslash(TempInstallDir) + '_isetup\_isdecmp.dll';
  2374. SaveStreamToTempFile(DecompressorDLL, Filename);
  2375. FreeAndNil(DecompressorDLL);
  2376. DecompressorDLLHandle := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
  2377. if DecompressorDLLHandle = 0 then
  2378. InternalError(Format('Failed to load DLL "%s"', [Filename]));
  2379. case SetupHeader.CompressMethod of
  2380. cmZip:
  2381. if not ZlibInitDecompressFunctions(DecompressorDLLHandle) then
  2382. InternalError('ZlibInitDecompressFunctions failed');
  2383. cmBzip:
  2384. if not BZInitDecompressFunctions(DecompressorDLLHandle) then
  2385. InternalError('BZInitDecompressFunctions failed');
  2386. end;
  2387. end;
  2388. var
  2389. Reader: TCompressedBlockReader;
  2390. procedure ReadEntriesWithoutVersion(const EntryType: TEntryType;
  2391. const Count: Integer; const Size: Integer);
  2392. var
  2393. I: Integer;
  2394. P: Pointer;
  2395. begin
  2396. Entries[EntryType].Capacity := Count;
  2397. for I := 0 to Count-1 do begin
  2398. P := AllocMem(Size);
  2399. SECompressedBlockRead(Reader, P^, Size, EntryStrings[EntryType],
  2400. EntryAnsiStrings[EntryType]);
  2401. Entries[EntryType].Add(P);
  2402. end;
  2403. end;
  2404. procedure ReadEntries(const EntryType: TEntryType; const Count: Integer;
  2405. const Size: Integer; const MinVersionOfs, OnlyBelowVersionOfs: Integer);
  2406. var
  2407. I: Integer;
  2408. P: Pointer;
  2409. begin
  2410. if Debugging then begin
  2411. OriginalEntryIndexes[EntryType] := TList.Create;
  2412. OriginalEntryIndexes[EntryType].Capacity := Count;
  2413. end;
  2414. Entries[EntryType].Capacity := Count;
  2415. for I := 0 to Count-1 do begin
  2416. P := AllocMem(Size);
  2417. SECompressedBlockRead(Reader, P^, Size, EntryStrings[EntryType],
  2418. EntryAnsiStrings[Entrytype]);
  2419. if (MinVersionOfs = -1) or
  2420. (InstallOnThisVersion(TSetupVersionData((@PByteArray(P)[MinVersionOfs])^),
  2421. TSetupVersionData((@PByteArray(P)[OnlyBelowVersionOfs])^)) = irInstall) then begin
  2422. Entries[EntryType].Add(P);
  2423. if Debugging then
  2424. OriginalEntryIndexes[EntryType].Add(Pointer(I));
  2425. end
  2426. else
  2427. SEFreeRec(P, EntryStrings[EntryType], EntryAnsiStrings[EntryType]);
  2428. end;
  2429. end;
  2430. function HandleInitPassword(const NeedPassword: Boolean): Boolean;
  2431. { Handles InitPassword and returns the updated value of NeedPassword }
  2432. { Also see WizardForm.CheckPassword }
  2433. begin
  2434. Result := NeedPassword;
  2435. if NeedPassword and (InitPassword <> '') then begin
  2436. var PasswordOk := False;
  2437. var S := InitPassword;
  2438. var CryptKey: TSetupEncryptionKey;
  2439. GenerateEncryptionKey(S, SetupHeader.EncryptionKDFSalt, SetupHeader.EncryptionKDFIterations, CryptKey);
  2440. if shPassword in SetupHeader.Options then
  2441. PasswordOk := TestPassword(CryptKey);
  2442. if not PasswordOk and (CodeRunner <> nil) then
  2443. PasswordOk := CodeRunner.RunBooleanFunctions('CheckPassword', [S], bcTrue, False, PasswordOk);
  2444. if PasswordOk then begin
  2445. Result := False;
  2446. if shEncryptionUsed in SetupHeader.Options then
  2447. FileExtractor.CryptKey := CryptKey;
  2448. end;
  2449. end;
  2450. end;
  2451. procedure SetupInstallMode;
  2452. begin
  2453. if InitVerySilent then
  2454. InstallMode := imVerySilent
  2455. else if InitSilent then
  2456. InstallMode := imSilent;
  2457. end;
  2458. function RecurseExternalGetSizeOfFiles(const DisableFsRedir: Boolean;
  2459. const SearchBaseDir, SearchSubDir, SearchWildcard: String;
  2460. const SourceIsWildcard: Boolean; const RecurseSubDirs: Boolean): Integer64;
  2461. var
  2462. SearchFullPath: String;
  2463. H: THandle;
  2464. FindData: TWin32FindData;
  2465. I: Integer64;
  2466. begin
  2467. SearchFullPath := SearchBaseDir + SearchSubDir + SearchWildcard;
  2468. Result.Hi := 0;
  2469. Result.Lo := 0;
  2470. H := FindFirstFileRedir(DisableFsRedir, SearchFullPath, FindData);
  2471. if H <> INVALID_HANDLE_VALUE then begin
  2472. repeat
  2473. if FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY = 0 then begin
  2474. if SourceIsWildcard then
  2475. if FindData.dwFileAttributes and FILE_ATTRIBUTE_HIDDEN <> 0 then
  2476. Continue;
  2477. I.Hi := FindData.nFileSizeHigh;
  2478. I.Lo := FindData.nFileSizeLow;
  2479. Inc6464(Result, I);
  2480. end;
  2481. until not FindNextFile(H, FindData);
  2482. Windows.FindClose(H);
  2483. end;
  2484. if RecurseSubDirs then begin
  2485. H := FindFirstFileRedir(DisableFsRedir, SearchBaseDir + SearchSubDir + '*', FindData);
  2486. if H <> INVALID_HANDLE_VALUE then begin
  2487. try
  2488. repeat
  2489. if IsRecurseableDirectory(FindData) then begin
  2490. I := RecurseExternalGetSizeOfFiles(DisableFsRedir, SearchBaseDir,
  2491. SearchSubDir + FindData.cFileName + '\', SearchWildcard,
  2492. SourceIsWildcard, RecurseSubDirs);
  2493. Inc6464(Result, I);
  2494. end;
  2495. until not FindNextFile(H, FindData);
  2496. finally
  2497. Windows.FindClose(H);
  2498. end;
  2499. end;
  2500. end;
  2501. end;
  2502. { Also see Install.pas }
  2503. function ExistingInstallationAt(const RootKey: HKEY; const SubkeyName: String): Boolean;
  2504. var
  2505. K: HKEY;
  2506. begin
  2507. if RegOpenKeyExView(InstallDefaultRegView, RootKey, PChar(SubkeyName), 0, KEY_QUERY_VALUE, K) = ERROR_SUCCESS then begin
  2508. Result := True;
  2509. RegCloseKey(K);
  2510. end else
  2511. Result := False;
  2512. end;
  2513. procedure HandlePrivilegesRequiredOverrides(var ExtraRespawnParam: String);
  2514. var
  2515. ExistingAtAdminInstallMode, ExistingAtNonAdminInstallMode, DesireAnInstallMode, DesireAdminInstallMode: Boolean;
  2516. SubkeyName, AppName: String;
  2517. begin
  2518. if HasInitPrivilegesRequired and (proCommandLine in SetupHeader.PrivilegesRequiredOverridesAllowed) then begin
  2519. SetupHeader.PrivilegesRequired := InitPrivilegesRequired;
  2520. { We don't need to set ExtraRespawnParam since the existing command line
  2521. already contains the needed parameters and it will automatically be
  2522. passed on to any respawned Setup(Ldr). }
  2523. end else if proDialog in SetupHeader.PrivilegesRequiredOverridesAllowed then begin
  2524. if shUsePreviousPrivileges in SetupHeader.Options then begin
  2525. { Note: if proDialog is used and UsePreviousPrivileges is set to "yes"
  2526. then the compiler does not allow AppId to include constants but we
  2527. should still call ExpandConst to handle any '{{'. }
  2528. SubkeyName := GetUninstallRegSubkeyName(GetUninstallRegKeyBaseName(ExpandConst(SetupHeader.AppID)));
  2529. ExistingAtAdminInstallMode := ExistingInstallationAt(HKEY_LOCAL_MACHINE, SubkeyName);
  2530. ExistingAtNonAdminInstallMode := ExistingInstallationAt(HKEY_CURRENT_USER, SubkeyName);
  2531. end else begin
  2532. ExistingAtAdminInstallMode := False;
  2533. ExistingAtNonAdminInstallMode := False;
  2534. end;
  2535. DesireAnInstallMode := True;
  2536. DesireAdminInstallMode := False; { Silence compiler }
  2537. if ExistingAtAdminInstallMode and not ExistingAtNonAdminInstallMode then
  2538. DesireAdminInstallMode := True
  2539. else if not ExistingAtAdminInstallMode and ExistingAtNonAdminInstallMode then
  2540. DesireAdminInstallMode := False
  2541. else if not InitSuppressMsgBoxes then begin
  2542. { Ask user. Doesn't log since logging hasn't started yet. Also doesn't
  2543. use ExpandedAppName since it isn't set yet. Afterwards we need to tell
  2544. any respawned Setup(Ldr) about the user choice (and avoid asking again).
  2545. Will use the command line parameter for this. Allowing proDialog forces
  2546. allowing proCommandLine, so we can count on the parameter to work. }
  2547. if shAppNameHasConsts in SetupHeader.Options then
  2548. AppName := PathChangeExt(PathExtractName(SetupLdrOriginalFilename), '')
  2549. else
  2550. AppName := SetupHeader.AppName;
  2551. if SetupHeader.PrivilegesRequired = prLowest then begin
  2552. case TaskDialogMsgBox('MAINICON', SetupMessages[msgPrivilegesRequiredOverrideInstruction],
  2553. FmtSetupMessage(msgPrivilegesRequiredOverrideText2, [AppName]),
  2554. SetupMessages[msgPrivilegesRequiredOverrideTitle], mbInformation, MB_YESNOCANCEL,
  2555. [SetupMessages[msgPrivilegesRequiredOverrideCurrentUserRecommended], SetupMessages[msgPrivilegesRequiredOverrideAllUsers]], IDNO) of
  2556. IDYES: DesireAdminInstallMode := False;
  2557. IDNO: DesireAdminInstallMode := True;
  2558. IDCANCEL: Abort;
  2559. end;
  2560. end else begin
  2561. case TaskDialogMsgBox('MAINICON', SetupMessages[msgPrivilegesRequiredOverrideInstruction],
  2562. FmtSetupMessage(msgPrivilegesRequiredOverrideText1, [AppName]),
  2563. SetupMessages[msgPrivilegesRequiredOverrideTitle], mbInformation, MB_YESNOCANCEL,
  2564. [SetupMessages[msgPrivilegesRequiredOverrideAllUsersRecommended], SetupMessages[msgPrivilegesRequiredOverrideCurrentUser]], IDYES) of
  2565. IDYES: DesireAdminInstallMode := True;
  2566. IDNO: DesireAdminInstallMode := False;
  2567. IDCANCEL: Abort;
  2568. end;
  2569. end;
  2570. end else
  2571. DesireAnInstallMode := False; { No previous found and msgboxes are suppressed, just keep things as they are. }
  2572. if DesireAnInstallMode then begin
  2573. if DesireAdminInstallMode then begin
  2574. SetupHeader.PrivilegesRequired := prAdmin;
  2575. ExtraRespawnParam := '/ALLUSERS';
  2576. end else begin
  2577. SetupHeader.PrivilegesRequired := prLowest;
  2578. ExtraRespawnParam := '/CURRENTUSER';
  2579. end;
  2580. end;
  2581. end;
  2582. end;
  2583. var
  2584. ParamName, ParamValue: String;
  2585. ParamIsAutomaticInternal: Boolean;
  2586. StartParam: Integer;
  2587. I, N: Integer;
  2588. IsRespawnedProcess, EnableLogging, WantToSuppressMsgBoxes, Res: Boolean;
  2589. DebugServerWnd: HWND;
  2590. LogFilename: String;
  2591. SetupFilename: String;
  2592. SetupFile: TFile;
  2593. TestID: TSetupID;
  2594. NameAndVersionMsg: String;
  2595. NextAllowedLevel: Integer;
  2596. LastShownComponentEntry, ComponentEntry: PSetupComponentEntry;
  2597. MinimumTypeSpace: Integer64;
  2598. SourceWildcard: String;
  2599. ExpandedSetupMutex, ExtraRespawnParam, RespawnParams: String;
  2600. begin
  2601. InitializeCommonVars;
  2602. { NewParamsForCode will hold all params except automatic internal ones like /SL5= and /DEBUGWND=
  2603. Also see Uninstall.ProcessCommandLine }
  2604. NewParamsForCode.Add(NewParamStr(0));
  2605. { Based on SetupLdr or not?
  2606. Parameters for launching SetupLdr-based installation are:
  2607. /SL5="<handle to SetupLdr's notify window>,<setup 0 data offset>,
  2608. <setup 1 data offset>,<original exe filename>"
  2609. }
  2610. SplitNewParamStr(1, ParamName, ParamValue);
  2611. if CompareText(ParamName, '/SL5=') = 0 then begin
  2612. StartParam := 2;
  2613. SetupLdrMode := True;
  2614. SetupNotifyWnd := ExtractLongWord(ParamValue);
  2615. SetupNotifyWndPresent := True;
  2616. SetupLdrOffset0 := ExtractLongWord(ParamValue);
  2617. SetupLdrOffset1 := ExtractLongWord(ParamValue);
  2618. SetupLdrOriginalFilename := ParamValue;
  2619. end
  2620. else begin
  2621. StartParam := 1;
  2622. SetupLdrOriginalFilename := NewParamStr(0);
  2623. end;
  2624. SourceDir := PathExtractDir(SetupLdrOriginalFilename);
  2625. IsRespawnedProcess := False;
  2626. EnableLogging := False;
  2627. WantToSuppressMsgBoxes := False;
  2628. DebugServerWnd := 0;
  2629. for I := StartParam to NewParamCount do begin
  2630. SplitNewParamStr(I, ParamName, ParamValue);
  2631. ParamIsAutomaticInternal := False;
  2632. if CompareText(ParamName, '/Log') = 0 then begin
  2633. EnableLogging := True;
  2634. LogFilename := '';
  2635. end else if CompareText(ParamName, '/Log=') = 0 then begin
  2636. EnableLogging := True;
  2637. LogFilename := ParamValue;
  2638. end else if CompareText(ParamName, '/Silent') = 0 then
  2639. InitSilent := True
  2640. else if CompareText(ParamName, '/VerySilent') = 0 then
  2641. InitVerySilent := True
  2642. else if CompareText(ParamName, '/NoRestart') = 0 then
  2643. InitNoRestart := True
  2644. else if CompareText(ParamName, '/CloseApplications') = 0 then
  2645. InitCloseApplications := True
  2646. else if CompareText(ParamName, '/NoCloseApplications') = 0 then
  2647. InitNoCloseApplications := True
  2648. else if CompareText(ParamName, '/ForceCloseApplications') = 0 then
  2649. InitForceCloseApplications := True
  2650. else if CompareText(ParamName, '/NoForceCloseApplications') = 0 then
  2651. InitNoForceCloseApplications := True
  2652. else if CompareText(ParamName, '/LogCloseApplications') = 0 then
  2653. InitLogCloseApplications := True
  2654. else if CompareText(ParamName, '/RestartApplications') = 0 then
  2655. InitRestartApplications := True
  2656. else if CompareText(ParamName, '/NoRestartApplications') = 0 then
  2657. InitNoRestartApplications := True
  2658. else if CompareText(ParamName, '/NoIcons') = 0 then
  2659. InitNoIcons := True
  2660. else if CompareText(ParamName, '/NoCancel') = 0 then
  2661. InitNoCancel := True
  2662. else if CompareText(ParamName, '/Lang=') = 0 then
  2663. InitLang := ParamValue
  2664. else if CompareText(ParamName, '/Type=') = 0 then
  2665. InitSetupType := ParamValue
  2666. else if CompareText(ParamName, '/Components=') = 0 then begin
  2667. InitComponentsSpecified := True;
  2668. SetStringsFromCommaString(InitComponents, SlashesToBackslashes(ParamValue));
  2669. end else if CompareText(ParamName, '/Tasks=') = 0 then begin
  2670. InitDeselectAllTasks := True;
  2671. SetStringsFromCommaString(InitTasks, SlashesToBackslashes(ParamValue));
  2672. end else if CompareText(ParamName, '/MergeTasks=') = 0 then begin
  2673. InitDeselectAllTasks := False;
  2674. SetStringsFromCommaString(InitTasks, SlashesToBackslashes(ParamValue));
  2675. end else if CompareText(ParamName, '/LoadInf=') = 0 then
  2676. InitLoadInf := PathExpand(ParamValue)
  2677. else if CompareText(ParamName, '/SaveInf=') = 0 then
  2678. InitSaveInf := PathExpand(ParamValue)
  2679. else if CompareText(ParamName, '/DIR=') = 0 then
  2680. InitDir := ParamValue
  2681. else if CompareText(ParamName, '/GROUP=') = 0 then
  2682. InitProgramGroup := ParamValue
  2683. else if CompareText(ParamName, '/Password=') = 0 then
  2684. InitPassword := ParamValue
  2685. else if CompareText(ParamName, '/RestartExitCode=') = 0 then
  2686. InitRestartExitCode := StrToIntDef(ParamValue, 0)
  2687. else if CompareText(ParamName, '/SuppressMsgBoxes') = 0 then
  2688. WantToSuppressMsgBoxes := True
  2689. else if CompareText(ParamName, '/DETACHEDMSG') = 0 then { for debugging }
  2690. DetachedUninstMsgFile := True
  2691. else if CompareText(ParamName, '/SPAWNWND=') = 0 then begin
  2692. ParamIsAutomaticInternal := True; { sent by RespawnSetupElevated }
  2693. IsRespawnedProcess := True;
  2694. InitializeSpawnClient(StrToInt(ParamValue));
  2695. end else if CompareText(ParamName, '/NOTIFYWND=') = 0 then begin
  2696. ParamIsAutomaticInternal := True; { sent by RespawnSetupElevated }
  2697. { /NOTIFYWND= takes precedence over any previously set SetupNotifyWnd }
  2698. SetupNotifyWnd := StrToInt(ParamValue);
  2699. SetupNotifyWndPresent := True;
  2700. end else if CompareText(ParamName, '/DebugSpawnServer') = 0 then { for debugging }
  2701. EnterSpawnServerDebugMode { does not return }
  2702. else if CompareText(ParamName, '/DEBUGWND=') = 0 then begin
  2703. ParamIsAutomaticInternal := True; { sent by IDE.MainForm's StartProcess }
  2704. DebugServerWnd := StrToInt(ParamValue);
  2705. end else if CompareText(ParamName, '/ALLUSERS') = 0 then begin
  2706. InitPrivilegesRequired := prAdmin;
  2707. HasInitPrivilegesRequired := True;
  2708. end else if CompareText(ParamName, '/CURRENTUSER') = 0 then begin
  2709. InitPrivilegesRequired := prLowest;
  2710. HasInitPrivilegesRequired := True;
  2711. end;
  2712. if not ParamIsAutomaticInternal then
  2713. NewParamsForCode.Add(NewParamStr(I));
  2714. end;
  2715. if InitLoadInf <> '' then
  2716. LoadInf(InitLoadInf, WantToSuppressMsgBoxes);
  2717. if WantToSuppressMsgBoxes and (InitSilent or InitVerySilent) then
  2718. InitSuppressMsgBoxes := True;
  2719. { Assign some default messages that may be used before the messages are read }
  2720. SetupMessages[msgSetupFileMissing] := SSetupFileMissing;
  2721. SetupMessages[msgSetupFileCorrupt] := SSetupFileCorrupt;
  2722. SetupMessages[msgSetupFileCorruptOrWrongVer] := SSetupFileCorruptOrWrongVer;
  2723. { Read setup-0.bin, or from EXE }
  2724. if not SetupLdrMode then begin
  2725. SetupFilename := PathChangeExt(SetupLdrOriginalFilename, '') + '-0.bin';
  2726. if not NewFileExists(SetupFilename) then
  2727. AbortInitFmt1(msgSetupFileMissing, PathExtractName(SetupFilename));
  2728. end
  2729. else
  2730. SetupFilename := SetupLdrOriginalFilename;
  2731. SetupFile := TFile.Create(SetupFilename, fdOpenExisting, faRead, fsRead);
  2732. try
  2733. SetupFile.Seek(SetupLdrOffset0);
  2734. if SetupFile.Read(TestID, SizeOf(TestID)) <> SizeOf(TestID) then
  2735. AbortInit(msgSetupFileCorruptOrWrongVer);
  2736. if TestID <> SetupID then
  2737. AbortInit(msgSetupFileCorruptOrWrongVer);
  2738. try
  2739. Reader := TCompressedBlockReader.Create(SetupFile, TLZMA1Decompressor);
  2740. try
  2741. { Header }
  2742. SECompressedBlockRead(Reader, SetupHeader, SizeOf(SetupHeader),
  2743. SetupHeaderStrings, SetupHeaderAnsiStrings);
  2744. { Language entries }
  2745. ReadEntriesWithoutVersion(seLanguage, SetupHeader.NumLanguageEntries,
  2746. SizeOf(TSetupLanguageEntry));
  2747. { CustomMessage entries }
  2748. ReadEntriesWithoutVersion(seCustomMessage, SetupHeader.NumCustomMessageEntries,
  2749. SizeOf(TSetupCustomMessageEntry));
  2750. { Permission entries }
  2751. ReadEntriesWithoutVersion(sePermission, SetupHeader.NumPermissionEntries,
  2752. SizeOf(TSetupPermissionEntry));
  2753. { Type entries }
  2754. ReadEntries(seType, SetupHeader.NumTypeEntries, SizeOf(TSetupTypeEntry),
  2755. Integer(@PSetupTypeEntry(nil).MinVersion),
  2756. Integer(@PSetupTypeEntry(nil).OnlyBelowVersion));
  2757. ActivateDefaultLanguage;
  2758. { Set Is64BitInstallMode if we're on Win64 and the processor architecture is
  2759. one on which a "64-bit mode" install should be performed. Doing this early
  2760. so that UsePreviousPrivileges knows where to look. Will log later. }
  2761. if (SetupHeader.ArchitecturesInstallIn64BitMode <> '') and
  2762. EvalExpression(SetupHeader.ArchitecturesInstallIn64BitMode, TDummyClass.EvalArchitectureIdentifier) then begin
  2763. if not IsWin64 then begin
  2764. { The script writer made a mistake: their expression matched a
  2765. 32-bit system. Obviously that can't be allowed.
  2766. With "not" there are lots of ways that could happen without
  2767. explicitly specifying a 32-bit architecture in the expression.
  2768. One example: "not win64" }
  2769. InternalError('ArchitecturesInstallIn64BitMode expression matched 32-bit system');
  2770. end;
  2771. Initialize64BitInstallMode(True);
  2772. end
  2773. else
  2774. Initialize64BitInstallMode(False);
  2775. HandlePrivilegesRequiredOverrides(ExtraRespawnParam);
  2776. { Start a new, elevated Setup(Ldr) process if needed }
  2777. if not IsRespawnedProcess and
  2778. NeedToRespawnSelfElevated(not (SetupHeader.PrivilegesRequired in [prNone, prLowest]),
  2779. SetupHeader.PrivilegesRequired <> prLowest) then begin
  2780. FreeAndNil(Reader);
  2781. FreeAndNil(SetupFile);
  2782. RespawnParams := GetCmdTailEx(StartParam);
  2783. if ExtraRespawnParam <> '' then
  2784. RespawnParams := RespawnParams + ' ' + ExtraRespawnParam;
  2785. RespawnSetupElevated(RespawnParams);
  2786. { Note: RespawnSetupElevated does not return; it either calls Halt
  2787. or raises an exception. }
  2788. end;
  2789. { Application.Handle is now known to be the main window. Set the shutdown block reason. }
  2790. ShutdownBlockReasonCreate(Application.Handle, SetupMessages[msgWizardInstalling]);
  2791. { Initialize debug client (client=Setup, server=debugger/IDE) }
  2792. if DebugServerWnd <> 0 then
  2793. SetDebugServerWnd(DebugServerWnd, False);
  2794. { Initialize logging }
  2795. if EnableLogging or (shSetupLogging in SetupHeader.Options) then begin
  2796. try
  2797. if LogFilename = '' then
  2798. StartLogging('Setup')
  2799. else
  2800. StartLoggingWithFixedFilename(LogFilename);
  2801. except
  2802. on E: Exception do begin
  2803. E.Message := 'Error creating log file:' + SNewLine2 + E.Message;
  2804. raise;
  2805. end;
  2806. end;
  2807. end;
  2808. Log('Setup version: ' + SetupTitle + ' version ' + SetupVersion);
  2809. Log('Original Setup EXE: ' + SetupLdrOriginalFilename);
  2810. Log('Setup command line: ' + GetCmdTail);
  2811. LogCompatibilityMode;
  2812. LogWindowsVersion;
  2813. NeedPassword := shPassword in SetupHeader.Options;
  2814. NeedSerial := False;
  2815. NeedsRestart := shAlwaysRestart in SetupHeader.Options;
  2816. { Component entries }
  2817. ReadEntries(seComponent, SetupHeader.NumComponentEntries, SizeOf(TSetupComponentEntry),
  2818. -1, -1);
  2819. { Task entries }
  2820. ReadEntries(seTask, SetupHeader.NumTaskEntries, SizeOf(TSetupTaskEntry),
  2821. -1, -1);
  2822. { Dir entries }
  2823. ReadEntries(seDir, SetupHeader.NumDirEntries, SizeOf(TSetupDirEntry),
  2824. Integer(@PSetupDirEntry(nil).MinVersion),
  2825. Integer(@PSetupDirEntry(nil).OnlyBelowVersion));
  2826. { File entries }
  2827. ReadEntries(seFile, SetupHeader.NumFileEntries, SizeOf(TSetupFileEntry),
  2828. Integer(@PSetupFileEntry(nil).MinVersion),
  2829. Integer(@PSetupFileEntry(nil).OnlyBelowVersion));
  2830. { Icon entries }
  2831. ReadEntries(seIcon, SetupHeader.NumIconEntries, SizeOf(TSetupIconEntry),
  2832. Integer(@PSetupIconEntry(nil).MinVersion),
  2833. Integer(@PSetupIconEntry(nil).OnlyBelowVersion));
  2834. { INI entries }
  2835. ReadEntries(seIni, SetupHeader.NumIniEntries, SizeOf(TSetupIniEntry),
  2836. Integer(@PSetupIniEntry(nil).MinVersion),
  2837. Integer(@PSetupIniEntry(nil).OnlyBelowVersion));
  2838. { Registry entries }
  2839. ReadEntries(seRegistry, SetupHeader.NumRegistryEntries, SizeOf(TSetupRegistryEntry),
  2840. Integer(@PSetupRegistryEntry(nil).MinVersion),
  2841. Integer(@PSetupRegistryEntry(nil).OnlyBelowVersion));
  2842. { InstallDelete entries }
  2843. ReadEntries(seInstallDelete, SetupHeader.NumInstallDeleteEntries, SizeOf(TSetupDeleteEntry),
  2844. Integer(@PSetupDeleteEntry(nil).MinVersion),
  2845. Integer(@PSetupDeleteEntry(nil).OnlyBelowVersion));
  2846. { UninstallDelete entries }
  2847. ReadEntries(seUninstallDelete, SetupHeader.NumUninstallDeleteEntries, SizeOf(TSetupDeleteEntry),
  2848. Integer(@PSetupDeleteEntry(nil).MinVersion),
  2849. Integer(@PSetupDeleteEntry(nil).OnlyBelowVersion));
  2850. { Run entries }
  2851. ReadEntries(seRun, SetupHeader.NumRunEntries, SizeOf(TSetupRunEntry),
  2852. Integer(@PSetupRunEntry(nil).MinVersion),
  2853. Integer(@PSetupRunEntry(nil).OnlyBelowVersion));
  2854. { UninstallRun entries }
  2855. ReadEntries(seUninstallRun, SetupHeader.NumUninstallRunEntries, SizeOf(TSetupRunEntry),
  2856. Integer(@PSetupRunEntry(nil).MinVersion),
  2857. Integer(@PSetupRunEntry(nil).OnlyBelowVersion));
  2858. { Wizard images }
  2859. Reader.Read(N, SizeOf(LongInt));
  2860. for I := 0 to N-1 do
  2861. WizardImages.Add(ReadWizardImage(Reader));
  2862. Reader.Read(N, SizeOf(LongInt));
  2863. for I := 0 to N-1 do
  2864. WizardSmallImages.Add(ReadWizardImage(Reader));
  2865. { Decompressor DLL }
  2866. DecompressorDLL := nil;
  2867. if SetupHeader.CompressMethod in [cmZip, cmBzip] then begin
  2868. DecompressorDLL := TMemoryStream.Create;
  2869. ReadFileIntoStream(DecompressorDLL, Reader);
  2870. end;
  2871. finally
  2872. Reader.Free;
  2873. end;
  2874. Reader := TCompressedBlockReader.Create(SetupFile, TLZMA1Decompressor);
  2875. try
  2876. { File location entries }
  2877. ReadEntriesWithoutVersion(seFileLocation, SetupHeader.NumFileLocationEntries,
  2878. SizeOf(TSetupFileLocationEntry));
  2879. finally
  2880. Reader.Free;
  2881. end;
  2882. except
  2883. on ECompressDataError do
  2884. AbortInit(msgSetupFileCorrupt);
  2885. end;
  2886. finally
  2887. SetupFile.Free;
  2888. end;
  2889. InitializeAdminInstallMode(IsAdmin and (SetupHeader.PrivilegesRequired <> prLowest));
  2890. Log64BitInstallMode;
  2891. { Show "Select Language" dialog if necessary - requires "64-bit mode" to be
  2892. initialized else it might query the previous language from the wrong registry
  2893. view }
  2894. if Entries[seLanguage].Count > 1 then begin
  2895. if ShowLanguageDialog and not InitSilent and not InitVerySilent then begin
  2896. if not AskForLanguage then
  2897. Abort;
  2898. end else if not MatchedLangParameter and (shUsePreviousLanguage in SetupHeader.Options) then begin
  2899. { Replicate the dialog's UsePreviousLanguage functionality. }
  2900. { Note: if UsePreviousLanguage is set to "yes" then the compiler does not
  2901. allow AppId to include constants but we should still call ExpandConst
  2902. to handle any '{{'. }
  2903. I := GetPreviousLanguage(ExpandConst(SetupHeader.AppId));
  2904. if I <> -1 then
  2905. SetActiveLanguage(I);
  2906. end;
  2907. end;
  2908. { Check unsupported Itanium - must be on Windows Server 2008 R2 so remove once
  2909. this becomes unsupported as well and Windows 8 (6.2+) becomes the new minimum }
  2910. var SysInfo: TSystemInfo;
  2911. GetNativeSystemInfo(SysInfo);
  2912. if SysInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64 then
  2913. AbortInit(msgWindowsVersionNotSupported);
  2914. { Check allowed processor architectures }
  2915. if (SetupHeader.ArchitecturesAllowed <> '') and
  2916. not EvalExpression(SetupHeader.ArchitecturesAllowed, TDummyClass.EvalArchitectureIdentifier) then
  2917. AbortInit(msgWindowsVersionNotSupported);
  2918. { Check Windows version }
  2919. case InstallOnThisVersion(SetupHeader.MinVersion, SetupHeader.OnlyBelowVersion) of
  2920. irInstall: ;
  2921. irServicePackTooLow:
  2922. AbortInitServicePackRequired(SetupHeader.MinVersion.NTServicePack);
  2923. else
  2924. AbortInit(msgWindowsVersionNotSupported);
  2925. end;
  2926. { Check if the user lacks the required privileges }
  2927. case SetupHeader.PrivilegesRequired of
  2928. prPowerUser:
  2929. if not IsPowerUserOrAdmin then AbortInit(msgPowerUserPrivilegesRequired);
  2930. prAdmin:
  2931. if not IsAdmin then AbortInit(msgAdminPrivilegesRequired);
  2932. end;
  2933. { Init main constants, not depending on shfolder.dll/_shfoldr.dll }
  2934. InitMainNonSHFolderConsts;
  2935. { Create temporary directory and extract 64-bit helper EXE if necessary }
  2936. CreateTempInstallDirAndExtract64BitHelper;
  2937. { Load system's "shfolder.dll" or extract "_shfoldr.dll" to TempInstallDir, and load it }
  2938. LoadSHFolderDLL;
  2939. { Extract "_isdecmp.dll" to TempInstallDir, and load it }
  2940. if SetupHeader.CompressMethod in [cmZip, cmBzip] then
  2941. LoadDecompressorDLL;
  2942. { Start RestartManager session }
  2943. if InitCloseApplications or
  2944. ((shCloseApplications in SetupHeader.Options) and not InitNoCloseApplications) then begin
  2945. InitRestartManagerLibrary;
  2946. { Note from Old New Thing: "The RmStartSession function doesn't properly
  2947. null-terminate the session key <...>. To work around this bug, we pre-fill
  2948. the buffer with null characters <...>." Our key is pre-filled too since
  2949. it's global. }
  2950. if UseRestartManager and (RmStartSession(@RmSessionHandle, 0, RmSessionKey) = ERROR_SUCCESS) then begin
  2951. RmSessionStarted := True;
  2952. SetStringsFromCommaString(CloseApplicationsFilterList, SetupHeader.CloseApplicationsFilter);
  2953. SetStringsFromCommaString(CloseApplicationsFilterExcludesList, SetupHeader.CloseApplicationsFilterExcludes);
  2954. end;
  2955. end;
  2956. { Set install mode }
  2957. SetupInstallMode;
  2958. { Load and initialize code }
  2959. if SetupHeader.CompiledCodeText <> '' then begin
  2960. CodeRunner := TScriptRunner.Create();
  2961. try
  2962. CodeRunner.NamingAttribute := CodeRunnerNamingAttribute;
  2963. CodeRunner.OnLog := CodeRunnerOnLog;
  2964. CodeRunner.OnLogFmt := CodeRunnerOnLogFmt;
  2965. CodeRunner.OnDllImport := CodeRunnerOnDllImport;
  2966. CodeRunner.OnDebug := CodeRunnerOnDebug;
  2967. CodeRunner.OnDebugIntermediate := CodeRunnerOnDebugIntermediate;
  2968. CodeRunner.OnException := CodeRunnerOnException;
  2969. CodeRunner.LoadScript(SetupHeader.CompiledCodeText, DebugClientCompiledCodeDebugInfo);
  2970. if not NeedPassword then
  2971. NeedPassword := CodeRunner.FunctionExists('CheckPassword', True);
  2972. NeedPassword := HandleInitPassword(NeedPassword);
  2973. if not NeedSerial then
  2974. NeedSerial := CodeRunner.FunctionExists('CheckSerial', True);
  2975. except
  2976. { Don't let DeinitSetup see a partially-initialized CodeRunner }
  2977. FreeAndNil(CodeRunner);
  2978. raise;
  2979. end;
  2980. try
  2981. Res := CodeRunner.RunBooleanFunctions('InitializeSetup', [''], bcFalse, False, True);
  2982. except
  2983. Log('InitializeSetup raised an exception (fatal).');
  2984. raise;
  2985. end;
  2986. if not Res then begin
  2987. Log('InitializeSetup returned False; aborting.');
  2988. Abort;
  2989. end;
  2990. end
  2991. else
  2992. NeedPassword := HandleInitPassword(NeedPassword);
  2993. { Expand AppName, AppVerName, and AppCopyright now since they're used often,
  2994. especially by the background window painting. }
  2995. ExpandedAppName := ExpandConst(SetupHeader.AppName);
  2996. if SetupHeader.AppVerName <> '' then
  2997. ExpandedAppVerName := ExpandConst(SetupHeader.AppVerName)
  2998. else begin
  2999. if not GetCustomMessageValue('NameAndVersion', NameAndVersionMsg) then
  3000. NameAndVersionMsg := '%1 %2'; { just in case }
  3001. ExpandedAppVerName := FmtMessage(PChar(NameAndVersionMsg),
  3002. [ExpandedAppName, ExpandConst(SetupHeader.AppVersion)]);
  3003. end;
  3004. ExpandedAppCopyright := ExpandConst(SetupHeader.AppCopyright);
  3005. ExpandedAppMutex := ExpandConst(SetupHeader.AppMutex);
  3006. ExpandedSetupMutex := ExpandConst(SetupHeader.SetupMutex);
  3007. { Update the shutdown block reason now that we have ExpandedAppName. }
  3008. ShutdownBlockReasonCreate(Application.Handle,
  3009. FmtSetupMessage1(msgShutdownBlockReasonInstallingApp, ExpandedAppName));
  3010. { Check if app is running }
  3011. while CheckForMutexes(ExpandedAppMutex) do
  3012. if LoggedMsgBox(FmtSetupMessage1(msgSetupAppRunningError, ExpandedAppName),
  3013. SetupMessages[msgSetupAppTitle], mbError, MB_OKCANCEL, True, IDCANCEL) <> IDOK then
  3014. Abort;
  3015. { Check if Setup is running and if not create mutexes }
  3016. while CheckForMutexes(ExpandedSetupMutex) do
  3017. if LoggedMsgBox(FmtSetupMessage1(msgSetupAppRunningError, SetupMessages[msgSetupAppTitle]),
  3018. SetupMessages[msgSetupAppTitle], mbError, MB_OKCANCEL, True, IDCANCEL) <> IDOK then
  3019. Abort;
  3020. CreateMutexes(ExpandedSetupMutex);
  3021. { Remove types that fail their 'languages' or 'check'. Can't do this earlier
  3022. because the InitializeSetup call above can't be done earlier. }
  3023. for I := 0 to Entries[seType].Count-1 do begin
  3024. if not ShouldProcessEntry(nil, nil, '', '', PSetupTypeEntry(Entries[seType][I]).Languages, PSetupTypeEntry(Entries[seType][I]).Check) then begin
  3025. SEFreeRec(Entries[seType][I], EntryStrings[seType], EntryAnsiStrings[seType]);
  3026. { Don't delete it yet so that the entries can be processed sequentially }
  3027. Entries[seType][I] := nil;
  3028. end;
  3029. end;
  3030. { Delete the nil-ed items now }
  3031. Entries[seType].Pack();
  3032. { Remove components }
  3033. NextAllowedLevel := 0;
  3034. LastShownComponentEntry := nil;
  3035. for I := 0 to Entries[seComponent].Count-1 do begin
  3036. ComponentEntry := PSetupComponentEntry(Entries[seComponent][I]);
  3037. if (ComponentEntry.Level <= NextAllowedLevel) and
  3038. (InstallOnThisVersion(ComponentEntry.MinVersion, ComponentEntry.OnlyBelowVersion) = irInstall) and
  3039. ShouldProcessEntry(nil, nil, '', '', ComponentEntry.Languages, ComponentEntry.Check) then begin
  3040. NextAllowedLevel := ComponentEntry.Level + 1;
  3041. LastShownComponentEntry := ComponentEntry;
  3042. end
  3043. else begin
  3044. { Not showing }
  3045. if Assigned(LastShownComponentEntry) and
  3046. (ComponentEntry.Level = LastShownComponentEntry.Level) and
  3047. (CompareText(ComponentEntry.Name, LastShownComponentEntry.Name) = 0) then begin
  3048. { It's a duplicate of the last shown item. Leave NextAllowedLevel
  3049. alone, so that any child items that follow can attach to the last
  3050. shown item. }
  3051. end
  3052. else begin
  3053. { Not a duplicate of the last shown item, so the next item must be
  3054. at the same level or less }
  3055. if NextAllowedLevel > ComponentEntry.Level then
  3056. NextAllowedLevel := ComponentEntry.Level;
  3057. { Clear LastShownComponentEntry so that no subsequent item can be
  3058. considered a duplicate of it. Needed in this case:
  3059. foo (shown)
  3060. foo\childA (not shown)
  3061. foo (not shown)
  3062. foo\childB
  3063. "foo\childB" should be hidden, not made a child of "foo" #1. }
  3064. LastShownComponentEntry := nil;
  3065. end;
  3066. Entries[seComponent][I] := nil;
  3067. SEFreeRec(ComponentEntry, EntryStrings[seComponent], EntryAnsiStrings[seComponent]);
  3068. end;
  3069. end;
  3070. Entries[seComponent].Pack();
  3071. { Set misc. variables }
  3072. HasCustomType := False;
  3073. for I := 0 to Entries[seType].Count-1 do begin
  3074. if toIsCustom in PSetupTypeEntry(Entries[seType][I]).Options then begin
  3075. HasCustomType := True;
  3076. Break;
  3077. end;
  3078. end;
  3079. HasComponents := Entries[seComponent].Count <> 0;
  3080. HasIcons := Entries[seIcon].Count <> 0;
  3081. HasTasks := Entries[seTask].Count <> 0;
  3082. { Calculate minimum disk space. If there are setup types, find the smallest
  3083. type and add the size of all files that don't belong to any component. Otherwise
  3084. calculate minimum disk space by adding all of the file's sizes. Also for each
  3085. "external" file, check the file size now, and store it the ExternalSize field
  3086. of the TSetupFileEntry record, except if an ExternalSize was specified by the
  3087. script. }
  3088. MinimumSpace := SetupHeader.ExtraDiskSpaceRequired;
  3089. for I := 0 to Entries[seFile].Count-1 do begin
  3090. with PSetupFileEntry(Entries[seFile][I])^ do begin
  3091. if LocationEntry <> -1 then begin { not an "external" file }
  3092. if Components = '' then { no types or a file that doesn't belong to any component }
  3093. if (Tasks = '') and (Check = '') then {don't count tasks and scripted entries}
  3094. Inc6464(MinimumSpace, PSetupFileLocationEntry(Entries[seFileLocation][LocationEntry])^.OriginalSize)
  3095. end else begin
  3096. if not(foExternalSizePreset in Options) then begin
  3097. try
  3098. if FileType <> ftUserFile then
  3099. SourceWildcard := NewParamStr(0)
  3100. else
  3101. SourceWildcard := ExpandConst(SourceFilename);
  3102. ExternalSize := RecurseExternalGetSizeOfFiles(
  3103. ShouldDisableFsRedirForFileEntry(PSetupFileEntry(Entries[seFile][I])),
  3104. PathExtractPath(SourceWildcard),
  3105. '', PathExtractName(SourceWildcard), IsWildcard(SourceWildcard),
  3106. foRecurseSubDirsExternal in Options);
  3107. except
  3108. { Ignore exceptions. One notable exception we want to ignore is
  3109. the one about "app" not being initialized. }
  3110. end;
  3111. end;
  3112. if Components = '' then { no types or a file that doesn't belong to any component }
  3113. if (Tasks = '') and (Check = '') then {don't count tasks or scripted entries}
  3114. Inc6464(MinimumSpace, ExternalSize);
  3115. end;
  3116. end;
  3117. end;
  3118. for I := 0 to Entries[seComponent].Count-1 do
  3119. with PSetupComponentEntry(Entries[seComponent][I])^ do
  3120. Size := GetSizeOfComponent(Name, ExtraDiskSpaceRequired);
  3121. if Entries[seType].Count > 0 then begin
  3122. for I := 0 to Entries[seType].Count-1 do begin
  3123. with PSetupTypeEntry(Entries[seType][I])^ do begin
  3124. Size := GetSizeOfType(Name, toIsCustom in Options);
  3125. if (I = 0) or (Compare64(Size, MinimumTypeSpace) < 0) then
  3126. MinimumTypeSpace := Size;
  3127. end;
  3128. end;
  3129. Inc6464(MinimumSpace, MinimumTypeSpace);
  3130. end;
  3131. end;
  3132. procedure InitializeWizard;
  3133. begin
  3134. WizardForm := AppCreateForm(TWizardForm) as TWizardForm;
  3135. if CodeRunner <> nil then begin
  3136. try
  3137. CodeRunner.RunProcedures('InitializeWizard', [''], False);
  3138. except
  3139. Log('InitializeWizard raised an exception (fatal).');
  3140. raise;
  3141. end;
  3142. end;
  3143. WizardForm.FlipSizeAndCenterIfNeeded(False, nil, False);
  3144. WizardForm.SetCurPage(wpWelcome);
  3145. if InstallMode = imNormal then begin
  3146. WizardForm.ClickToStartPage; { this won't go past wpReady }
  3147. WizardForm.Visible := True;
  3148. end
  3149. else
  3150. WizardForm.ClickThroughPages;
  3151. end;
  3152. procedure DeinitSetup(const AllowCustomSetupExitCode: Boolean);
  3153. var
  3154. I: Integer;
  3155. begin
  3156. Log('Deinitializing Setup.');
  3157. if Assigned(CodeRunner) then begin
  3158. if AllowCustomSetupExitCode then begin
  3159. try
  3160. SetupExitCode := CodeRunner.RunIntegerFunctions('GetCustomSetupExitCode',
  3161. [''], bcNonZero, False, SetupExitCode);
  3162. except
  3163. Log('GetCustomSetupExitCode raised an exception.');
  3164. Application.HandleException(nil);
  3165. end;
  3166. end;
  3167. try
  3168. CodeRunner.RunProcedures('DeinitializeSetup', [''], False);
  3169. except
  3170. Log('DeinitializeSetup raised an exception.');
  3171. Application.HandleException(nil);
  3172. end;
  3173. FreeAndNil(CodeRunner);
  3174. end;
  3175. for I := 0 to DeleteFilesAfterInstallList.Count-1 do
  3176. DeleteFileRedir(DeleteFilesAfterInstallList.Objects[I] <> nil,
  3177. DeleteFilesAfterInstallList[I]);
  3178. DeleteFilesAfterInstallList.Clear;
  3179. for I := DeleteDirsAfterInstallList.Count-1 downto 0 do
  3180. RemoveDirectoryRedir(DeleteDirsAfterInstallList.Objects[I] <> nil,
  3181. DeleteDirsAfterInstallList[I]);
  3182. DeleteDirsAfterInstallList.Clear;
  3183. FreeFileExtractor;
  3184. { End RestartManager session }
  3185. if RmSessionStarted then
  3186. RmEndSession(RmSessionHandle);
  3187. { Free the _isdecmp.dll handle }
  3188. if DecompressorDLLHandle <> 0 then
  3189. FreeLibrary(DecompressorDLLHandle);
  3190. { Free the shfolder.dll handles }
  3191. UnloadSHFolderDLL;
  3192. { Remove TempInstallDir, stopping the 64-bit helper first if necessary }
  3193. RemoveTempInstallDir;
  3194. { An attempt to restart while debugging is most likely an accident;
  3195. don't allow it }
  3196. if RestartSystem and Debugging then begin
  3197. Log('Not restarting Windows because Setup is being run from the debugger.');
  3198. RestartSystem := False;
  3199. end;
  3200. EndDebug;
  3201. ShutdownBlockReasonDestroy(Application.Handle);
  3202. if RestartSystem then begin
  3203. Log('Restarting Windows.');
  3204. if SetupNotifyWndPresent then begin
  3205. { Send a special message back to the first instance telling it to
  3206. restart the system after Setup returns }
  3207. SendNotifyMessage(SetupNotifyWnd, WM_USER + 150, 10000, 0);
  3208. end
  3209. else begin
  3210. { There is no other instance, so initiate the restart ourself.
  3211. Note: Depending on the OS, this may not return if successful. }
  3212. RestartComputerFromThisProcess;
  3213. end;
  3214. end;
  3215. end;
  3216. function ExitSetupMsgBox: Boolean;
  3217. begin
  3218. Result := LoggedMsgBox(SetupMessages[msgExitSetupMessage], SetupMessages[msgExitSetupTitle],
  3219. mbConfirmation, MB_YESNO or MB_DEFBUTTON2, False, 0) = IDYES;
  3220. end;
  3221. procedure ProcessMessagesProc; far;
  3222. begin
  3223. Application.ProcessMessages;
  3224. end;
  3225. procedure RunExecLog(const S: String; const Error, FirstLine: Boolean; const Data: NativeInt);
  3226. begin
  3227. if not Error and FirstLine then
  3228. Log('Output:');
  3229. Log(S);
  3230. end;
  3231. function ShouldDisableFsRedirForRunEntry(const RunEntry: PSetupRunEntry): Boolean;
  3232. begin
  3233. Result := InstallDefaultDisableFsRedir;
  3234. if roRun32Bit in RunEntry.Options then
  3235. Result := False;
  3236. if roRun64Bit in RunEntry.Options then begin
  3237. if not IsWin64 then
  3238. InternalError('Cannot run files in 64-bit locations on this version of Windows');
  3239. Result := True;
  3240. end;
  3241. end;
  3242. procedure ProcessRunEntry(const RunEntry: PSetupRunEntry);
  3243. var
  3244. RunAsOriginalUser: Boolean;
  3245. ExpandedFilename, ExpandedParameters: String;
  3246. Wait: TExecWait;
  3247. DisableFsRedir: Boolean;
  3248. ErrorCode: Integer;
  3249. begin
  3250. try
  3251. Log('-- Run entry --');
  3252. RunAsOriginalUser := (roRunAsOriginalUser in RunEntry.Options);
  3253. if RunAsOriginalUser then
  3254. Log('Run as: Original user')
  3255. else
  3256. Log('Run as: Current user');
  3257. if not(roShellExec in RunEntry.Options) then
  3258. Log('Type: Exec')
  3259. else
  3260. Log('Type: ShellExec');
  3261. ExpandedFilename := ExpandConst(RunEntry.Name);
  3262. Log('Filename: ' + ExpandedFilename);
  3263. ExpandedParameters := ExpandConst(RunEntry.Parameters);
  3264. if not(roDontLogParameters in RunEntry.Options) and (ExpandedParameters <> '') then
  3265. Log('Parameters: ' + ExpandedParameters);
  3266. Wait := ewWaitUntilTerminated;
  3267. case RunEntry.Wait of
  3268. rwNoWait: Wait := ewNoWait;
  3269. rwWaitUntilIdle: Wait := ewWaitUntilIdle;
  3270. end;
  3271. if not(roShellExec in RunEntry.Options) then begin
  3272. DisableFsRedir := ShouldDisableFsRedirForRunEntry(RunEntry);
  3273. if not(roSkipIfDoesntExist in RunEntry.Options) or
  3274. NewFileExistsRedir(DisableFsRedir, ExpandedFilename) then begin
  3275. var OutputReader: TCreateProcessOutputReader := nil;
  3276. try
  3277. if GetLogActive and (roLogOutput in RunEntry.Options) then
  3278. OutputReader := TCreateProcessOutputReader.Create(RunExecLog, 0);
  3279. if not InstExecEx(RunAsOriginalUser, DisableFsRedir, ExpandedFilename,
  3280. ExpandedParameters, ExpandConst(RunEntry.WorkingDir),
  3281. Wait, RunEntry.ShowCmd, ProcessMessagesProc, OutputReader, ErrorCode) then
  3282. raise Exception.Create(FmtSetupMessage1(msgErrorExecutingProgram, ExpandedFilename) +
  3283. SNewLine2 + FmtSetupMessage(msgErrorFunctionFailedWithMessage,
  3284. ['CreateProcess', IntToStr(ErrorCode), Win32ErrorString(ErrorCode)]));
  3285. if Wait = ewWaitUntilTerminated then
  3286. Log(Format('Process exit code: %u', [ErrorCode]));
  3287. finally
  3288. OutputReader.Free;
  3289. end;
  3290. end
  3291. else
  3292. Log('File doesn''t exist. Skipping.');
  3293. end
  3294. else begin
  3295. if not(roSkipIfDoesntExist in RunEntry.Options) or FileOrDirExists(ExpandedFilename) then begin
  3296. if not InstShellExecEx(RunAsOriginalUser, ExpandConst(RunEntry.Verb),
  3297. ExpandedFilename, ExpandedParameters, ExpandConst(RunEntry.WorkingDir),
  3298. Wait, RunEntry.ShowCmd, ProcessMessagesProc, ErrorCode) then
  3299. raise Exception.Create(FmtSetupMessage1(msgErrorExecutingProgram, ExpandedFilename) +
  3300. SNewLine2 + FmtSetupMessage(msgErrorFunctionFailedWithMessage,
  3301. ['ShellExecuteEx', IntToStr(ErrorCode), Win32ErrorString(ErrorCode)]));
  3302. end
  3303. else
  3304. Log('File/directory doesn''t exist. Skipping.');
  3305. end;
  3306. except
  3307. Application.HandleException(nil);
  3308. end;
  3309. end;
  3310. procedure ShellExecuteAsOriginalUser(hWnd: HWND; Operation, FileName, Parameters, Directory: LPWSTR; ShowCmd: Integer); stdcall;
  3311. var
  3312. ErrorCode: Integer;
  3313. begin
  3314. InstShellExecEx(True, Operation, Filename, Parameters, Directory, ewNoWait, ShowCmd, ProcessMessagesProc, ErrorCode);
  3315. end;
  3316. procedure InitIsWin64AndProcessorArchitectureAndMachineTypesSupportedBySystem;
  3317. const
  3318. PROCESSOR_ARCHITECTURE_ARM64 = 12;
  3319. IMAGE_FILE_MACHINE_ARM64 = $AA64;
  3320. IMAGE_FILE_MACHINE_ARMNT = $01C4;
  3321. UserEnabled = $1;
  3322. var
  3323. KernelModule: HMODULE;
  3324. IsWow64ProcessFunc: function(hProcess: THandle; var Wow64Process: BOOL): BOOL; stdcall;
  3325. IsWow64Process2Func: function(hProcess: THandle; var pProcessMachine, pNativeMachine: USHORT): BOOL; stdcall;
  3326. GetMachineTypeAttributesFunc: function(Machine: USHORT; var MachineTypeAttributes: Integer): HRESULT; stdcall;
  3327. IsWow64GuestMachineSupportedFunc: function(WowGuestMachine: USHORT; var MachineIsSupported: BOOL): HRESULT; stdcall;
  3328. ProcessMachine, NativeMachine: USHORT;
  3329. Wow64Process: BOOL;
  3330. SysInfo: TSystemInfo;
  3331. begin
  3332. KernelModule := GetModuleHandle(kernel32);
  3333. { The system is considered a "Win64" system if all of the following
  3334. conditions are true:
  3335. 1. One of the following two is true:
  3336. a. IsWow64Process2 is available, and returns True for the current process.
  3337. b. IsWow64Process is available, and returns True for the current process.
  3338. 2. Wow64DisableWow64FsRedirection is available.
  3339. 3. Wow64RevertWow64FsRedirection is available.
  3340. 4. GetSystemWow64DirectoryA is available.
  3341. 5. RegDeleteKeyExA is available.
  3342. The system does not have to be one of the known 64-bit architectures
  3343. to be considered a "Win64" system. }
  3344. IsWin64 := False;
  3345. IsWow64Process2Func := GetProcAddress(KernelModule, 'IsWow64Process2');
  3346. if Assigned(IsWow64Process2Func) and
  3347. IsWow64Process2Func(GetCurrentProcess, ProcessMachine, NativeMachine) and
  3348. (ProcessMachine <> IMAGE_FILE_MACHINE_UNKNOWN) then begin
  3349. IsWin64 := True;
  3350. case NativeMachine of
  3351. IMAGE_FILE_MACHINE_I386: ProcessorArchitecture := paX86;
  3352. IMAGE_FILE_MACHINE_AMD64: ProcessorArchitecture := paX64;
  3353. IMAGE_FILE_MACHINE_ARM64: ProcessorArchitecture := paArm64;
  3354. else
  3355. ProcessorArchitecture := paUnknown;
  3356. end;
  3357. end else begin
  3358. IsWow64ProcessFunc := GetProcAddress(KernelModule, 'IsWow64Process');
  3359. if Assigned(IsWow64ProcessFunc) and
  3360. IsWow64ProcessFunc(GetCurrentProcess, Wow64Process) and
  3361. Wow64Process then
  3362. IsWin64 := True;
  3363. GetNativeSystemInfo(SysInfo);
  3364. case SysInfo.wProcessorArchitecture of
  3365. PROCESSOR_ARCHITECTURE_INTEL: ProcessorArchitecture := paX86;
  3366. PROCESSOR_ARCHITECTURE_AMD64: ProcessorArchitecture := paX64;
  3367. PROCESSOR_ARCHITECTURE_ARM64: ProcessorArchitecture := paArm64;
  3368. else
  3369. ProcessorArchitecture := paUnknown;
  3370. end;
  3371. end;
  3372. if IsWin64 and
  3373. not (AreFsRedirectionFunctionsAvailable and
  3374. (GetProcAddress(KernelModule, 'GetSystemWow64DirectoryA') <> nil) and
  3375. (GetProcAddress(GetModuleHandle(advapi32), 'RegDeleteKeyExA') <> nil)) then
  3376. IsWin64 := False;
  3377. { Setup MachineTypesSupportedBySystem. The result should end up being:
  3378. - 32-bit x86: [paX86]
  3379. - x64: [paX86, paX64]
  3380. (but not paX86 in a future x64 build of Inno Setup if Windows was installed
  3381. without support for x86 binaries (which is possible with Windows Server))
  3382. - Arm64 Windows 10: [paX86, paArm64, paArm32]
  3383. (Arm32 support detected, not just assumed)
  3384. - Arm64 Windows 11: [paX86, paX64, paArm64, paArm32]
  3385. (X64 and Arm32 support detected, not just assumed) }
  3386. {$IFDEF CPUX86}
  3387. MachineTypesSupportedBySystem := [paX86];
  3388. {$ELSE}
  3389. {$MESSAGE ERROR 'This needs updating for non-x86 builds'}
  3390. {$ENDIF}
  3391. if ProcessorArchitecture <> paUnknown then
  3392. Include(MachineTypesSupportedBySystem, ProcessorArchitecture);
  3393. { On Windows 11 we can use GetMachineTypeAttributes to check what is supported extra }
  3394. GetMachineTypeAttributesFunc := GetProcAddress(KernelModule, 'GetMachineTypeAttributes');
  3395. if Assigned(GetMachineTypeAttributesFunc) then begin
  3396. var MachineTypeAttributes: Integer;
  3397. if (GetMachineTypeAttributesFunc(IMAGE_FILE_MACHINE_ARMNT, MachineTypeAttributes) = S_OK) and
  3398. ((MachineTypeAttributes and UserEnabled) <> 0) then
  3399. Include(MachineTypesSupportedBySystem, paArm32);
  3400. if not (paX64 in MachineTypesSupportedBySystem) and
  3401. (GetMachineTypeAttributesFunc(IMAGE_FILE_MACHINE_AMD64, MachineTypeAttributes) = S_OK) and
  3402. ((MachineTypeAttributes and UserEnabled) <> 0) then
  3403. Include(MachineTypesSupportedBySystem, paX64);
  3404. end else begin
  3405. { Without GetMachineTypeAttributes we can only check if Arm32 is supported extra
  3406. using IsWow64GuestMachineSupported }
  3407. IsWow64GuestMachineSupportedFunc := GetProcAddress(KernelModule, 'IsWow64GuestMachineSupported');
  3408. if Assigned(IsWow64GuestMachineSupportedFunc) then begin
  3409. var MachineIsSupported: BOOL;
  3410. if (IsWow64GuestMachineSupportedFunc(IMAGE_FILE_MACHINE_ARMNT, MachineIsSupported) = S_OK) and
  3411. MachineIsSupported then
  3412. Include(MachineTypesSupportedBySystem, paArm32);
  3413. end;
  3414. end;
  3415. end;
  3416. procedure InitWindowsVersion;
  3417. var
  3418. OSVersionInfo: TOSVersionInfo;
  3419. OSVersionInfoEx: TOSVersionInfoEx;
  3420. begin
  3421. OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
  3422. if GetVersionEx(OSVersionInfo) then begin
  3423. WindowsVersion := (Byte(OSVersionInfo.dwMajorVersion) shl 24) or
  3424. (Byte(OSVersionInfo.dwMinorVersion) shl 16) or
  3425. Word(OSVersionInfo.dwBuildNumber);
  3426. { ^ Note: We MUST clip dwBuildNumber to 16 bits for Win9x compatibility }
  3427. OSVersionInfoEx.dwOSVersionInfoSize := SizeOf(OSVersionInfoEx);
  3428. if GetVersionEx(POSVersionInfo(@OSVersionInfoEx)^) then begin
  3429. NTServicePackLevel := (Byte(OSVersionInfoEx.wServicePackMajor) shl 8) or
  3430. Byte(OSVersionInfoEx.wServicePackMinor);
  3431. WindowsProductType := OSVersionInfoEx.wProductType;
  3432. WindowsSuiteMask := OSVersionInfoEx.wSuiteMask;
  3433. end;
  3434. end;
  3435. end;
  3436. procedure CreateEntryLists;
  3437. var
  3438. I: TEntryType;
  3439. begin
  3440. for I := Low(I) to High(I) do
  3441. Entries[I] := TList.Create;
  3442. end;
  3443. procedure FreeEntryLists;
  3444. var
  3445. I: TEntryType;
  3446. List: TList;
  3447. J: Integer;
  3448. P: Pointer;
  3449. begin
  3450. for I := High(I) downto Low(I) do begin
  3451. List := Entries[I];
  3452. if Assigned(List) then begin
  3453. Entries[I] := nil;
  3454. for J := List.Count-1 downto 0 do begin
  3455. P := List[J];
  3456. if EntryStrings[I] <> 0 then
  3457. SEFreeRec(P, EntryStrings[I], EntryAnsiStrings[I])
  3458. else
  3459. FreeMem(P);
  3460. end;
  3461. List.Free;
  3462. end;
  3463. FreeAndNil(OriginalEntryIndexes[I]);
  3464. end;
  3465. end;
  3466. procedure FreeWizardImages;
  3467. var
  3468. I: Integer;
  3469. begin
  3470. for I := WizardImages.Count-1 downto 0 do
  3471. TBitmap(WizardImages[I]).Free;
  3472. FreeAndNil(WizardImages);
  3473. for I := WizardSmallImages.Count-1 downto 0 do
  3474. TBitmap(WizardSmallImages[I]).Free;
  3475. FreeAndNil(WizardSmallImages);
  3476. end;
  3477. initialization
  3478. InitIsWin64AndProcessorArchitectureAndMachineTypesSupportedBySystem;
  3479. InitWindowsVersion;
  3480. InitComponents := TStringList.Create();
  3481. InitTasks := TStringList.Create();
  3482. NewParamsForCode := TStringList.Create();
  3483. WizardComponents := TStringList.Create();
  3484. WizardDeselectedComponents := TStringList.Create();
  3485. WizardTasks := TStringList.Create();
  3486. WizardDeselectedTasks := TStringList.Create();
  3487. CreateEntryLists;
  3488. DeleteFilesAfterInstallList := TStringList.Create;
  3489. DeleteDirsAfterInstallList := TStringList.Create;
  3490. CloseApplicationsFilterList := TStringList.Create;
  3491. CloseApplicationsFilterExcludesList := TStringList.Create;
  3492. WizardImages := TList.Create;
  3493. WizardSmallImages := TList.Create;
  3494. SHGetKnownFolderPathFunc := GetProcAddress(SafeLoadLibrary(AddBackslash(GetSystemDir) + shell32,
  3495. SEM_NOOPENFILEERRORBOX), 'SHGetKnownFolderPath');
  3496. finalization
  3497. FreeWizardImages;
  3498. FreeAndNil(CloseApplicationsFilterExcludesList);
  3499. FreeAndNil(CloseApplicationsFilterList);
  3500. FreeAndNil(DeleteDirsAfterInstallList);
  3501. FreeAndNil(DeleteFilesAfterInstallList);
  3502. FreeEntryLists;
  3503. FreeAndNil(WizardDeselectedTasks);
  3504. FreeAndNil(WizardTasks);
  3505. FreeAndNil(WizardDeselectedComponents);
  3506. FreeAndNil(WizardComponents);
  3507. FreeAndNil(NewParamsForCode);
  3508. FreeAndNil(InitTasks);
  3509. FreeAndNil(InitComponents);
  3510. end.