winscpsetup.iss 67 KB

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