winscpsetup.iss 66 KB

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