Setup.MainFunc.pas 173 KB

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