winscpsetup.iss 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077
  1. #define AppId "winscp3"
  2. #define AppMutex "WinSCP"
  3. #define ParentRegistryKey "Software\Martin Prikryl"
  4. #define RegistryKey ParentRegistryKey+"\WinSCP 2"
  5. #define DefaultLang "en"
  6. #define WebRoot "https://winscp.net/"
  7. #define WebForum WebRoot+"forum/"
  8. #define WebDocumentation WebRoot+"eng/docs/"
  9. #define WebReport "https://winscp.net/install.php"
  10. #define Year 2025
  11. #define EnglishLang "English"
  12. #define SetupTypeData "SetupType"
  13. #define InnoSetupReg "Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
  14. #define InnoSetupAppPathReg "Inno Setup: App Path"
  15. #ifndef CompletenessThreshold
  16. #define CompletenessThreshold 100
  17. #else
  18. #define CompletenessThreshold Int(CompletenessThreshold)
  19. #endif
  20. #ifndef InclusionThreshold
  21. #define InclusionThreshold 100
  22. #else
  23. #define InclusionThreshold Int(InclusionThreshold)
  24. #endif
  25. #ifndef PuttySourceDir
  26. #if DirExists("c:\Program Files (x86)\PuTTY")
  27. #define PuttySourceDir "c:\Program Files (x86)\PuTTY"
  28. #else
  29. #define PuttySourceDir "c:\Program Files\PuTTY"
  30. #endif
  31. #endif
  32. #ifndef Status
  33. #define Status "unofficial"
  34. #endif
  35. #ifndef SourceDir
  36. #define SourceDir "..\source"
  37. #endif
  38. #ifndef BinariesDir
  39. #define BinariesDir SourceDir + "\Win32\Release"
  40. #endif
  41. #ifndef BinariesDir64
  42. #define BinariesDir64 SourceDir + "\Win64\Release"
  43. #endif
  44. #ifndef BinariesDirAssembly
  45. #define BinariesDirAssembly "..\dotnet\Win32\Release"
  46. #endif
  47. #ifndef AllTranslations
  48. #define AllTranslations
  49. #endif
  50. #define TranslationDir "translations"
  51. #define OutputDir "."
  52. #define TranslationFileMask "WinSCP.???"
  53. #define MainFileName "WinSCP.exe"
  54. #define MainFileSource BinariesDir+"\"+MainFileName
  55. #define ShellExtFileName "DragExt.dll"
  56. #define ShellExtFileSource BinariesDir+"\"+ShellExtFileName
  57. #define ShellExt64FileName "DragExt64.dll"
  58. #define ShellExt64FileSource BinariesDir64+"\"+ShellExt64FileName
  59. #define ConsoleFileSource BinariesDir+"\WinSCP.com"
  60. #define MapFileSource BinariesDir+"\WinSCP.map"
  61. #define AssemblyFileSource BinariesDirAssembly+"\WinSCPnet.dll"
  62. #ifdef Donations
  63. #define PayPalCardFileBase "PayPalCard"
  64. #endif
  65. #define Major
  66. #define Minor
  67. #define Rev
  68. #define Build
  69. #expr GetVersionComponents(MainFileSource, Major, Minor, Rev, Build)
  70. #define VersionOnly Str(Major)+"."+Str(Minor)+(Rev > 0 ? "."+Str(Rev) : "")
  71. #define Version VersionOnly+(Status != "" ? " "+Status : "")
  72. #ifndef BaseFilename
  73. #define FTag VersionOnly+(Status != "" ? "."+Status : "")
  74. #define BaseFilename "WinSCP-" + FTag + "-Setup"
  75. #endif
  76. #define WebArguments "ver=" +VersionOnly + "&lang={language}&utm_source=winscp&utm_medium=setup&utm_campaign=" + VersionOnly
  77. #define WebGettingStarted WebRoot + "eng/installed.php?" + WebArguments + "&prevver="
  78. #define MessagesPath(L) TranslationDir + "\" + "WinSCP." + L + ".islu"
  79. #define ExplorerFileBase "Explorer"
  80. #define CommanderFileBase "Commander"
  81. #define SelectDirFileBase "Opened bookmark folder-stored session folder"
  82. [Setup]
  83. AppId={#AppId}
  84. AppName=WinSCP
  85. AppPublisher=Martin Prikryl
  86. AppPublisherURL={#WebRoot}
  87. AppSupportURL={#WebForum}
  88. AppUpdatesURL={#WebRoot}eng/download.php
  89. VersionInfoCompany=Martin Prikryl
  90. VersionInfoDescription=Setup for WinSCP {#Version} (SFTP, FTP, WebDAV and SCP client)
  91. VersionInfoVersion={#Major}.{#Minor}.{#Rev}.{#Build}
  92. VersionInfoTextVersion={#Version}
  93. VersionInfoCopyright=(c) 2000-{#Year} Martin Prikryl
  94. VersionInfoOriginalFileName={#BaseFilename}.exe
  95. DefaultDirName={autopf}\WinSCP
  96. LicenseFile=license.setup.txt
  97. UninstallDisplayIcon={app}\WinSCP.exe
  98. OutputDir={#OutputDir}
  99. DisableStartupPrompt=yes
  100. AppVersion={#Version}
  101. AppVerName=WinSCP {#Version}
  102. OutputBaseFilename={#BaseFilename}
  103. SolidCompression=yes
  104. #ifdef ImagesDir
  105. WizardImageFile={#ImagesDir}\Tall *.bmp
  106. WizardSmallImageFile={#ImagesDir}\Square *.bmp
  107. #endif
  108. ShowTasksTreeLines=yes
  109. PrivilegesRequired=admin
  110. PrivilegesRequiredOverridesAllowed=commandline dialog
  111. ShowLanguageDialog=auto
  112. UsePreviousLanguage=no
  113. DisableProgramGroupPage=yes
  114. SetupIconFile=winscpsetup.ico
  115. DisableDirPage=no
  116. WizardStyle=modern
  117. CloseApplications=yes
  118. UsedUserAreasWarning=no
  119. #ifdef Sign
  120. SignTool=sign $f "WinSCP Installer" https://winscp.net/eng/docs/installation
  121. #endif
  122. [Languages]
  123. ; English has to be first so that it is pre-selected
  124. ; on Setup Select Language window, when no translation matching
  125. ; Windows UI locale is available
  126. Name: {#DefaultLang}; MessagesFile: {#MessagesPath(DefaultLang)}
  127. #define FindHandle
  128. #dim Languages[200]
  129. #define LanguageCount 0
  130. #define AnyLanguageComplete 0
  131. #define LangI
  132. ; For some reason the variable cannot be defined near the code where we use it
  133. #define AllTranslationsBuf
  134. #sub ProcessTranslationFile
  135. #define FileName FindGetFileName(FindHandle)
  136. #define Lang Copy(FileName, Pos(".", FileName)+1)
  137. #define LangNameFull ReadIni(MessagesPath(Lang), "LangOptions", "LanguageName")
  138. #define Sep Pos(" -", LangNameFull)
  139. #if Sep > 0
  140. #define LangName Copy(LangNameFull, 1, Sep - 1)
  141. #else
  142. #define LangName LangNameFull
  143. #endif
  144. #define LangID ReadIni(MessagesPath(Lang), "LangOptions", "LanguageID")
  145. #define LangCompleteness Int(ReadIni(MessagesPath(Lang), "CustomOptions", "TranslationCompleteness"))
  146. #expr Languages[LanguageCount*4] = Lang
  147. ; Not used atm
  148. #expr Languages[LanguageCount*4+1] = LangName
  149. ; Not used atm
  150. #expr Languages[LanguageCount*4+2] = LangID
  151. #expr Languages[LanguageCount*4+3] = LangCompleteness
  152. #expr LanguageCount++
  153. #if LangCompleteness >= CompletenessThreshold
  154. Name: {#Lang}; MessagesFile: {#MessagesPath(Lang)}
  155. #expr AnyLanguageComplete = 1
  156. #endif
  157. #endsub /* sub ProcessTranslationFile */
  158. #if FindHandle = FindFirst(TranslationDir + "\" + TranslationFileMask, 0)
  159. #define FResult 1
  160. #for {0; FResult; FResult = FindNext(FindHandle)} ProcessTranslationFile
  161. #expr FindClose(FindHandle)
  162. #endif
  163. ; Types are not used anymore, they are preserved only to let setup
  164. ; detect previous installation type and decide between typical/custom setup
  165. [Types]
  166. Name: full; Description: "full"
  167. Name: compact; Description: "compact"
  168. Name: custom; Description: "custom"; Flags: iscustom
  169. [Components]
  170. Name: main; Description: {cm:ApplicationComponent}; \
  171. Types: full custom compact; Flags: fixed
  172. ; Because the files for the component have Check parameters, they are ignored for the size calculation
  173. Name: shellext; Description: {cm:ShellExtComponent}; \
  174. ExtraDiskSpaceRequired: {#Max(FileSize(ShellExtFileSource), FileSize(ShellExt64FileSource))}; \
  175. Types: full compact;
  176. Name: pageant; Description: {cm:PageantComponent}; \
  177. Types: full
  178. Name: puttygen; Description: {cm:PuTTYgenComponent}; \
  179. Types: full
  180. #if AnyLanguageComplete == 1
  181. Name: transl; Description: {cm:TranslationsComponent}; \
  182. Types: full
  183. #endif
  184. [Tasks]
  185. Name: enableupdates; Description: {cm:EnableUpdates}
  186. Name: enableupdates\enablecollectusage; Description: {cm:EnableCollectUsage}
  187. ; Windows integration
  188. Name: desktopicon; Description: {cm:DesktopIconTask}
  189. Name: sendtohook; Description: {cm:SendToHookTask}
  190. Name: urlhandler; Description: {cm:RegisterAsUrlHandlers}
  191. Name: searchpath; Description: {cm:AddSearchPath}; \
  192. Flags: unchecked; Check: IsAdminInstallMode
  193. [Icons]
  194. Name: "{autoprograms}\WinSCP"; Filename: "{app}\WinSCP.exe"; Components: main; \
  195. Comment: "{cm:ProgramComment2}"; Check: not CmdLineParamExists('/NoStartIcon');
  196. ; This is created when desktopicon task is selected
  197. Name: "{autodesktop}\WinSCP"; Filename: "{app}\WinSCP.exe"; \
  198. Tasks: desktopicon; Comment: "{cm:ProgramComment2}"
  199. ; This is created when sendtohook task is selected
  200. Name: "{usersendto}\{cm:SendToHookNew}"; Filename: "{app}\WinSCP.exe"; \
  201. Parameters: "/upload"; Tasks: sendtohook
  202. [InstallDelete]
  203. ; Remove pre-5.8.2 PuTTY help file
  204. Type: files; Name: "{app}\PuTTY\putty.hlp"
  205. ; Remove pre-524 licence file (without .txt extension)
  206. Type: files; Name: "{app}\license"
  207. ; Remove pre-520 start menu folders
  208. Type: filesandordirs; Name: "{commonprograms}\WinSCP"
  209. Type: filesandordirs; Name: "{userprograms}\WinSCP"; Check: HasUserPrograms
  210. [Run]
  211. Filename: "{app}\WinSCP.exe"; Parameters: "{code:GetTaskCmdLine|RegisterForDefaultProtocols}"; \
  212. StatusMsg: {cm:RegisteringAsUrlHandlers}; Tasks: urlhandler
  213. Filename: "{app}\WinSCP.exe"; Parameters: "{code:GetTaskCmdLine|AddSearchPath}"; \
  214. StatusMsg: {cm:AddingSearchPath}; Tasks: searchpath
  215. Filename: "{app}\WinSCP.exe"; Parameters: "{code:GetTaskCmdLine|ImportSitesIfAny}"; \
  216. StatusMsg: {cm:ImportSites}; Flags: skipifsilent
  217. [UninstallDelete]
  218. ; These additional files are created by application
  219. Type: files; Name: "{app}\WinSCP.ini"
  220. Type: files; Name: "{app}\WinSCP.cgl"
  221. [Files]
  222. #ifdef ImagesDir
  223. ; Put these to the top as we extract them on demand and
  224. ; that can take long with solid compression enabled
  225. Source: "{#ImagesDir}\{#ExplorerFileBase} *.bmp"; Flags: dontcopy
  226. Source: "{#ImagesDir}\{#CommanderFileBase} *.bmp"; Flags: dontcopy
  227. Source: "{#ImagesDir}\{#SelectDirFileBase} *.bmp"; Flags: dontcopy
  228. #ifdef Donations
  229. Source: "{#ImagesDir}\{#PayPalCardFileBase} *.bmp"; Flags: dontcopy
  230. #endif
  231. #endif
  232. Source: "{#MainFileSource}"; DestDir: "{app}"; \
  233. Components: main; Flags: ignoreversion
  234. Source: "{#ConsoleFileSource}"; DestDir: "{app}"; \
  235. Components: main; Flags: ignoreversion
  236. Source: "{#MapFileSource}"; DestDir: "{app}"; \
  237. Components: main; Flags: ignoreversion
  238. Source: "{#AssemblyFileSource}"; DestDir: "{app}"; \
  239. Components: main; Flags: ignoreversion
  240. Source: "license.txt"; DestDir: "{app}"; \
  241. Components: main; Flags: ignoreversion
  242. ; If the Check is ever removed, remove the ExtraDiskSpaceRequired parameter of the component too
  243. Source: "{#ShellExtFileSource}"; DestDir: "{app}"; \
  244. Components: shellext; \
  245. Flags: regserver restartreplace uninsrestartdelete ignoreversion; \
  246. Check: not IsWin64 and ShouldInstallShellExt(ExpandConstant('{app}\{#ShellExtFileName}'), '{#GetVersionNumbersString(ShellExtFileSource)}')
  247. Source: "{#ShellExt64FileSource}"; DestDir: "{app}"; \
  248. Components: shellext; \
  249. Flags: regserver restartreplace uninsrestartdelete ignoreversion; \
  250. Check: IsWin64 and ShouldInstallShellExt(ExpandConstant('{app}\{#ShellExt64FileName}'), '{#GetVersionNumbersString(ShellExt64FileSource)}')
  251. Source: "{#PuttySourceDir}\LICENCE"; DestDir: "{app}\PuTTY"; \
  252. Components: pageant puttygen; Flags: ignoreversion
  253. Source: "{#PuttySourceDir}\putty.chm"; DestDir: "{app}\PuTTY"; \
  254. Components: pageant puttygen; Flags: ignoreversion
  255. Source: "{#PuttySourceDir}\pageant.exe"; DestDir: "{app}\PuTTY"; \
  256. Components: pageant; Flags: ignoreversion
  257. Source: "{#PuttySourceDir}\puttygen.exe"; DestDir: "{app}\PuTTY"; \
  258. Components: puttygen; Flags: ignoreversion
  259. #ifdef ExtensionsDir
  260. Source: "{#ExtensionsDir}\*.*"; DestDir: "{app}\Extensions"
  261. #endif
  262. #ifdef Sponsor
  263. Source: "{#Sponsor}\*.*"; Flags: dontcopy skipifsourcedoesntexist
  264. #define SponsorImages
  265. #if FindHandle = FindFirst(Sponsor + "\*.*", 0)
  266. #define FResult 1
  267. #for {0; FResult; FResult = FindNext(FindHandle)} SponsorImages = SponsorImages + FindGetFileName(FindHandle) + ","
  268. #expr FindClose(FindHandle)
  269. #expr SponsorImages = Copy(SponsorImages, 1, Len(SponsorImages) - 1)
  270. #endif
  271. #endif
  272. [Registry]
  273. Root: HKCU; Subkey: "{#ParentRegistryKey}"; Flags: uninsdeletekeyifempty
  274. Root: HKCU; Subkey: "{#RegistryKey}"; Flags: uninsdeletekeyifempty
  275. ; Norton Commander interface
  276. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  277. ValueName: "Interface"; ValueData: 0; Check: UserSettings(1)
  278. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  279. ValueName: "DefaultInterfaceInterface"; ValueData: 0; \
  280. Check: UserSettings(1) and IsAdminInstallMode; Flags: noerror
  281. ; Explorer-like interface
  282. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  283. ValueName: "Interface"; ValueData: 1; Check: not UserSettings(1)
  284. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  285. ValueName: "DefaultInterfaceInterface"; ValueData: 1; \
  286. Check: (not UserSettings(1)) and IsAdminInstallMode; Flags: noerror
  287. ; If installer enabled ddext, let it reset the settings on uninstall,
  288. ; so the default is used on the next run
  289. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  290. ValueName: "DDExtEnabled"; ValueData: 1; Components: shellext; \
  291. Flags: uninsdeletevalue
  292. ; Updates
  293. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface\Updates"; \
  294. ValueType: dword; ValueName: "Period"; ValueData: 7; \
  295. Tasks: enableupdates; Check: not UpdatesEnabled
  296. Root: HKLM; SubKey: "{#RegistryKey}"; \
  297. ValueType: dword; ValueName: "DefaultUpdatesPeriod"; ValueData: 7; \
  298. Tasks: enableupdates; Flags: noerror; Check: IsAdminInstallMode
  299. Root: HKLM; SubKey: "{#RegistryKey}"; \
  300. ValueType: dword; ValueName: "DefaultCollectUsage"; ValueData: 1; \
  301. Tasks: enableupdates\enablecollectusage; Flags: noerror; Check: IsAdminInstallMode
  302. #sub EmitLang
  303. #if Languages[LangI*4+3] >= InclusionThreshold
  304. [Files]
  305. Source: "{#TranslationDir}\WinSCP.{#Languages[LangI*4]}"; DestDir: "{app}\Translations"; \
  306. Components: transl; Flags: ignoreversion
  307. #endif
  308. #endsub /* sub EmitLang */
  309. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang
  310. ; Delete translations from installation root folder (pre-5.10)
  311. [InstallDelete]
  312. #expr AllTranslationsBuf = AllTranslations + '-'
  313. #sub DeleteRootTranslation
  314. #define P Pos('-', AllTranslationsBuf)
  315. #define Lang Copy(AllTranslationsBuf, 1, P - 1)
  316. #expr AllTranslationsBuf = Copy(AllTranslationsBuf, P + 1)
  317. Type: files; Name: "{app}\WinSCP.{#Lang}"
  318. #endsub
  319. #for { 0; Len(AllTranslationsBuf) > 0; 0 } DeleteRootTranslation
  320. [UninstallRun]
  321. ; Make sure no later uninstall task recreate the configuration
  322. Filename: "{app}\WinSCP.exe"; Parameters: "{code:GetTaskCmdLine|UninstallCleanup}"; \
  323. RunOnceId: "UninstallCleanup"
  324. Filename: "{app}\WinSCP.exe"; Parameters: "{code:GetTaskCmdLine|RemoveSearchPath}"; \
  325. RunOnceId: "RemoveSearchPath"
  326. Filename: "{app}\WinSCP.exe"; Parameters: "{code:GetTaskCmdLine|UnregisterForProtocols}"; \
  327. RunOnceId: "UnregisterForProtocols"
  328. [Code]
  329. const
  330. NewLine = #13#10;
  331. var
  332. TypicalTypeButton: TRadioButton;
  333. CustomTypeButton: TRadioButton;
  334. CommanderRadioButton: TRadioButton;
  335. ExplorerRadioButton: TRadioButton;
  336. LaunchCheckbox: TCheckbox;
  337. OpenGettingStartedCheckbox: TCheckbox;
  338. AreUpdatesEnabled: Boolean;
  339. AutomaticUpdate: Boolean;
  340. Upgrade: Boolean;
  341. PrevVersion: string;
  342. ShellExtNewerCacheFileName: string;
  343. ShellExtNewerCacheResult: Boolean;
  344. ShellExtNoRestart: Boolean;
  345. #ifdef Donations
  346. DonationPanel: TPanel;
  347. AboutDonationCaption: TLabel;
  348. #endif
  349. InstallationDone: Boolean;
  350. LicenseAccepted: Boolean;
  351. InitDir: string;
  352. InitComponents: string;
  353. InitTasks: string;
  354. InitInterface: Integer;
  355. Donated: Boolean;
  356. InterfacePage: TWizardPage;
  357. SetupTypePage: TWizardPage;
  358. #ifdef Sponsor
  359. SponsorReq: Variant;
  360. SponsorPage: TWizardPage;
  361. Sponsor: string;
  362. SponsorStatus: string;
  363. #endif
  364. procedure ShowMessage(Text: string);
  365. begin
  366. MsgBox(Text, mbInformation, MB_OK);
  367. end;
  368. procedure CutVersionPart(var VersionString: string; var VersionPart: Word);
  369. var
  370. P: Integer;
  371. begin
  372. P := Pos('.', VersionString);
  373. if P > 0 then
  374. begin
  375. VersionPart := StrToIntDef(Copy(VersionString, 1, P - 1), 0);
  376. Delete(VersionString, 1, P);
  377. end
  378. else
  379. begin
  380. VersionPart := StrToIntDef(VersionString, 0);
  381. VersionString := '';
  382. end;
  383. end;
  384. function CmdLineParamExists(const Value: string): Boolean;
  385. var
  386. I: Integer;
  387. begin
  388. Result := False;
  389. for I := 1 to ParamCount do
  390. begin
  391. if CompareText(ParamStr(I), Value) = 0 then
  392. begin
  393. Result := True;
  394. Exit;
  395. end;
  396. end;
  397. end;
  398. function ShouldInstallShellExt(FileName: string; InstalledVersion: string): Boolean;
  399. var
  400. ExistingMS, ExistingLS: Cardinal;
  401. ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild: Cardinal;
  402. InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild: Word;
  403. begin
  404. if ShellExtNewerCacheFileName = FileName then
  405. begin
  406. if ShellExtNewerCacheResult then
  407. begin
  408. Log(Format('Allowing installation of shell extension %s as already decided', [FileName]));
  409. Result := True;
  410. end
  411. else
  412. begin
  413. Log(Format('Skipping installation of shell extension %s as already decided', [FileName]));
  414. Result := False;
  415. end;
  416. // Keeping ShellExtNoRestart value
  417. end
  418. else
  419. if not FileExists(FileName) then
  420. begin
  421. Log(Format('Shell extension %s does not exist yet, allowing installation', [FileName]));
  422. ShellExtNoRestart := False;
  423. Result := True;
  424. end
  425. else
  426. if not GetVersionNumbers(FileName, ExistingMS, ExistingLS) then
  427. begin
  428. Log(Format('Cannot retrieve version of existing shell extension %s, allowing installation', [FileName]));
  429. ShellExtNoRestart := False;
  430. Result := True;
  431. end
  432. else
  433. begin
  434. if CmdLineParamExists('/InstallShellExt') then
  435. begin
  436. Log('Installing shell extension and will require restart as explicitly requested using command-line parameter.');
  437. Result := True;
  438. ShellExtNoRestart := False;
  439. end
  440. else
  441. if CmdLineParamExists('/InstallShellExtNoRestart') then
  442. begin
  443. Log('Installing shell extension and will delay replacing as explicitly requested using command-line parameter.');
  444. Result := True;
  445. ShellExtNoRestart := True;
  446. end
  447. else
  448. begin
  449. ExistingMajor := ExistingMS shr 16;
  450. ExistingMinor := ExistingMS and $FFFF;
  451. ExistingRev := ExistingLS shr 16;
  452. ExistingBuild := ExistingLS and $FFFF;
  453. Log(Format('Existing shell extension %s version: %d.%d.%d[.%d]', [FileName, ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild]));
  454. Log(Format('Installed extension version string: %s', [InstalledVersion]));
  455. CutVersionPart(InstalledVersion, InstalledMajor);
  456. CutVersionPart(InstalledVersion, InstalledMinor);
  457. CutVersionPart(InstalledVersion, InstalledRev);
  458. CutVersionPart(InstalledVersion, InstalledBuild);
  459. Log(Format('Installed extension version: %d.%d.%d[.%d]', [InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild]));
  460. if (InstalledMajor <> ExistingMajor) or
  461. ((ExistingMajor = 1) and (ExistingMinor <= 1)) then
  462. begin
  463. if InstalledMajor <> ExistingMajor then
  464. begin
  465. Log('Existing extension has different major version, allowing installation, and will require restart, if it is locked.')
  466. end
  467. else
  468. begin
  469. // 1.1 uses Ansi encoding, and is incompatible with 1.2 and newer which uses Unicode
  470. Log('Existing extension is 1.1 or older, allowing installation, and will require restart, if it is locked.');
  471. end;
  472. Result := True;
  473. ShellExtNoRestart := False;
  474. end
  475. else
  476. if (InstalledMinor > ExistingMinor) or
  477. ((InstalledMinor = ExistingMinor) and (InstalledRev > ExistingRev)) then
  478. begin
  479. Log('Installed extension is newer than existing extension, but major version is the same, allowing installation, but we will delay replacing the extension until the next system start, if it is locked.');
  480. Result := True;
  481. ShellExtNoRestart := True;
  482. end
  483. else
  484. begin
  485. Log('Installed extension is same or older than existing extension (but the same major version), skipping installation');
  486. ShellExtNoRestart := False;
  487. Result := False;
  488. end;
  489. end;
  490. end;
  491. ShellExtNewerCacheFileName := FileName;
  492. ShellExtNewerCacheResult := Result;
  493. end;
  494. function UpdatesEnabled: Boolean;
  495. begin
  496. Result := AreUpdatesEnabled;
  497. end;
  498. function UserSettings(Settings: Integer): Boolean;
  499. begin
  500. case Settings of
  501. 1: Result := CommanderRadioButton.Checked;
  502. else Result := False;
  503. end;
  504. end;
  505. function LanguageCompleteness(Lang: string): Integer;
  506. begin
  507. #sub EmitLang4
  508. if (Lang = '{#Languages[LangI*4]}') then
  509. begin
  510. Result := {#Languages[LangI*4+3]};
  511. end
  512. else
  513. #endsub /* sub EmitLang4 */
  514. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang4
  515. // used also for the default language
  516. Result := 100;
  517. end;
  518. procedure OpenBrowser(Url: string);
  519. var
  520. ErrorCode: Integer;
  521. begin
  522. ShellExec('open', Url, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
  523. end;
  524. function IsRestartingApplicationsPage: Boolean;
  525. begin
  526. Result := WizardForm.PreparingMemo.Visible;
  527. end;
  528. function IsRestartPage: Boolean;
  529. begin
  530. Result := WizardForm.YesRadio.Visible;
  531. end;
  532. #ifdef Sponsor
  533. var
  534. SponsoringClicked: Boolean;
  535. procedure SponsoringLinkLabelClick(Sender: TObject);
  536. begin
  537. SponsoringClicked := True;
  538. OpenBrowser('{#WebReport}?mode=sponsoring' + Format('&sponsor=%s&', [Sponsor]) + ExpandConstant('{#WebArguments}'));
  539. end;
  540. #endif
  541. procedure OpenHelp;
  542. var
  543. HelpKeyword: string;
  544. begin
  545. HelpKeyword := 'ui_installer'; // default
  546. case WizardForm.CurPageID of
  547. wpLicense:
  548. HelpKeyword := 'ui_installer_license';
  549. wpSelectDir:
  550. HelpKeyword := 'ui_installer_selectdir';
  551. wpSelectComponents:
  552. HelpKeyword := 'ui_installer_selectcomponents';
  553. wpSelectTasks:
  554. HelpKeyword := 'ui_installer_selecttasks';
  555. wpReady:
  556. HelpKeyword := 'ui_installer_ready';
  557. wpPreparing:
  558. if IsRestartingApplicationsPage then
  559. HelpKeyword := 'ui_installer_restartingapplications';
  560. wpFinished:
  561. HelpKeyword := 'ui_installer_finished';
  562. SetupTypePage.ID:
  563. HelpKeyword := 'ui_installer_setuptype';
  564. InterfacePage.ID:
  565. HelpKeyword := 'ui_installer_interface';
  566. #ifdef Sponsor
  567. SponsorPage.ID:
  568. begin
  569. SponsoringLinkLabelClick(nil);
  570. Exit;
  571. end;
  572. #endif
  573. end;
  574. OpenBrowser('{#WebDocumentation}' + HelpKeyword + '?' + ExpandConstant('{#WebArguments}'));
  575. end;
  576. procedure HelpButtonClick(Sender: TObject);
  577. begin
  578. OpenHelp;
  579. end;
  580. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  581. begin
  582. if Key = 112 { VK_F1 } then
  583. begin
  584. OpenHelp;
  585. Key := 0;
  586. end;
  587. end;
  588. procedure CaptionClick(Sender: TObject);
  589. begin
  590. WizardForm.ActiveControl := TLabel(Sender).FocusControl;
  591. end;
  592. procedure ImageClick(Sender: TObject);
  593. begin
  594. WizardForm.ActiveControl := TWinControl(TControl(Sender).Tag);
  595. end;
  596. function WillRestart: Boolean;
  597. begin
  598. Result := WizardForm.YesRadio.Visible and WizardForm.YesRadio.Checked;
  599. end;
  600. procedure UpdatePostInstallRunCheckboxes(Sender: TObject);
  601. begin
  602. LaunchCheckbox.Enabled := not WillRestart;
  603. OpenGettingStartedCheckbox.Enabled :=
  604. LaunchCheckbox.Enabled
  605. end;
  606. procedure LinkLabel(Control: TLabel);
  607. begin
  608. Control.ParentFont := True;
  609. Control.Font.Style := Control.Font.Style + [fsUnderline];
  610. Control.Font.Color := clBlue;
  611. Control.Cursor := crHand;
  612. end;
  613. function IsTypicalInstallation: Boolean;
  614. begin
  615. Result := TypicalTypeButton.Checked;
  616. end;
  617. #ifdef Donations
  618. procedure AboutDonationsLinkClick(Sender: TObject);
  619. begin
  620. OpenBrowser('{#WebRoot}eng/donate.php?' + ExpandConstant('{#WebArguments}'));
  621. Donated := true;
  622. end;
  623. procedure DonateLinkClick(Sender: TObject);
  624. var
  625. Control: TControl;
  626. begin
  627. Control := TControl(Sender);
  628. OpenBrowser('{#WebRoot}eng/donate.php?amount=' + IntToStr(Control.Tag) + '&currency=' + CustomMessage('Currency') + '&' + ExpandConstant('{#WebArguments}'));
  629. Donated := true;
  630. end;
  631. procedure CreateDonateLink(Amount: Integer; var Top: Integer);
  632. var
  633. Caption: TLabel;
  634. begin
  635. Caption := TLabel.Create(DonationPanel);
  636. Caption.Left := 0;
  637. Caption.Top := Top;
  638. Caption.Tag := Amount;
  639. Caption.Parent := DonationPanel;
  640. Caption.Caption := Format(CustomMessage('Donate'), ['$' + IntToStr(Amount)]);
  641. Caption.OnClick := @DonateLinkClick;
  642. LinkLabel(Caption);
  643. Top := Top + ScaleY(16);
  644. end;
  645. #endif
  646. procedure LoadBitmap(Image: TBitmapImage; FileName: string);
  647. var
  648. Bitmap: TBitmap;
  649. begin
  650. Bitmap := TBitmap.Create();
  651. Bitmap.AlphaFormat := afDefined;
  652. Bitmap.LoadFromFile(FileName);
  653. Image.Bitmap := Bitmap;
  654. Bitmap.Free;
  655. end;
  656. function GetScalingFactor: Integer;
  657. begin
  658. if WizardForm.Font.PixelsPerInch >= 192 then Result := 200
  659. else
  660. if WizardForm.Font.PixelsPerInch >= 144 then Result := 150
  661. else
  662. if WizardForm.Font.PixelsPerInch >= 120 then Result := 125
  663. else Result := 100;
  664. end;
  665. procedure LoadEmbededScaledIcon(Image: TBitmapImage; NameBase: string; SizeBase: Integer);
  666. var
  667. Name: String;
  668. FileName: string;
  669. begin
  670. Name := Format('%s %d.bmp', [NameBase, SizeBase * GetScalingFactor div 100]);
  671. ExtractTemporaryFile(Name);
  672. FileName := ExpandConstant('{tmp}\' + Name);
  673. LoadBitmap(Image, FileName);
  674. // we won't need this anymore
  675. DeleteFile(FileName);
  676. Image.AutoSize := True;
  677. end;
  678. // WORKAROUND
  679. // Checkboxes and Radio buttons created on runtime do
  680. // not scale their height automatically
  681. procedure ScaleFixedHeightControl(Control: TButtonControl);
  682. begin
  683. Control.Height := ScaleY(Control.Height);
  684. end;
  685. function GetBottom(Control: TControl): Integer;
  686. begin
  687. Result := Control.Top + Control.Height;
  688. end;
  689. function GetRight(Control: TControl): Integer;
  690. begin
  691. Result := Control.Left + Control.Width;
  692. end;
  693. function MsiEnumRelatedProducts(
  694. lpUpgradeCode: string; dwReserved, iProductIndex: DWORD; lpProductBuf: string): UINT;
  695. external '[email protected] stdcall delayload setuponly';
  696. function MsiGetProductInfo(szProduct, szAttribute, lpValueBuf: string; var pcchValueBuf: DWORD): UINT;
  697. external '[email protected] stdcall delayload setuponly';
  698. const
  699. ERROR_SUCCESS = 0;
  700. ERROR_MORE_DATA = 234;
  701. ERROR_NO_MORE_ITEMS = 259;
  702. function GetProductInfo(ProductCode, Attribute: string): string;
  703. var
  704. BufSize: DWORD;
  705. ErrorCode: Integer;
  706. begin
  707. BufSize := 256;
  708. SetLength(Result, BufSize);
  709. ErrorCode := MsiGetProductInfo(ProductCode, Attribute, Result, BufSize);
  710. if ErrorCode = ERROR_MORE_DATA then
  711. begin
  712. Inc(BufSize);
  713. SetLength(Result, BufSize);
  714. ErrorCode := MsiGetProductInfo(ProductCode, Attribute, Result, BufSize);
  715. end;
  716. if ErrorCode <> ERROR_SUCCESS then
  717. begin
  718. Log(Format('Error %d reading MSI installation %s: %s', [ErrorCode, Attribute, SysErrorMessage(ErrorCode)]));
  719. Result := '';
  720. end
  721. else
  722. begin
  723. SetLength(Result, BufSize);
  724. Log(Format('MSI installation %s: %s [%d]', [Attribute, Result, Integer(BufSize)]));
  725. end;
  726. end;
  727. function InitializeSetup: Boolean;
  728. var
  729. WaitInterval: Integer;
  730. Wait: Integer;
  731. ProductCode, VersionString: string;
  732. ErrorCode: Integer;
  733. begin
  734. Log('Initializing...');
  735. AutomaticUpdate := CmdLineParamExists('/AutomaticUpdate');
  736. if AutomaticUpdate then
  737. begin
  738. Log('Automatic update');
  739. Wait := 10000;
  740. end
  741. else
  742. begin
  743. Wait := 0;
  744. end;
  745. Log('Checking for mutexes...');
  746. WaitInterval := 250;
  747. while (Wait > 0) and CheckForMutexes('{#AppMutex}') do
  748. begin
  749. Log('Application is still running, waiting...');
  750. Sleep(WaitInterval);
  751. Wait := Wait - WaitInterval;
  752. end;
  753. while CheckForMutexes('{#AppMutex}') do
  754. begin
  755. if MsgBox(
  756. FmtMessage(SetupMessage(msgSetupAppRunningError), ['WinSCP']),
  757. mbError, MB_OKCANCEL) <> IDOK then
  758. begin
  759. Abort;
  760. end;
  761. end;
  762. Result := True;
  763. if CmdLineParamExists('/OverrideMsi') then
  764. begin
  765. Log('Skipping MSI installation check');
  766. end
  767. else
  768. begin
  769. Log('Checking MSI installation...');
  770. try
  771. SetLength(ProductCode, 38);
  772. ErrorCode := MsiEnumRelatedProducts('{029F9450-CFEF-4408-A2BB-B69ECE29EB18}', 0, 0, ProductCode);
  773. if ErrorCode <> ERROR_SUCCESS then
  774. begin
  775. if ErrorCode = ERROR_NO_MORE_ITEMS then
  776. begin
  777. Log('MSI installation not detected');
  778. end
  779. else
  780. begin
  781. Log(Format('Error %d detecting MSI installation: %s', [ErrorCode, SysErrorMessage(ErrorCode)]));
  782. end;
  783. end
  784. else
  785. begin
  786. Log('Product code: "' + ProductCode + '"');
  787. GetProductInfo(ProductCode, 'InstalledProductName');
  788. GetProductInfo(ProductCode, 'InstallDate');
  789. GetProductInfo(ProductCode, 'InstallLocation');
  790. GetProductInfo(ProductCode, 'Publisher');
  791. VersionString := GetProductInfo(ProductCode, 'VersionString');
  792. GetProductInfo(ProductCode, 'VersionMajor');
  793. GetProductInfo(ProductCode, 'VersionMinor');
  794. if VersionString = '' then
  795. begin
  796. Log('Corrupted MSI installation, proceeding...');
  797. end
  798. else
  799. begin
  800. MsgBox(CustomMessage('MsiInstallation'), mbError, MB_OK);
  801. Result := False;
  802. end;
  803. end;
  804. except
  805. Log('Error checking MSI installations: ' + GetExceptionMessage);
  806. end;
  807. end;
  808. end;
  809. // Keep in sync with similar function on Preferences dialog
  810. function Bullet(S: string): string;
  811. begin
  812. if Copy(S, 1, 1) = '-' then S := #$2022' ' + Trim(Copy(S, 2, Length(S) - 1));
  813. Result := S;
  814. end;
  815. procedure InitializeWizard;
  816. var
  817. UserInterface: Cardinal;
  818. UpdatesPeriod: Cardinal;
  819. Caption: TLabel;
  820. #ifdef ImagesDir
  821. Image: TBitmapImage;
  822. #endif
  823. HelpButton: TButton;
  824. #ifdef Donations
  825. P, H: Integer;
  826. #ifdef ImagesDir
  827. P2: Integer;
  828. #endif
  829. #endif
  830. S: string;
  831. Completeness: Integer;
  832. begin
  833. InstallationDone := False;
  834. LicenseAccepted := False;
  835. InitInterface := -1;
  836. Upgrade :=
  837. // We may want to use HKA to work correctly with side-by-side installations.
  838. // But as Updade is really used for changing REGISTRY configuration options only,
  839. // which are shared between all-users and current-user installations, it does not really matter.
  840. RegQueryStringValue(HKLM, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S) or
  841. RegQueryStringValue(HKCU, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S);
  842. if Upgrade then
  843. begin
  844. Log('Upgrade');
  845. end
  846. else
  847. begin
  848. Log('New installation');
  849. end;
  850. if Upgrade and GetVersionNumbersString(AddBackslash(WizardDirValue) + '{#MainFileName}', PrevVersion) and
  851. (PrevVersion[2] = '.') and (PrevVersion[4] = '.') and (PrevVersion[6] = '.') then
  852. begin
  853. PrevVersion := Copy(PrevVersion, 1, 5);
  854. end;
  855. WizardForm.KeyPreview := True;
  856. WizardForm.OnKeyDown := @FormKeyDown;
  857. // allow installation without requiring user to accept license
  858. WizardForm.LicenseAcceptedRadio.Checked := True;
  859. WizardForm.LicenseAcceptedRadio.Visible := False;
  860. WizardForm.LicenseNotAcceptedRadio.Visible := False;
  861. WizardForm.LicenseMemo.Height :=
  862. GetBottom(WizardForm.LicenseNotAcceptedRadio) -
  863. WizardForm.LicenseMemo.Top - ScaleY(5);
  864. // hide installation types combo
  865. WizardForm.TypesCombo.Visible := False;
  866. WizardForm.ComponentsList.Height :=
  867. GetBottom(WizardForm.ComponentsList) -
  868. WizardForm.TypesCombo.Top;
  869. WizardForm.ComponentsList.Top := WizardForm.TypesCombo.Top;
  870. // add help button
  871. HelpButton := TButton.Create(WizardForm);
  872. HelpButton.Parent := WizardForm;
  873. HelpButton.Anchors := [akLeft, akBottom];
  874. HelpButton.Left := WizardForm.ClientWidth - GetRight(WizardForm.CancelButton);
  875. HelpButton.Top := WizardForm.CancelButton.Top;
  876. HelpButton.Width := WizardForm.CancelButton.Width;
  877. HelpButton.Height := WizardForm.CancelButton.Height;
  878. HelpButton.Caption := CustomMessage('HelpButton');
  879. HelpButton.OnClick := @HelpButtonClick;
  880. Completeness := LanguageCompleteness(ActiveLanguage);
  881. if (Completeness < 100) and (not WizardSilent) then
  882. begin
  883. ShowMessage(FmtMessage(CustomMessage('IncompleteTranslation'), [IntToStr(Completeness)]));
  884. end;
  885. // installation type page
  886. SetupTypePage := CreateCustomPage(wpLicense,
  887. CustomMessage('SetupTypeTitle'),
  888. CustomMessage('SetupTypePrompt'));
  889. TypicalTypeButton := TRadioButton.Create(SetupTypePage);
  890. if not Upgrade then
  891. S := CustomMessage('TypicalType')
  892. else
  893. S := CustomMessage('TypicalUpgradeType');
  894. TypicalTypeButton.Caption :=
  895. FmtMessage(CustomMessage('Recommended'), [S]);
  896. // check typical install, if typical install was installed before or
  897. // when version without setup type support was installed with
  898. // "full" installation or when there were no installation before
  899. // ("full" installation is default)
  900. TypicalTypeButton.Checked :=
  901. ((GetPreviousData('{#SetupTypeData}', '') = 'typical')) or
  902. ((GetPreviousData('{#SetupTypeData}', '') = '') and
  903. (WizardSetupType(False) = 'full'));
  904. TypicalTypeButton.Left := ScaleX(4);
  905. TypicalTypeButton.Width := SetupTypePage.SurfaceWidth -
  906. TypicalTypeButton.Left;
  907. ScaleFixedHeightControl(TypicalTypeButton);
  908. TypicalTypeButton.Parent := SetupTypePage.Surface;
  909. Caption := TLabel.Create(SetupTypePage);
  910. Caption.WordWrap := True;
  911. if not Upgrade then
  912. begin
  913. Caption.Caption :=
  914. Bullet(CustomMessage('TypicalType1')) + NewLine +
  915. Bullet(CustomMessage('TypicalType2')) + NewLine +
  916. Bullet(CustomMessage('TypicalType3'));
  917. end
  918. else
  919. begin
  920. Caption.Caption :=
  921. Bullet(CustomMessage('TypicalUpgradeType1'));
  922. end;
  923. Caption.Left := ScaleX(4) + ScaleX(20);
  924. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  925. Caption.Top := GetBottom(TypicalTypeButton) + ScaleY(6);
  926. Caption.Parent := SetupTypePage.Surface;
  927. Caption.FocusControl := TypicalTypeButton;
  928. Caption.OnClick := @CaptionClick;
  929. CustomTypeButton := TRadioButton.Create(SetupTypePage);
  930. if not Upgrade then
  931. CustomTypeButton.Caption := CustomMessage('CustomType')
  932. else
  933. CustomTypeButton.Caption := CustomMessage('CustomUpgradeType');
  934. CustomTypeButton.Checked := (not TypicalTypeButton.Checked);
  935. CustomTypeButton.Left := ScaleX(4);
  936. CustomTypeButton.Width := SetupTypePage.SurfaceWidth -
  937. CustomTypeButton.Left;
  938. CustomTypeButton.Top := GetBottom(Caption) + ScaleY(10);
  939. ScaleFixedHeightControl(CustomTypeButton);
  940. CustomTypeButton.Parent := SetupTypePage.Surface;
  941. Caption := TLabel.Create(SetupTypePage);
  942. Caption.WordWrap := True;
  943. if not Upgrade then
  944. begin
  945. Caption.Caption :=
  946. Bullet(CustomMessage('CustomType1'));
  947. end
  948. else
  949. begin
  950. Caption.Caption :=
  951. Bullet(CustomMessage('CustomUpgradeType1')) + NewLine +
  952. Bullet(CustomMessage('CustomUpgradeType2'));
  953. end;
  954. Caption.Left := ScaleX(4) + ScaleX(20);
  955. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  956. Caption.Top := GetBottom(CustomTypeButton) + ScaleY(6);
  957. Caption.Parent := SetupTypePage.Surface;
  958. Caption.FocusControl := CustomTypeButton;
  959. Caption.OnClick := @CaptionClick;
  960. // interface page
  961. InterfacePage := CreateCustomPage(wpSelectTasks,
  962. CustomMessage('UserSettingsTitle'),
  963. CustomMessage('UserSettingsPrompt'));
  964. UpdatesPeriod := 0;
  965. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface\Updates',
  966. 'Period', UpdatesPeriod);
  967. AreUpdatesEnabled := (UpdatesPeriod <> 0);
  968. UserInterface := 0; { default is commander }
  969. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface',
  970. 'Interface', UserInterface);
  971. Caption := TLabel.Create(InterfacePage);
  972. Caption.Caption := CustomMessage('UserInterfaceStyle');
  973. Caption.Width := InterfacePage.SurfaceWidth;
  974. Caption.Parent := InterfacePage.Surface;
  975. CommanderRadioButton := TRadioButton.Create(InterfacePage);
  976. CommanderRadioButton.Caption := CustomMessage('NortonCommanderInterfaceC');
  977. CommanderRadioButton.Checked := (UserInterface = 0);
  978. CommanderRadioButton.Left := ScaleX(4);
  979. CommanderRadioButton.Width := ScaleX(116);
  980. CommanderRadioButton.Top := GetBottom(Caption) + ScaleY(6);
  981. ScaleFixedHeightControl(CommanderRadioButton);
  982. CommanderRadioButton.Parent := InterfacePage.Surface;
  983. #ifdef ImagesDir
  984. Image := TBitmapImage.Create(InterfacePage);
  985. Image.Top := GetBottom(CommanderRadioButton) + ScaleY(6);
  986. Image.Left := CommanderRadioButton.Left + ScaleX(45);
  987. Image.Parent := InterfacePage.Surface;
  988. LoadEmbededScaledIcon(Image, '{#CommanderFileBase}', 32);
  989. Image.OnClick := @ImageClick;
  990. Image.Tag := Integer(CommanderRadioButton);
  991. #endif
  992. Caption := TLabel.Create(InterfacePage);
  993. Caption.WordWrap := True;
  994. Caption.Caption :=
  995. Bullet(CustomMessage('NortonCommanderInterface1')) + NewLine +
  996. Bullet(CustomMessage('NortonCommanderInterface2')) + NewLine +
  997. Bullet(CustomMessage('NortonCommanderInterface3'));
  998. Caption.Anchors := [akLeft, akTop, akRight];
  999. Caption.Left := GetRight(CommanderRadioButton);
  1000. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  1001. Caption.Top := CommanderRadioButton.Top;
  1002. Caption.Parent := InterfacePage.Surface;
  1003. Caption.FocusControl := CommanderRadioButton;
  1004. Caption.OnClick := @CaptionClick;
  1005. ExplorerRadioButton := TRadioButton.Create(InterfacePage);
  1006. ExplorerRadioButton.Caption := CustomMessage('ExplorerInterfaceC');
  1007. ExplorerRadioButton.Checked := (UserInterface <> 0);
  1008. ExplorerRadioButton.Left := ScaleX(4);
  1009. ExplorerRadioButton.Width := CommanderRadioButton.Width;
  1010. ExplorerRadioButton.Top := GetBottom(Caption) + ScaleY(10);
  1011. ScaleFixedHeightControl(ExplorerRadioButton);
  1012. ExplorerRadioButton.Parent := InterfacePage.Surface;
  1013. #ifdef ImagesDir
  1014. Image := TBitmapImage.Create(InterfacePage);
  1015. Image.Top := GetBottom(ExplorerRadioButton) + ScaleY(6);
  1016. Image.Left := ExplorerRadioButton.Left + ScaleX(45);
  1017. Image.Parent := InterfacePage.Surface;
  1018. LoadEmbededScaledIcon(Image, '{#ExplorerFileBase}', 32);
  1019. Image.OnClick := @ImageClick;
  1020. Image.Tag := Integer(ExplorerRadioButton);
  1021. #endif
  1022. Caption := TLabel.Create(InterfacePage);
  1023. Caption.WordWrap := True;
  1024. Caption.Caption :=
  1025. Bullet(CustomMessage('ExplorerInterface1')) + NewLine +
  1026. Bullet(CustomMessage('ExplorerInterface2')) + NewLine +
  1027. Bullet(CustomMessage('ExplorerInterface3'));
  1028. Caption.Anchors := [akLeft, akTop, akRight];
  1029. Caption.Left := GetRight(ExplorerRadioButton);
  1030. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  1031. Caption.Top := ExplorerRadioButton.Top;
  1032. Caption.Parent := InterfacePage.Surface;
  1033. Caption.FocusControl := ExplorerRadioButton;
  1034. Caption.OnClick := @CaptionClick;
  1035. // run checkbox
  1036. LaunchCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  1037. LaunchCheckbox.Caption := CustomMessage('Launch');
  1038. LaunchCheckbox.Checked := True;
  1039. LaunchCheckbox.Left := WizardForm.YesRadio.Left;
  1040. LaunchCheckbox.Width := WizardForm.YesRadio.Width;
  1041. ScaleFixedHeightControl(LaunchCheckbox);
  1042. LaunchCheckbox.Parent := WizardForm.FinishedPage;
  1043. OpenGettingStartedCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  1044. OpenGettingStartedCheckbox.Caption := CustomMessage('OpenGettingStarted');
  1045. OpenGettingStartedCheckbox.Checked := True;
  1046. OpenGettingStartedCheckbox.Left := WizardForm.YesRadio.Left;
  1047. OpenGettingStartedCheckbox.Width := WizardForm.YesRadio.Width;
  1048. ScaleFixedHeightControl(OpenGettingStartedCheckbox);
  1049. OpenGettingStartedCheckbox.Parent := WizardForm.FinishedPage;
  1050. #ifdef Donations
  1051. DonationPanel := TPanel.Create(WizardForm.FinishedPage);
  1052. DonationPanel.Left := WizardForm.YesRadio.Left;
  1053. DonationPanel.Width := WizardForm.YesRadio.Width;
  1054. DonationPanel.Parent := WizardForm.FinishedPage;
  1055. DonationPanel.BevelInner := bvNone;
  1056. DonationPanel.BevelOuter := bvNone;
  1057. DonationPanel.Color := WizardForm.FinishedPage.Color;
  1058. Caption := TLabel.Create(DonationPanel);
  1059. Caption.WordWrap := True;
  1060. Caption.Caption := CustomMessage('PleaseDonate');
  1061. Caption.Anchors := [akLeft, akTop, akRight];
  1062. Caption.Left := 0;
  1063. Caption.Top := 0;
  1064. Caption.Width := DonationPanel.Width;
  1065. Caption.Parent := DonationPanel;
  1066. P := GetBottom(Caption) + ScaleY(12);
  1067. #ifdef ImagesDir
  1068. P2 := P;
  1069. #endif
  1070. CreateDonateLink(19, P);
  1071. CreateDonateLink(29, P);
  1072. CreateDonateLink(49, P);
  1073. AboutDonationCaption := TLabel.Create(DonationPanel);
  1074. AboutDonationCaption.Left := 0;
  1075. AboutDonationCaption.Top := P;
  1076. AboutDonationCaption.Parent := DonationPanel;
  1077. AboutDonationCaption.Caption := CustomMessage('AboutDonations');
  1078. AboutDonationCaption.OnClick := @AboutDonationsLinkClick;
  1079. LinkLabel(AboutDonationCaption);
  1080. #ifdef ImagesDir
  1081. Image := TBitmapImage.Create(DonationPanel);
  1082. LoadEmbededScaledIcon(Image, '{#PayPalCardFileBase}', 100);
  1083. Image.Cursor := crHand;
  1084. Image.Parent := DonationPanel;
  1085. Image.Left := ScaleX(108);
  1086. Image.Top := P2;
  1087. Image.Hint := CustomMessage('AboutDonations');
  1088. Image.ShowHint := True;
  1089. Image.OnClick := @AboutDonationsLinkClick;
  1090. #endif
  1091. H := GetBottom(AboutDonationCaption);
  1092. if H < Image.Height then H := Image.Height;
  1093. DonationPanel.Height := H + ScaleY(8);
  1094. #endif
  1095. WizardForm.YesRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  1096. WizardForm.NoRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  1097. UpdatePostInstallRunCheckboxes(nil);
  1098. #ifdef ImagesDir
  1099. // Text does not scale as quick as with DPI,
  1100. // so the icon may overlap the labels. Shift them.
  1101. P := WizardForm.SelectDirBitmapImage.Width;
  1102. LoadEmbededScaledIcon(WizardForm.SelectDirBitmapImage, '{#SelectDirFileBase}', 32);
  1103. P := (WizardForm.SelectDirBitmapImage.Width - P);
  1104. // Vertical change should be the same as horizontal
  1105. WizardForm.SelectDirLabel.Left := WizardForm.SelectDirLabel.Left + P;
  1106. WizardForm.SelectDirBrowseLabel.Top := WizardForm.SelectDirBrowseLabel.Top + P;
  1107. WizardForm.DirEdit.Top := WizardForm.DirEdit.Top + P;
  1108. WizardForm.DirBrowseButton.Top := WizardForm.DirBrowseButton.Top + P;
  1109. #endif
  1110. end;
  1111. procedure RegisterPreviousData(PreviousDataKey: Integer);
  1112. var
  1113. S: string;
  1114. begin
  1115. if IsTypicalInstallation then S := 'typical'
  1116. else S := 'custom';
  1117. SetPreviousData(PreviousDataKey, '{#SetupTypeData}', S);
  1118. end;
  1119. function SaveCheckListBoxState(ListBox: TNewCheckListBox): string;
  1120. var
  1121. I: Integer;
  1122. begin
  1123. for I := 0 to ListBox.Items.Count - 1 do
  1124. begin
  1125. Result := Result + IntToStr(Integer(ListBox.State[I]));
  1126. end;
  1127. end;
  1128. procedure CurPageChanged(CurPageID: Integer);
  1129. var
  1130. Delta: Integer;
  1131. LineHeight: Integer;
  1132. LaunchCheckboxTop: Integer;
  1133. S: string;
  1134. #ifdef Sponsor
  1135. SponsorQueryUrl: string;
  1136. PreferredSponsor: string;
  1137. #endif
  1138. begin
  1139. if CurPageID = wpLicense then
  1140. begin
  1141. WizardForm.NextButton.Caption := CustomMessage('AcceptButton')
  1142. end;
  1143. if CurPageID = wpSelectDir then
  1144. begin
  1145. if InitDir = '' then
  1146. InitDir := WizardForm.DirEdit.Text;
  1147. end
  1148. else
  1149. if CurPageID = wpSelectComponents then
  1150. begin
  1151. if InitComponents = '' then
  1152. InitComponents := SaveCheckListBoxState(WizardForm.ComponentsList);
  1153. end
  1154. else
  1155. if CurPageID = wpSelectTasks then
  1156. begin
  1157. if InitTasks = '' then
  1158. InitTasks := SaveCheckListBoxState(WizardForm.TasksList);
  1159. end
  1160. else
  1161. if CurPageID = InterfacePage.ID then
  1162. begin
  1163. if InitInterface < 0 then
  1164. InitInterface := Integer(CommanderRadioButton.Checked);
  1165. end
  1166. else
  1167. if CurPageID = wpFinished then
  1168. begin
  1169. LineHeight := (WizardForm.NoRadio.Top - WizardForm.YesRadio.Top);
  1170. // Are we at the "Restart?" screen
  1171. // Note that it's not possible to get to the "finished" page more than once,
  1172. // so the code below does not expect re-entry
  1173. if IsRestartPage then
  1174. begin
  1175. if ShellExtNoRestart then
  1176. begin
  1177. Log('Hiding restart page as it''s not critical to replace the shell extension');
  1178. WizardForm.YesRadio.Visible := False;
  1179. WizardForm.NoRadio.Visible := False;
  1180. WizardForm.NoRadio.Checked := True;
  1181. S := SetupMessage(msgFinishedLabel);
  1182. StringChange(S, '[name]', 'WinSCP');
  1183. WizardForm.FinishedLabel.Caption :=
  1184. S + NewLine + NewLine +
  1185. // The additional new line is a padding for the "launch check box",
  1186. // as the same padding is there for the YesRadio too.
  1187. SetupMessage(msgClickFinish) + NewLine;
  1188. Log(WizardForm.FinishedLabel.Caption);
  1189. Delta := WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
  1190. LaunchCheckboxTop := WizardForm.YesRadio.Top + Delta;
  1191. end
  1192. else
  1193. begin
  1194. WizardForm.FinishedLabel.Caption :=
  1195. CustomMessage('FinishedRestartDragExtLabel') + NewLine;
  1196. Delta := WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
  1197. WizardForm.YesRadio.Top := WizardForm.YesRadio.Top + Delta;
  1198. WizardForm.NoRadio.Top := WizardForm.NoRadio.Top + Delta;
  1199. LaunchCheckboxTop := WizardForm.NoRadio.Top + LineHeight;
  1200. #ifdef Donations
  1201. DonationPanel.Visible := False;
  1202. #endif
  1203. end;
  1204. end
  1205. else
  1206. begin
  1207. LaunchCheckboxTop := WizardForm.RunList.Top;
  1208. end;
  1209. LaunchCheckbox.Top := LaunchCheckboxTop;
  1210. OpenGettingStartedCheckbox.Top := LaunchCheckbox.Top + LineHeight;
  1211. UpdatePostInstallRunCheckboxes(nil);
  1212. #ifdef Donations
  1213. if DonationPanel.Visible then
  1214. begin
  1215. DonationPanel.Top := GetBottom(OpenGettingStartedCheckbox) + ScaleY(12);
  1216. // Hide "about donations" if it does not fit nicely
  1217. // (happens on "long" languages, as German)
  1218. // Should not happen anymore with "modern" style of IS6.
  1219. if (DonationPanel.Top + GetBottom(AboutDonationCaption)) >
  1220. (WizardForm.FinishedPage.Height - ScaleY(8)) then
  1221. begin
  1222. AboutDonationCaption.Visible := False;
  1223. end;
  1224. end;
  1225. #endif
  1226. end
  1227. else
  1228. if CurPageID = SetupTypePage.ID then
  1229. begin
  1230. Log('License accepted');
  1231. LicenseAccepted := True;
  1232. #ifdef Sponsor
  1233. if VarIsEmpty(SponsorReq) then
  1234. begin
  1235. if WizardSilent or CmdLineParamExists('/NoSponsor') then
  1236. begin
  1237. Log('Skipping sponsor query request');
  1238. SponsorStatus := 'N';
  1239. end
  1240. else
  1241. begin
  1242. SponsorPage :=
  1243. CreateCustomPage(wpInstalling, 'Release sponsor', 'Please read a message from the sponsor of this release.');
  1244. SponsorQueryUrl :=
  1245. '{#WebReport}?' +
  1246. Format('mode=sponsorrequest&ver=%s&lang=%s&prevver=%s&scale=%d&images=%s', [
  1247. '{#VersionOnly}', ActiveLanguage, PrevVersion, GetScalingFactor, '{#SponsorImages}']);
  1248. PreferredSponsor := ExpandConstant('{param:Sponsor}');
  1249. if PreferredSponsor <> '' then
  1250. begin
  1251. SponsorQueryUrl := SponsorQueryUrl + Format('&sponsor=%s', [PreferredSponsor]);
  1252. end;
  1253. Log('Sending sponsor query request: ' + SponsorQueryUrl);
  1254. SponsorReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  1255. SponsorReq.Open('GET', SponsorQueryUrl, True);
  1256. SponsorReq.Send('');
  1257. end;
  1258. end;
  1259. #endif
  1260. end
  1261. else
  1262. if CurPageID = wpPreparing then
  1263. begin
  1264. // Are we at the "Restart applications?" screen.
  1265. // If PreparingMemo is hidden, it's "installation/removal was not completed" screen
  1266. if IsRestartingApplicationsPage then
  1267. begin
  1268. S := CustomMessage('ApplicationsFoundDragExt');
  1269. if IsAdminInstallMode then
  1270. S := S + NewLine + CustomMessage('ApplicationsFoundRestart');
  1271. WizardForm.PreparingLabel.Caption := S;
  1272. WizardForm.IncTopDecHeight(
  1273. WizardForm.PreparingMemo, WizardForm.AdjustLabelHeight(WizardForm.PreparingLabel));
  1274. if not IsAdminInstallMode then
  1275. begin
  1276. Log('Not automatically choosing not to restart applications as installer is not running in admin mode, so delaying replacement after Windows restart is not possible');
  1277. end
  1278. else
  1279. if not ShellExtNoRestart then
  1280. begin
  1281. Log('Not automatically choosing not to restart applications as shell extension upgrade is critical');
  1282. end
  1283. else
  1284. begin
  1285. Log('Automatically choosing not to restart applications as shell extension upgrade is not critical (or there''s even other reason the restart is needed) and installer is running in admin mode, so replacement after Windows restart should be possible and enough');
  1286. WizardForm.PreparingNoRadio.Checked := True;
  1287. WizardForm.NextButton.OnClick(WizardForm.NextButton);
  1288. end;
  1289. end;
  1290. end;
  1291. end;
  1292. function AskedRestart: Boolean;
  1293. begin
  1294. Result := IsRestartPage;
  1295. end;
  1296. procedure DeinitializeSetup;
  1297. var
  1298. WinHttpReq: Variant;
  1299. ReportUrl: string;
  1300. ReportData: string;
  1301. begin
  1302. // cannot send report, unless user already accepted license
  1303. // (with privacy policy)
  1304. if LicenseAccepted then
  1305. begin
  1306. Log('Preparing installation report');
  1307. ReportData := Format(
  1308. 'mode=report&installed=%d&silent=%d&ver=%s&lang=%s&prevver=%s&', [
  1309. Integer(InstallationDone), Integer(WizardSilent),
  1310. '{#VersionOnly}', ActiveLanguage,
  1311. PrevVersion]);
  1312. #ifdef Sponsor
  1313. ReportData := ReportData +
  1314. Format('sponsorstatus=%s&sponsor=%s&sponsoringclicked=%d&', [SponsorStatus, Sponsor, Integer(SponsoringClicked)]);
  1315. #endif
  1316. try
  1317. ReportUrl := '{#WebReport}?' + ReportData;
  1318. Log('Sending installation report: ' + ReportUrl);
  1319. WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  1320. WinHttpReq.Open('GET', ReportUrl, False);
  1321. WinHttpReq.Send('');
  1322. Log('Installation report send result: ' + IntToStr(WinHttpReq.Status) + ' ' + WinHttpReq.StatusText);
  1323. except
  1324. Log('Error sending installation report: ' + GetExceptionMessage);
  1325. end;
  1326. end;
  1327. end;
  1328. function GetTaskLogCmdLine(Task: string): string;
  1329. var
  1330. LogPath: string;
  1331. Ext: string;
  1332. begin
  1333. if CmdLineParamExists('/LogTasks') then
  1334. begin
  1335. LogPath := Trim(ExpandConstant('{param:Log}'));
  1336. if LogPath <> '' then
  1337. begin
  1338. Ext := ExtractFileExt(LogPath);
  1339. LogPath := Copy(LogPath, 1, Length(LogPath) - Length(Ext)) + '.' + Task + Ext;
  1340. Result := ' /AppLog="' + LogPath + '"';
  1341. end;
  1342. end;
  1343. end;
  1344. procedure ExecApp(Params: string; ShowCmd: Integer; Wait: TExecWait; Task: string);
  1345. var
  1346. Path: string;
  1347. ErrorCode: Integer;
  1348. begin
  1349. Path := ExpandConstant('{app}\{#MainFileName}');
  1350. Params := Params + GetTaskLogCmdLine(Task);
  1351. ExecAsOriginalUser(Path, Params, '', ShowCmd, Wait, ErrorCode)
  1352. end;
  1353. procedure OpenBrowserGettingStarted;
  1354. var
  1355. WebGettingStarted: string;
  1356. begin
  1357. WebGettingStarted :=
  1358. ExpandConstant('{#WebGettingStarted}') + PrevVersion +
  1359. '&automatic=' + IntToStr(Integer(AutomaticUpdate));
  1360. Log('Opening getting started page: ' + WebGettingStarted);
  1361. OpenBrowser(WebGettingStarted);
  1362. end;
  1363. procedure CurStepChanged(CurStep: TSetupStep);
  1364. var
  1365. ShowCmd: Integer;
  1366. OpenGettingStarted: Boolean;
  1367. UsageData: string;
  1368. CanPostInstallRuns: Boolean;
  1369. Installations: Cardinal;
  1370. begin
  1371. if CurStep = ssPostInstall then
  1372. begin
  1373. Log('Post install');
  1374. InstallationDone := True;
  1375. end
  1376. else
  1377. if CurStep = ssDone then
  1378. begin
  1379. Log('Done');
  1380. // bug in InnoSetup causes it using ssDone even when
  1381. // setup failed because machine was not restarted to complete previous
  1382. // installation. double check that ssPostInstall was called
  1383. if InstallationDone then
  1384. begin
  1385. CanPostInstallRuns := (not WizardSilent) and (not WillRestart);
  1386. OpenGettingStarted :=
  1387. OpenGettingStartedCheckbox.Enabled and
  1388. OpenGettingStartedCheckbox.Checked;
  1389. UsageData := '/Usage=';
  1390. // old style counter
  1391. UsageData := UsageData + Format('TypicalInstallation:%d,', [Integer(IsTypicalInstallation)]);
  1392. UsageData := UsageData + 'InstallationsUser+,InstallationParentProcess@,';
  1393. Installations := 0; // default, if the counter does not exist
  1394. RegQueryDWordValue(HKEY_LOCAL_MACHINE, '{#RegistryKey}', 'Installations', Installations);
  1395. Inc(Installations);
  1396. if not RegWriteDWordValue(HKEY_LOCAL_MACHINE, '{#RegistryKey}', 'Installations', Installations) then
  1397. begin
  1398. Log('Cannot increment administrator installations counter, probably a non-elevated installation');
  1399. end;
  1400. // new style counters
  1401. if not Upgrade then
  1402. begin
  1403. if IsTypicalInstallation then
  1404. UsageData := UsageData + 'InstallationsFirstTypical+,'
  1405. else
  1406. UsageData := UsageData + 'InstallationsFirstCustom+,';
  1407. end
  1408. else
  1409. begin
  1410. if AutomaticUpdate then
  1411. UsageData := UsageData + 'InstallationsUpgradeAutomatic+,'
  1412. else if IsTypicalInstallation then
  1413. UsageData := UsageData + 'InstallationsUpgradeTypical+,'
  1414. else
  1415. UsageData := UsageData + 'InstallationsUpgradeCustom+,';
  1416. end;
  1417. UsageData := UsageData + Format('LastInstallationAutomaticUpgrade:%d,', [Integer(AutomaticUpdate)]);
  1418. if (InitDir <> '') and (InitDir <> WizardForm.DirEdit.Text) then
  1419. UsageData := UsageData + 'InstallationsCustomDir+,';
  1420. if (InitComponents <> '') and (InitComponents <> SaveCheckListBoxState(WizardForm.ComponentsList)) then
  1421. UsageData := UsageData + 'InstallationsCustomComponents+,';
  1422. if (InitTasks <> '') and (InitTasks <> SaveCheckListBoxState(WizardForm.TasksList)) then
  1423. UsageData := UsageData + 'InstallationsCustomTasks+,';
  1424. if (InitInterface >= 0) and (InitInterface <> Integer(CommanderRadioButton.Checked)) then
  1425. UsageData := UsageData + 'InstallationsCustomInterface+,';
  1426. if CanPostInstallRuns and OpenGettingStarted then
  1427. UsageData := UsageData + 'InstallationsGettingStarted+,';
  1428. if CanPostInstallRuns and LaunchCheckbox.Checked then
  1429. UsageData := UsageData + 'InstallationsLaunch+,';
  1430. if WizardSilent then
  1431. UsageData := UsageData + 'InstallationsSilent+,';
  1432. if AskedRestart then
  1433. UsageData := UsageData + 'InstallationsNeedRestart+,';
  1434. if WillRestart then
  1435. UsageData := UsageData + 'InstallationsRestart+,';
  1436. if Donated then
  1437. UsageData := UsageData + 'InstallationsDonate+,';
  1438. if not IsAdminInstallMode then
  1439. UsageData := UsageData + 'InstallationsNonElevated+,';
  1440. // have to do this before running WinSCP GUI instance below,
  1441. // otherwise it loads the empty/previous counters and overwrites our changes,
  1442. // when it's closed
  1443. Log('Recording installer usage statistics: ' + UsageData);
  1444. // make sure we write the counters using the "normal" account
  1445. // (the account that will be used to report the counters)
  1446. ExecApp(UsageData, SW_HIDE, ewWaitUntilTerminated, 'Usage');
  1447. if AutomaticUpdate then
  1448. begin
  1449. Log('Launching WinSCP after automatic update');
  1450. ExecApp('', SW_SHOWNORMAL, ewNoWait, 'AutomaticUpdateRun');
  1451. if CmdLineParamExists('/OpenGettingStarted') then
  1452. begin
  1453. OpenBrowserGettingStarted;
  1454. end;
  1455. end
  1456. else
  1457. if CanPostInstallRuns then
  1458. begin
  1459. if OpenGettingStarted then
  1460. begin
  1461. OpenBrowserGettingStarted;
  1462. end;
  1463. if LaunchCheckbox.Checked then
  1464. begin
  1465. if OpenGettingStarted then
  1466. begin
  1467. Log('Will launch WinSCP minimized');
  1468. ShowCmd := SW_SHOWMINIMIZED
  1469. end
  1470. else
  1471. begin
  1472. ShowCmd := SW_SHOWNORMAL;
  1473. end;
  1474. Log('Launching WinSCP');
  1475. ExecApp('', ShowCmd, ewNoWait, 'Run');
  1476. end;
  1477. end;
  1478. end;
  1479. end;
  1480. end;
  1481. #ifdef Sponsor
  1482. function CryptStringToBinary(
  1483. sz: string; cch: LongWord; flags: LongWord; binary: string; var size: LongWord;
  1484. skip: LongWord; flagsused: LongWord): Integer;
  1485. external '[email protected] stdcall';
  1486. const
  1487. CRYPT_STRING_HEX = $04;
  1488. SHCONTCH_NOPROGRESSBOX = 4;
  1489. SHCONTCH_RESPONDYESTOALL = 16;
  1490. var
  1491. ShowSponsor: Integer;
  1492. SponsoringLinkLabel, SponsorLinkLabel: TLabel;
  1493. SponsorImage: TBitmapImage;
  1494. procedure SponsorImageClick(Sender: TObject);
  1495. begin
  1496. SponsorStatus := 'C';
  1497. OpenBrowser('{#WebReport}?mode=sponsor' + Format('&sponsor=%s&', [Sponsor]) + ExpandConstant('{#WebArguments}'));
  1498. end;
  1499. function GetSponsorAreaHeight: Integer;
  1500. begin
  1501. Result := SponsorPage.SurfaceHeight - SponsorLinkLabel.Height - ScaleY(12);
  1502. end;
  1503. procedure CenterSponsorImage;
  1504. begin
  1505. if (Extended(SponsorImage.Bitmap.Width) / SponsorPage.SurfaceWidth) <
  1506. (Extended(SponsorImage.Bitmap.Height) / GetSponsorAreaHeight()) then
  1507. begin
  1508. SponsorImage.Top := 0;
  1509. SponsorImage.Height := GetSponsorAreaHeight();
  1510. SponsorImage.Width :=
  1511. Trunc((Extended(SponsorImage.Bitmap.Width) / SponsorImage.Bitmap.Height) * SponsorImage.Height);
  1512. SponsorImage.Left := (SponsorPage.SurfaceWidth - SponsorImage.Width) div 2;
  1513. end
  1514. else
  1515. begin
  1516. SponsorImage.Left := 0;
  1517. SponsorImage.Width := SponsorPage.SurfaceWidth;
  1518. SponsorImage.Height :=
  1519. Trunc((Extended(SponsorImage.Bitmap.Height) / SponsorImage.Bitmap.Width) * SponsorImage.Width);
  1520. SponsorImage.Top := (GetSponsorAreaHeight() - SponsorImage.Height) div 2;
  1521. end;
  1522. SponsorLinkLabel.Left := SponsorImage.Left;
  1523. SponsorLinkLabel.Top := GetBottom(SponsorImage) + ScaleX(6);
  1524. SponsoringLinkLabel.Left := GetRight(SponsorImage) - SponsoringLinkLabel.Width;
  1525. SponsoringLinkLabel.Top := SponsorLinkLabel.Top;
  1526. end;
  1527. procedure WizardFormResize(Sender: TObject);
  1528. begin
  1529. CenterSponsorImage;
  1530. end;
  1531. function CheckSponsorReq: Boolean;
  1532. var
  1533. R, Succeeded: Integer;
  1534. Lines: TStrings;
  1535. I, P: Integer;
  1536. L, Key, Value: string;
  1537. Stream: TStream;
  1538. Buffer: string;
  1539. Size: LongWord;
  1540. ZipPath, TargetPath, ImagePath: string;
  1541. Shell, ZipFile, TargetFolder: Variant;
  1542. SponsorArea: TBitmapImage;
  1543. ImageSize: Integer;
  1544. begin
  1545. if ShowSponsor = 0 then
  1546. begin
  1547. SponsorImage := nil;
  1548. Log('Checking for response to sponsor request');
  1549. try
  1550. Succeeded := 0;
  1551. // Not testing return value, as it always returns -1 for some reason
  1552. R := SponsorReq.WaitForResponse(1, Succeeded);
  1553. if R = 0 then
  1554. begin
  1555. Log('Timed out waiting for a response to sponsor request');
  1556. SponsorStatus := 'T';
  1557. ShowSponsor := -1;
  1558. end
  1559. else
  1560. if SponsorReq.Status <> 200 then
  1561. begin
  1562. Log('Sponsor request failed with HTTP error: ' + IntToStr(SponsorReq.Status) + ' ' + SponsorReq.StatusText);
  1563. SponsorStatus := 'H';
  1564. ShowSponsor := -1;
  1565. end
  1566. else
  1567. begin
  1568. Log('Sponsor request succeeded');
  1569. if CmdLineParamExists('/SponsorArea') then
  1570. begin
  1571. SponsorArea := TBitmapImage.Create(SponsorPage);
  1572. SponsorArea.Parent := SponsorPage.Surface;
  1573. SponsorArea.Visible := CmdLineParamExists('/SponsorArea');
  1574. SponsorArea.BackColor := clTeal;
  1575. SponsorArea.Anchors := [akLeft, akTop, akRight, akBottom];
  1576. end
  1577. else
  1578. begin
  1579. SponsorArea := nil;
  1580. end;
  1581. SponsorLinkLabel := TLabel.Create(SponsorPage);
  1582. SponsorLinkLabel.Parent := SponsorPage.Surface;
  1583. SponsorLinkLabel.Caption := 'Visit release sponsor';
  1584. SponsorLinkLabel.OnClick := @SponsorImageClick;
  1585. LinkLabel(SponsorLinkLabel);
  1586. SponsoringLinkLabel := TLabel.Create(SponsorPage);
  1587. SponsoringLinkLabel.Parent := SponsorPage.Surface;
  1588. SponsoringLinkLabel.Caption := 'Become next release sponsor';
  1589. SponsoringLinkLabel.OnClick := @SponsoringLinkLabelClick;
  1590. LinkLabel(SponsoringLinkLabel);
  1591. Lines := TStringList.Create;
  1592. try
  1593. Lines.Text := SponsorReq.ResponseText;
  1594. for I := 0 to Lines.Count - 1 do
  1595. begin
  1596. L := Lines[I];
  1597. P := Pos('=', L);
  1598. if P = 0 then
  1599. begin
  1600. Log('Malformed sponsor response directive: ' + L);
  1601. SponsorStatus := 'P';
  1602. ShowSponsor := -1;
  1603. break;
  1604. end
  1605. else
  1606. begin
  1607. Key := Trim(Copy(L, 1, P - 1));
  1608. Value := Trim(Copy(L, P + 1, Length(L) - P));
  1609. if CompareText(Key, 'result') = 0 then
  1610. begin
  1611. if Value <> '-' then
  1612. begin
  1613. Log('No sponsor returned');
  1614. SponsorStatus := Copy(Value, 1, 1);
  1615. ShowSponsor := -1;
  1616. break;
  1617. end
  1618. else
  1619. begin
  1620. Log('Sponsor returned');
  1621. end;
  1622. end
  1623. else
  1624. if (CompareText(Key, 'image') = 0) or
  1625. (CompareText(Key, 'localimage') = 0) then
  1626. begin
  1627. if CompareText(Key, 'localimage') = 0 then
  1628. begin
  1629. Log(Format('Extracting embedded sponsor image (%s)', [Value]));
  1630. ExtractTemporaryFile(Value);
  1631. ImagePath := ExpandConstant('{tmp}\' + Value);
  1632. end
  1633. else
  1634. begin
  1635. Log(Format('Extracting returned sponsor image (%d bytes)', [Length(Value)]));
  1636. ZipPath := ExpandConstant('{tmp}\sponsor.zip');
  1637. Stream := TFileStream.Create(ZipPath, fmCreate);
  1638. try
  1639. SetLength(Buffer, (Length(Value) div 4) + 1);
  1640. Size := Length(Value) div 2;
  1641. if (CryptStringToBinary(Value, Length(Value), CRYPT_STRING_HEX, Buffer, Size, 0, 0) = 0) or
  1642. (Size <> Length(Value) div 2) then
  1643. begin
  1644. Log('Error decoding binary string');
  1645. SponsorStatus := 'P';
  1646. ShowSponsor := -1;
  1647. break;
  1648. end;
  1649. Stream.WriteBuffer(Buffer, Size);
  1650. finally
  1651. Stream.Free;
  1652. end;
  1653. Shell := CreateOleObject('Shell.Application');
  1654. ZipFile := Shell.NameSpace(ZipPath);
  1655. if VarIsClear(ZipFile) then
  1656. begin
  1657. RaiseException(Format('ZIP file "%s" does not exist or cannot be opened', [ZipPath]));
  1658. end
  1659. else
  1660. begin
  1661. TargetPath := ExpandConstant('{tmp}');
  1662. TargetFolder := Shell.NameSpace(TargetPath);
  1663. if VarIsClear(TargetFolder) then
  1664. begin
  1665. RaiseException(Format('Target path "%s" does not exist', [TargetPath]));
  1666. end
  1667. else
  1668. begin
  1669. TargetFolder.CopyHere(ZipFile.Items, SHCONTCH_NOPROGRESSBOX or SHCONTCH_RESPONDYESTOALL);
  1670. ImagePath := ExpandConstant('{tmp}\sponsor.bmp');
  1671. end;
  1672. end;
  1673. end;
  1674. SponsorImage := TBitmapImage.Create(SponsorPage);
  1675. SponsorImage.Parent := SponsorPage.Surface;
  1676. SponsorImage.Hint := SponsorLinkLabel.Caption;
  1677. SponsorImage.ShowHint := True;
  1678. SponsorImage.Stretch := True;
  1679. try
  1680. LoadBitmap(SponsorImage, ImagePath);
  1681. except
  1682. Log('Error loading sponsor image: ' + GetExceptionMessage);
  1683. SponsorStatus := 'I';
  1684. ShowSponsor := -1;
  1685. end;
  1686. if ShowSponsor = 0 then
  1687. begin
  1688. if Assigned(SponsorArea) then
  1689. begin
  1690. SponsorArea.Left := 0;
  1691. SponsorArea.Top := 0;
  1692. SponsorArea.Width := SponsorPage.Surface.Width;
  1693. SponsorArea.Height := GetSponsorAreaHeight();
  1694. Log(Format('Sponsor area is %dx%d', [SponsorArea.Width, SponsorArea.Height]));
  1695. end;
  1696. CenterSponsorImage;
  1697. SponsorImage.Cursor := crHand;
  1698. SponsorImage.OnClick := @SponsorImageClick;
  1699. WizardForm.OnResize := @WizardFormResize;
  1700. FileSize(ImagePath, ImageSize);
  1701. Log(Format('Sponsor image loaded (%d bytes, %dx%d) and displayed (%dx%d)', [
  1702. Integer(ImageSize), SponsorImage.Bitmap.Width, SponsorImage.Bitmap.Height,
  1703. SponsorImage.Width, SponsorImage.Height]));
  1704. end;
  1705. end
  1706. else
  1707. if CompareText(Key, 'sponsor') = 0 then
  1708. begin
  1709. Sponsor := Value;
  1710. end
  1711. else
  1712. if CompareText(Key, 'description') = 0 then
  1713. begin
  1714. SponsorPage.Description := Value;
  1715. Log('Sponsor page description: ' + Value);
  1716. end
  1717. else
  1718. if CompareText(Key, 'caption') = 0 then
  1719. begin
  1720. SponsorPage.Caption := Value;
  1721. Log('Sponsor page caption: ' + Value);
  1722. end
  1723. else
  1724. if CompareText(Key, 'sponsor_caption') = 0 then
  1725. begin
  1726. SponsorLinkLabel.Caption := Value;
  1727. Log('Sponsor link caption: ' + Value);
  1728. end
  1729. else
  1730. if CompareText(Key, 'sponsoring_caption') = 0 then
  1731. begin
  1732. if Value = '' then
  1733. begin
  1734. SponsoringLinkLabel.Visible := False;
  1735. Log('Hiding sponsoring link');
  1736. end
  1737. else
  1738. begin
  1739. SponsoringLinkLabel.Caption := Value;
  1740. Log('Sponsoring link caption: ' + Value);
  1741. end;
  1742. end
  1743. else
  1744. begin
  1745. Log('Unknown sponsor directive: ' + Key);
  1746. end;
  1747. end;
  1748. end;
  1749. finally
  1750. Lines.Free;
  1751. end;
  1752. if ShowSponsor = 0 then
  1753. begin
  1754. if SponsorImage = nil then
  1755. begin
  1756. Log('Incomplete sponsor data');
  1757. SponsorStatus := 'P';
  1758. ShowSponsor := -1;
  1759. end
  1760. else
  1761. begin
  1762. SponsorStatus := 'S';
  1763. ShowSponsor := 1;
  1764. end;
  1765. end;
  1766. end;
  1767. except
  1768. Log('Error processing response to sponsor request: ' + GetExceptionMessage);
  1769. SponsorStatus := 'E';
  1770. ShowSponsor := -1;
  1771. end;
  1772. end;
  1773. Result := (ShowSponsor > 0);
  1774. end;
  1775. #endif
  1776. function ShouldSkipPage(PageID: Integer): Boolean;
  1777. begin
  1778. Result :=
  1779. { Hide most pages during typical installation }
  1780. (IsTypicalInstallation and
  1781. ((PageID = wpSelectDir) or (PageID = wpSelectComponents) or
  1782. (PageID = wpSelectTasks) or
  1783. { Hide Interface page for upgrades only, show for fresh installs }
  1784. ((PageID = InterfacePage.ID) and Upgrade)))
  1785. #ifdef Sponsor
  1786. or
  1787. ((SponsorPage <> nil) and (PageID = SponsorPage.ID) and (not CheckSponsorReq))
  1788. #endif
  1789. ;
  1790. end;
  1791. function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
  1792. MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: string): string;
  1793. var
  1794. S: string;
  1795. S2: string;
  1796. begin
  1797. S := '';
  1798. S := S + MemoDirInfo + NewLine + NewLine;
  1799. if not Upgrade then
  1800. begin
  1801. if IsTypicalInstallation then S2 := CustomMessage('TypicalType')
  1802. else S2 := CustomMessage('CustomType');
  1803. end
  1804. else
  1805. begin
  1806. if IsTypicalInstallation then S2 := CustomMessage('TypicalUpgradeType')
  1807. else S2 := CustomMessage('CustomUpgradeType');
  1808. end;
  1809. StringChange(S2, '&', '');
  1810. S := S + SetupMessage(msgReadyMemoType) + NewLine + Space + S2 + NewLine + NewLine;
  1811. S := S + MemoComponentsInfo + NewLine + NewLine;
  1812. if Length(MemoGroupInfo) > 0 then
  1813. S := S + MemoGroupInfo + NewLine + NewLine;
  1814. if Length(MemoTasksInfo) > 0 then
  1815. S := S + MemoTasksInfo + NewLine + NewLine;
  1816. S := S + CustomMessage('UserSettingsOverview') + NewLine;
  1817. S := S + Space;
  1818. if CommanderRadioButton.Checked then S2 := CustomMessage('NortonCommanderInterfaceC')
  1819. else S2 := CustomMessage('ExplorerInterfaceC');
  1820. StringChange(S2, '&', '');
  1821. S := S + S2;
  1822. S := S + NewLine;
  1823. Result := S;
  1824. end;
  1825. function InitializeUninstall: Boolean;
  1826. begin
  1827. // let application know that we are running silent uninstall,
  1828. // this turns UninstallCleanup to noop
  1829. if UninstallSilent then
  1830. CreateMutex('WinSCPSilentUninstall');
  1831. Result := True;
  1832. end;
  1833. function HasUserPrograms: Boolean;
  1834. begin
  1835. // To avoid the installer failing when the {userprograms}
  1836. // cannot be resolved (when installing via system account or SCCM)
  1837. try
  1838. ExpandConstant('{userprograms}');
  1839. Log('Have user programs');
  1840. Result := True;
  1841. except
  1842. Log('Does not have user programs');
  1843. Result := False;
  1844. end;
  1845. end;
  1846. function GetTaskCmdLine(Param: string): string;
  1847. begin
  1848. Result := '/' + Param + GetTaskLogCmdLine(Param);
  1849. Log(Format('Command-line for %s task: %s', [Param, Result]));
  1850. end;
  1851. #expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")