winscpsetup.iss 53 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663
  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 "http://winscp.net/"
  7. #define WebForum WebRoot+"forum/"
  8. #define WebDocumentation WebRoot+"eng/docs/"
  9. #define WebReport WebRoot+"install.php"
  10. #define WebPuTTY "http://www.chiark.greenend.org.uk/~sgtatham/putty/"
  11. #define Year 2016
  12. #define EnglishLang "English"
  13. #define SetupTypeData "SetupType"
  14. #define InnoSetupReg "Software\Microsoft\Windows\CurrentVersion\Uninstall\" + AppId + "_is1"
  15. #define InnoSetupAppPathReg "Inno Setup: App Path"
  16. #ifndef CompletenessThreshold
  17. #define CompletenessThreshold 100
  18. #else
  19. #define CompletenessThreshold Int(CompletenessThreshold)
  20. #endif
  21. #ifndef PuttySourceDir
  22. #define PuttySourceDir "c:\Program Files\PuTTY"
  23. #endif
  24. #ifndef Status
  25. #define Status "unofficial"
  26. #endif
  27. #ifndef SourceDir
  28. #define SourceDir "..\source"
  29. #endif
  30. #ifndef BinariesDir
  31. #define BinariesDir SourceDir + "\Win32\Release"
  32. #endif
  33. #ifndef BinariesDir64
  34. #define BinariesDir64 SourceDir + "\Win64\Release"
  35. #endif
  36. #ifndef AllTranslations
  37. #define AllTranslations
  38. #endif
  39. #define TranslationDir "translations"
  40. #define ImagesDir "images"
  41. #define OutputDir "."
  42. #define TranslationFileMask "WinSCP.???"
  43. #define MainFileName "WinSCP.exe"
  44. #define MainFileSource BinariesDir+"\"+MainFileName
  45. #define ShellExtFileName "DragExt.dll"
  46. #define ShellExtFileSource BinariesDir+"\"+ShellExtFileName
  47. #define ShellExt64FileName "DragExt64.dll"
  48. #define ShellExt64FileSource BinariesDir64+"\"+ShellExt64FileName
  49. #define ConsoleFileSource BinariesDir+"\WinSCP.com"
  50. #define MapFileSource BinariesDir+"\WinSCP.map"
  51. #ifdef Donations
  52. #define PayPalCardImage "PayPalCard.bmp"
  53. #endif
  54. #define Major
  55. #define Minor
  56. #define Rev
  57. #define Build
  58. #expr ParseVersion(MainFileSource, Major, Minor, Rev, Build)
  59. #define VersionOnly Str(Major)+"."+Str(Minor)+(Rev > 0 ? "."+Str(Rev) : "")
  60. #define Version VersionOnly+(Status != "" ? " "+Status : "")
  61. #define FTag VersionOnly+(Status != "" ? "."+Status : "")
  62. #define WebArguments "ver=" +VersionOnly + "&lang={language}&utm_source=winscp&utm_medium=setup&utm_campaign=" + VersionOnly
  63. #define WebGettingStarted WebRoot + "eng/installed.php?" + WebArguments + "&prevver="
  64. #define MessagesPath(L) TranslationDir + "\" + "WinSCP." + L + ".islu"
  65. #define ExplorerFileBase "Explorer"
  66. #define CommanderFileBase "Commander"
  67. #define WizardImageFileBase "Tall"
  68. #define WizardSmallImageFileBase "Square"
  69. #define SelectDirFileBase "Opened bookmark folder-stored session folder"
  70. [Setup]
  71. AppId={#AppId}
  72. AppName=WinSCP
  73. AppPublisher=Martin Prikryl
  74. AppPublisherURL={#WebRoot}
  75. AppSupportURL={#WebForum}
  76. AppUpdatesURL={#WebRoot}eng/download.php
  77. VersionInfoCompany=Martin Prikryl
  78. VersionInfoDescription=Setup for WinSCP {#Version} (SFTP, FTP, WebDAV and SCP client)
  79. VersionInfoVersion={#Major}.{#Minor}.{#Rev}.{#Build}
  80. VersionInfoTextVersion={#Version}
  81. VersionInfoCopyright=(c) 2000-{#Year} Martin Prikryl
  82. DefaultDirName={pf}\WinSCP
  83. LicenseFile=license.setup.txt
  84. UninstallDisplayIcon={app}\WinSCP.exe
  85. OutputDir={#OutputDir}
  86. DisableStartupPrompt=yes
  87. AppVersion={#Version}
  88. AppVerName=WinSCP {#Version}
  89. OutputBaseFilename=WinSCP-{#FTag}-Setup
  90. SolidCompression=yes
  91. WizardImageFile={#ImagesDir}\{#WizardImageFileBase} 100.bmp
  92. WizardSmallImageFile={#ImagesDir}\{#WizardSmallImageFileBase} 100.bmp
  93. ShowTasksTreeLines=yes
  94. PrivilegesRequired=none
  95. ShowLanguageDialog=auto
  96. UsePreviousLanguage=yes
  97. DisableProgramGroupPage=yes
  98. MinVersion=0,5.1
  99. SetupIconFile=winscpsetup.ico
  100. DisableDirPage=no
  101. #ifdef Sign
  102. SignTool=sign $f "WinSCP Installer" http://winscp.net/eng/docs/installation
  103. #endif
  104. [Languages]
  105. ; English has to be first so that it is pre-selected
  106. ; on Setup Select Language window, when no translation matching
  107. ; Windows UI locale is available
  108. Name: {#DefaultLang}; MessagesFile: {#MessagesPath(DefaultLang)}
  109. #define FindHandle
  110. #dim Languages[200]
  111. #define LanguageCount 0
  112. #define AnyLanguageComplete 0
  113. #define LangI
  114. #sub ProcessTranslationFile
  115. #define FileName FindGetFileName(FindHandle)
  116. #define Lang Copy(FileName, Pos(".", FileName)+1)
  117. #define LangNameFull ReadIni(MessagesPath(Lang), "LangOptions", "LanguageName")
  118. #define Sep Pos(" -", LangNameFull)
  119. #if Sep > 0
  120. #define LangName Copy(LangNameFull, 1, Sep - 1)
  121. #else
  122. #define LangName LangNameFull
  123. #endif
  124. #define LangID ReadIni(MessagesPath(Lang), "LangOptions", "LanguageID")
  125. #define LangCompleteness Int(ReadIni(MessagesPath(Lang), "CustomOptions", "TranslationCompleteness"))
  126. #expr Languages[LanguageCount*4] = Lang
  127. #expr Languages[LanguageCount*4+1] = LangName
  128. #expr Languages[LanguageCount*4+2] = LangID
  129. #expr Languages[LanguageCount*4+3] = LangCompleteness
  130. #expr LanguageCount++
  131. #if LangCompleteness > CompletenessThreshold
  132. Name: {#Lang}; MessagesFile: {#MessagesPath(Lang)}
  133. #expr AnyLanguageComplete = 1
  134. #endif
  135. #endsub /* sub ProcessTranslationFile */
  136. #if FindHandle = FindFirst(TranslationDir + "\" + TranslationFileMask, 0)
  137. #define FResult 1
  138. #for {0; FResult; FResult = FindNext(FindHandle)} ProcessTranslationFile
  139. #expr FindClose(FindHandle)
  140. #endif
  141. ; Types are not used anymore, they are preserved only to let setup
  142. ; detect previous installation type and decide between typical/custom setup
  143. [Types]
  144. Name: full; Description: "full"
  145. Name: compact; Description: "compact"
  146. Name: custom; Description: "custom"; Flags: iscustom
  147. [Components]
  148. Name: main; Description: {cm:ApplicationComponent}; \
  149. Types: full custom compact; Flags: fixed
  150. Name: shellext; Description: {cm:ShellExtComponent}; \
  151. Types: full compact
  152. Name: pageant; Description: {cm:PageantComponent}; \
  153. Types: full
  154. Name: puttygen; Description: {cm:PuTTYgenComponent}; \
  155. Types: full
  156. #if AnyLanguageComplete == 1
  157. Name: transl; Description: {cm:TranslationsComponent}; \
  158. Types: full
  159. #endif
  160. [Tasks]
  161. Name: enableupdates; Description: {cm:EnableUpdates}
  162. Name: enableupdates\enablecollectusage; Description: {cm:EnableCollectUsage}
  163. ; Windows integration
  164. Name: desktopicon; Description: {cm:DesktopIconTask}
  165. Name: desktopicon\user; Description: {cm:DesktopIconUserTask}; \
  166. Flags: exclusive unchecked
  167. Name: desktopicon\common; Description: {cm:DesktopIconCommonTask}; \
  168. Flags: exclusive
  169. ; No Quick Launch on Win7
  170. Name: quicklaunchicon; Description: {cm:QuickLaunchIconTask}; \
  171. Flags: unchecked; OnlyBelowVersion: 6.1.7600
  172. Name: sendtohook; Description: {cm:SendToHookTask}
  173. Name: urlhandler; Description: {cm:RegisterAsUrlHandlers}
  174. Name: searchpath; Description: {cm:AddSearchPath}; \
  175. Flags: unchecked; Check: IsAdminLoggedOn
  176. [Icons]
  177. Name: "{commonprograms}\WinSCP"; Filename: "{app}\WinSCP.exe"; Components: main; \
  178. Comment: "{cm:ProgramComment2}"
  179. ; This is created when desktopicon task is selected
  180. Name: "{userdesktop}\WinSCP"; Filename: "{app}\WinSCP.exe"; \
  181. Tasks: desktopicon\user; Comment: "{cm:ProgramComment2}"
  182. Name: "{commondesktop}\WinSCP"; Filename: "{app}\WinSCP.exe"; \
  183. Tasks: desktopicon\common; Comment: "{cm:ProgramComment2}"
  184. ; This is created when quicklaunchicon task is selected
  185. Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\WinSCP"; \
  186. Filename: "{app}\WinSCP.exe"; Tasks: quicklaunchicon
  187. ; This is created when sendtohook task is selected
  188. Name: "{sendto}\{cm:SendToHookNew}"; Filename: "{app}\WinSCP.exe"; \
  189. Parameters: "/upload"; Tasks: sendtohook
  190. [InstallDelete]
  191. ; Remove pre-524 licence file (without .txt extension)
  192. Type: files; Name: "{app}\license"
  193. ; Remove pre-520 start menu folders
  194. Type: filesandordirs; Name: "{commonprograms}\WinSCP"
  195. Type: filesandordirs; Name: "{userprograms}\WinSCP"; Check: HasUserPrograms
  196. [Run]
  197. Filename: "{app}\WinSCP.exe"; Parameters: "/RegisterForDefaultProtocols"; \
  198. StatusMsg: {cm:RegisteringAsUrlHandlers}; Tasks: urlhandler
  199. Filename: "{app}\WinSCP.exe"; Parameters: "/AddSearchPath"; \
  200. StatusMsg: {cm:AddingSearchPath}; Tasks: searchpath
  201. Filename: "{app}\WinSCP.exe"; Parameters: "/ImportSitesIfAny"; \
  202. StatusMsg: {cm:ImportSites}; Flags: skipifsilent
  203. [UninstallDelete]
  204. ; These additional files are created by application
  205. Type: files; Name: "{app}\WinSCP.ini"
  206. Type: files; Name: "{app}\WinSCP.cgl"
  207. [Files]
  208. ; Put these to the top as we extract them on demand and
  209. ; that can take long with solid compression enabled
  210. Source: "{#ImagesDir}\{#ExplorerFileBase} *.bmp"; Flags: dontcopy
  211. Source: "{#ImagesDir}\{#CommanderFileBase} *.bmp"; Flags: dontcopy
  212. ; We do not need 100% images here, they are embedded already automatically
  213. ; by WizardImageFile and WizardSmallImageFile
  214. Source: "{#ImagesDir}\{#WizardImageFileBase} *.bmp"; Excludes: "* 100.bmp"; \
  215. Flags: dontcopy
  216. Source: "{#ImagesDir}\{#WizardSmallImageFileBase} *.bmp"; Excludes: "* 100.bmp"; \
  217. Flags: dontcopy
  218. Source: "{#ImagesDir}\{#SelectDirFileBase} *.bmp"; Flags: dontcopy
  219. #ifdef Donations
  220. Source: "{#ImagesDir}\{#PayPalCardImage}"; Flags: dontcopy
  221. #endif
  222. Source: "{#MainFileSource}"; DestDir: "{app}"; \
  223. Components: main; Flags: ignoreversion
  224. Source: "{#ConsoleFileSource}"; DestDir: "{app}"; \
  225. Components: main; Flags: ignoreversion
  226. Source: "{#MapFileSource}"; DestDir: "{app}"; \
  227. Components: main; Flags: ignoreversion
  228. Source: "license.txt"; DestDir: "{app}"; \
  229. Components: main; Flags: ignoreversion
  230. Source: "{#ShellExtFileSource}"; DestDir: "{app}"; \
  231. Components: shellext; \
  232. Flags: regserver restartreplace uninsrestartdelete; \
  233. Check: not IsWin64 and IsShellExtNewer(ExpandConstant('{app}\{#ShellExtFileName}'), '{#GetFileVersion(ShellExtFileSource)}')
  234. Source: "{#ShellExt64FileSource}"; DestDir: "{app}"; \
  235. Components: shellext; \
  236. Flags: regserver restartreplace uninsrestartdelete; \
  237. Check: IsWin64 and IsShellExtNewer(ExpandConstant('{app}\{#ShellExt64FileName}'), '{#GetFileVersion(ShellExt64FileSource)}')
  238. Source: "{#PuttySourceDir}\LICENCE"; DestDir: "{app}\PuTTY"; \
  239. Components: pageant puttygen; Flags: ignoreversion
  240. Source: "{#PuttySourceDir}\putty.hlp"; DestDir: "{app}\PuTTY"; \
  241. Components: pageant puttygen; Flags: ignoreversion
  242. Source: "{#PuttySourceDir}\pageant.exe"; DestDir: "{app}\PuTTY"; \
  243. Components: pageant; Flags: ignoreversion
  244. Source: "{#PuttySourceDir}\puttygen.exe"; DestDir: "{app}\PuTTY"; \
  245. Components: puttygen; Flags: ignoreversion
  246. [Registry]
  247. Root: HKCU; Subkey: "{#ParentRegistryKey}"; Flags: uninsdeletekeyifempty
  248. Root: HKCU; Subkey: "{#RegistryKey}"; Flags: uninsdeletekeyifempty
  249. ; Norton Commander interface
  250. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  251. ValueName: "Interface"; ValueData: 0; Check: UserSettings(1)
  252. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  253. ValueName: "DefaultInterfaceInterface"; ValueData: 0; \
  254. Check: UserSettings(1); Flags: noerror
  255. ; Explorer-like interface
  256. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  257. ValueName: "Interface"; ValueData: 1; Check: not UserSettings(1)
  258. Root: HKLM; SubKey: "{#RegistryKey}"; ValueType: dword; \
  259. ValueName: "DefaultInterfaceInterface"; ValueData: 1; \
  260. Check: not UserSettings(1); Flags: noerror
  261. ; If installer enabled ddext, let it reset the settings on uninstall,
  262. ; so the default is used on the next run
  263. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; ValueType: dword; \
  264. ValueName: "DDExtEnabled"; ValueData: 1; Components: shellext; \
  265. Flags: uninsdeletevalue
  266. ; Updates
  267. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface\Updates"; \
  268. ValueType: dword; ValueName: "Period"; ValueData: 7; \
  269. Tasks: enableupdates; Check: not UpdatesEnabled
  270. Root: HKLM; SubKey: "{#RegistryKey}"; \
  271. ValueType: dword; ValueName: "DefaultUpdatesPeriod"; ValueData: 7; \
  272. Tasks: enableupdates; Flags: noerror
  273. Root: HKLM; SubKey: "{#RegistryKey}"; \
  274. ValueType: dword; ValueName: "DefaultCollectUsage"; ValueData: 1; \
  275. Tasks: enableupdates\enablecollectusage; Flags: noerror
  276. #if AnyLanguageComplete == 1
  277. [Components]
  278. Name: transl\eng; Description: {#EnglishLang}; Types: full custom compact; \
  279. Flags: fixed
  280. #endif
  281. #sub EmitLang
  282. #if Languages[LangI*4+3] > CompletenessThreshold
  283. [Components]
  284. Name: transl\{#Languages[LangI*4]}; Description: {#Languages[LangI*4+1]}; \
  285. Types: full compact custom; Check: IsLang('{#Languages[LangI*4]}')
  286. Name: transl\{#Languages[LangI*4]}; Description: {#Languages[LangI*4+1]}; \
  287. Check: not IsLang('{#Languages[LangI*4]}')
  288. [Files]
  289. Source: "{#TranslationDir}\WinSCP.{#Languages[LangI*4]}"; DestDir: "{app}"; \
  290. Components: transl\{#Languages[LangI*4]}; Flags: ignoreversion
  291. [Registry]
  292. ; set program default language to setup language, but only if user installs it
  293. Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; \
  294. ValueType: dword; ValueName: "LocaleSafe"; ValueData: {#Languages[LangI*4+2]}; \
  295. Components: transl\{#Languages[LangI*4]}; Languages: {#Languages[LangI*4]}
  296. #endif
  297. #endsub /* sub EmitLang */
  298. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang
  299. [UninstallRun]
  300. ; Make sure no later uninstall task recreate the configuration
  301. Filename: "{app}\WinSCP.exe"; Parameters: "/UninstallCleanup"; \
  302. RunOnceId: "UninstallCleanup"
  303. Filename: "{app}\WinSCP.exe"; Parameters: "/RemoveSearchPath"; \
  304. RunOnceId: "RemoveSearchPath"
  305. Filename: "{app}\WinSCP.exe"; Parameters: "/UnregisterForProtocols"; \
  306. RunOnceId: "UnregisterForProtocols"
  307. [Code]
  308. const
  309. NewLine = #13#10;
  310. var
  311. TypicalTypeButton: TRadioButton;
  312. CustomTypeButton: TRadioButton;
  313. CommanderRadioButton: TRadioButton;
  314. ExplorerRadioButton: TRadioButton;
  315. LaunchCheckbox: TCheckbox;
  316. OpenGettingStartedCheckbox: TCheckbox;
  317. AreUpdatesEnabled: Boolean;
  318. AutomaticUpdate: Boolean;
  319. Upgrade: Boolean;
  320. MissingTranslations: string;
  321. PrevVersion: string;
  322. ShellExtNewerCacheFileName: string;
  323. ShellExtNewerCacheResult: Boolean;
  324. #ifdef Donations
  325. DonationPanel: TPanel;
  326. AboutDonationCaption: TLabel;
  327. #endif
  328. InstallationDone: Boolean;
  329. LicenseAccepted: Boolean;
  330. InitDir: string;
  331. InitComponents: string;
  332. InitTasks: string;
  333. InitInterface: Integer;
  334. Donated: Boolean;
  335. InterfacePage: TWizardPage;
  336. SetupTypePage: TWizardPage;
  337. procedure ShowMessage(Text: string);
  338. begin
  339. MsgBox(Text, mbInformation, MB_OK);
  340. end;
  341. function IsLang(Lang: string): Boolean;
  342. begin
  343. Result := (Lang = ActiveLanguage);
  344. end;
  345. function IsWinVista: Boolean;
  346. begin
  347. Result := (GetWindowsVersion >= $06000000);
  348. end;
  349. procedure CutVersionPart(var VersionString: string; var VersionPart: Word);
  350. var
  351. P: Integer;
  352. begin
  353. P := Pos('.', VersionString);
  354. if P > 0 then
  355. begin
  356. VersionPart := StrToIntDef(Copy(VersionString, 1, P - 1), 0);
  357. Delete(VersionString, 1, P);
  358. end
  359. else
  360. begin
  361. VersionPart := StrToIntDef(VersionString, 0);
  362. VersionString := '';
  363. end;
  364. end;
  365. function IsShellExtNewer(FileName: string; InstalledVersion: string): Boolean;
  366. var
  367. ExistingMS, ExistingLS: Cardinal;
  368. ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild: Cardinal;
  369. InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild: Word;
  370. begin
  371. if ShellExtNewerCacheFileName = FileName then
  372. begin
  373. if ShellExtNewerCacheResult then
  374. begin
  375. Log(Format('Allowing installation of shell extension %s as already decided', [FileName]));
  376. Result := True;
  377. end
  378. else
  379. begin
  380. Log(Format('Skipping installation of shell extension %s as already decided', [FileName]));
  381. Result := False;
  382. end;
  383. end
  384. else
  385. if not FileExists(FileName) then
  386. begin
  387. Log(Format('Shell extension %s does not exist yet, allowing installation', [FileName]));
  388. Result := True;
  389. end
  390. else
  391. if not GetVersionNumbers(FileName, ExistingMS, ExistingLS) then
  392. begin
  393. Log(Format('Cannot retrieve version of existing shell extension %s, allowing installation', [FileName]));
  394. Result := True;
  395. end
  396. else
  397. begin
  398. ExistingMajor := ExistingMS shr 16;
  399. ExistingMinor := ExistingMS and $FFFF;
  400. ExistingRev := ExistingLS shr 16;
  401. ExistingBuild := ExistingLS and $FFFF;
  402. Log(Format('Existing shell extension %s version: %d.%d.%d[.%d]', [FileName, ExistingMajor, ExistingMinor, ExistingRev, ExistingBuild]));
  403. Log(Format('Installed extension version string: %s', [InstalledVersion]));
  404. CutVersionPart(InstalledVersion, InstalledMajor);
  405. CutVersionPart(InstalledVersion, InstalledMinor);
  406. CutVersionPart(InstalledVersion, InstalledRev);
  407. CutVersionPart(InstalledVersion, InstalledBuild);
  408. Log(Format('Installed extension version: %d.%d.%d[.%d]', [InstalledMajor, InstalledMinor, InstalledRev, InstalledBuild]));
  409. if ((InstalledMajor > ExistingMajor)) or
  410. ((InstalledMajor = ExistingMajor) and (InstalledMinor > ExistingMinor)) or
  411. ((InstalledMajor = ExistingMajor) and (InstalledMinor = ExistingMinor) and (InstalledRev > ExistingRev)) then
  412. begin
  413. Log('Installed extension is newer than existing extension, allowing installation');
  414. Result := True;
  415. end
  416. else
  417. begin
  418. Log('Installed extension is same or older than existing extension, skipping installation');
  419. Result := False;
  420. end;
  421. end;
  422. ShellExtNewerCacheFileName := FileName;
  423. ShellExtNewerCacheResult := Result;
  424. end;
  425. function UpdatesEnabled: Boolean;
  426. begin
  427. Result := AreUpdatesEnabled;
  428. end;
  429. function UserSettings(Settings: Integer): Boolean;
  430. begin
  431. case Settings of
  432. 1: Result := CommanderRadioButton.Checked;
  433. else Result := False;
  434. end;
  435. end;
  436. function LanguageName(Lang: string; Unknown: string): string;
  437. begin
  438. #sub EmitLang2
  439. if Lang = '{#Languages[LangI*4]}' then Result := '{#Languages[LangI*4+1]}'
  440. else
  441. #endsub /* sub EmitLang2 */
  442. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang2
  443. Result := Unknown;
  444. end;
  445. function ContainsLanguage(Lang: string): Boolean;
  446. begin
  447. #sub EmitLang3
  448. #if Languages[LangI*4+3] > CompletenessThreshold
  449. if (Lang = '{#Languages[LangI*4]}') then Result := True
  450. else
  451. #endif
  452. #endsub /* sub EmitLang3 */
  453. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang3
  454. Result := False;
  455. end;
  456. function LanguageCompleteness(Lang: string): Integer;
  457. begin
  458. #sub EmitLang4
  459. if (Lang = '{#Languages[LangI*4]}') then
  460. begin
  461. Result := {#Languages[LangI*4+3]};
  462. end
  463. else
  464. #endsub /* sub EmitLang4 */
  465. #for {LangI = 0; LangI < LanguageCount; LangI++} EmitLang4
  466. // used also for the default language
  467. Result := 100;
  468. end;
  469. procedure OpenBrowser(Url: string);
  470. var
  471. ErrorCode: Integer;
  472. begin
  473. ShellExec('open', Url, '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
  474. end;
  475. function IsRestartingApplicationsPage: Boolean;
  476. begin
  477. Result := WizardForm.PreparingMemo.Visible;
  478. end;
  479. function IsRestartPage: Boolean;
  480. begin
  481. Result := WizardForm.YesRadio.Visible;
  482. end;
  483. procedure OpenHelp;
  484. var
  485. HelpKeyword: string;
  486. begin
  487. HelpKeyword := 'ui_installer'; // default
  488. case WizardForm.CurPageID of
  489. wpLicense:
  490. HelpKeyword := 'ui_installer_license';
  491. wpSelectDir:
  492. HelpKeyword := 'ui_installer_selectdir';
  493. wpSelectComponents:
  494. HelpKeyword := 'ui_installer_selectcomponents';
  495. wpSelectTasks:
  496. HelpKeyword := 'ui_installer_selecttasks';
  497. wpReady:
  498. HelpKeyword := 'ui_installer_ready';
  499. wpPreparing:
  500. if IsRestartingApplicationsPage then
  501. HelpKeyword := 'ui_installer_restartingapplications';
  502. wpFinished:
  503. HelpKeyword := 'ui_installer_finished';
  504. SetupTypePage.ID:
  505. HelpKeyword := 'ui_installer_setuptype';
  506. InterfacePage.ID:
  507. HelpKeyword := 'ui_installer_interface';
  508. end;
  509. OpenBrowser('{#WebDocumentation}' + HelpKeyword + '?' + ExpandConstant('{#WebArguments}'));
  510. end;
  511. procedure HelpButtonClick(Sender: TObject);
  512. begin
  513. OpenHelp;
  514. end;
  515. procedure FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
  516. begin
  517. if Key = 112 { VK_F1 } then
  518. begin
  519. OpenHelp;
  520. Key := 0;
  521. end;
  522. end;
  523. procedure CaptionClick(Sender: TObject);
  524. begin
  525. WizardForm.ActiveControl := TLabel(Sender).FocusControl;
  526. end;
  527. procedure ImageClick(Sender: TObject);
  528. begin
  529. WizardForm.ActiveControl := TWinControl(TControl(Sender).Tag);
  530. end;
  531. type
  532. TProcessTranslationEvent = procedure(Lang: string; FileName: string);
  533. procedure CollectNames(Lang: string; FileName: string);
  534. begin
  535. if Length(MissingTranslations) > 0 then
  536. MissingTranslations := MissingTranslations + ', ';
  537. MissingTranslations := MissingTranslations + LanguageName(Lang, Lang);
  538. end;
  539. procedure DeleteTranslation(Lang: string; FileName: string);
  540. begin
  541. DeleteFile(FileName);
  542. end;
  543. procedure ProcessMissingTranslations(OnProcessTranslation: TProcessTranslationEvent);
  544. var
  545. Path: string;
  546. FindRec: TFindRec;
  547. Ext: string;
  548. LExt: string;
  549. begin
  550. Path := AddBackslash(WizardDirValue);
  551. if FindFirst(Path + '{#TranslationFileMask}', FindRec) then
  552. begin
  553. try
  554. repeat
  555. if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
  556. begin
  557. Ext := Uppercase(ExtractFileExt(FindRec.Name));
  558. if Pos('.', Ext) = 1 then
  559. begin
  560. Ext := Uppercase(Copy(Ext, 2, Length(Ext) - 1));
  561. LExt := Lowercase(Ext);
  562. if (Pos('-' + Ext + '-', '-{#AllTranslations}-') > 0) and
  563. not ContainsLanguage(LExt) then
  564. OnProcessTranslation(LExt, Path + FindRec.Name);
  565. end;
  566. end;
  567. until not FindNext(FindRec);
  568. finally
  569. FindClose(FindRec);
  570. end;
  571. end;
  572. end;
  573. function WillRestart: Boolean;
  574. begin
  575. Result := WizardForm.YesRadio.Visible and WizardForm.YesRadio.Checked;
  576. end;
  577. procedure UpdatePostInstallRunCheckboxes(Sender: TObject);
  578. begin
  579. LaunchCheckbox.Enabled := not WillRestart;
  580. OpenGettingStartedCheckbox.Enabled :=
  581. LaunchCheckbox.Enabled
  582. end;
  583. procedure LinkLabel(Control: TLabel);
  584. begin
  585. Control.ParentFont := True;
  586. Control.Font.Style := Control.Font.Style + [fsUnderline];
  587. Control.Font.Color := clBlue;
  588. Control.Cursor := crHand;
  589. end;
  590. function IsTypicalInstallation: Boolean;
  591. begin
  592. Result := TypicalTypeButton.Checked;
  593. end;
  594. #ifdef Donations
  595. procedure AboutDonationsLinkClick(Sender: TObject);
  596. begin
  597. OpenBrowser('{#WebRoot}eng/donate.php?' + ExpandConstant('{#WebArguments}'));
  598. Donated := true;
  599. end;
  600. procedure DonateLinkClick(Sender: TObject);
  601. var
  602. Control: TControl;
  603. begin
  604. Control := TControl(Sender);
  605. OpenBrowser('{#WebRoot}eng/donate.php?amount=' + IntToStr(Control.Tag) + '&currency=' + CustomMessage('Currency') + '&' + ExpandConstant('{#WebArguments}'));
  606. Donated := true;
  607. end;
  608. procedure CreateDonateLink(Amount: Integer; Row: Integer; Top: Integer);
  609. var
  610. Caption: TLabel;
  611. begin
  612. Caption := TLabel.Create(DonationPanel);
  613. Caption.Left := 0;
  614. Caption.Top := Top + Row * ScaleY(16);
  615. Caption.Tag := Amount;
  616. Caption.Parent := DonationPanel;
  617. Caption.Caption := Format(CustomMessage('Donate'), ['$' + IntToStr(Amount)]);
  618. Caption.OnClick := @DonateLinkClick;
  619. LinkLabel(Caption);
  620. end;
  621. #endif
  622. const
  623. fsSurface = 0;
  624. procedure LoadEmbededBitmap(Image: TBitmapImage; Name: string; BackgroundColor: TColor);
  625. var
  626. FileName: string;
  627. Bitmap: TAlphaBitmap;
  628. begin
  629. ExtractTemporaryFile(Name);
  630. Bitmap := TAlphaBitmap.Create();
  631. Bitmap.AlphaFormat := afDefined;
  632. FileName := ExpandConstant('{tmp}\' + Name);
  633. Bitmap.LoadFromFile(FileName);
  634. // we won't need this anymore
  635. DeleteFile(FileName);
  636. Image.Bitmap := Bitmap;
  637. Bitmap.Free;
  638. Image.BackColor := BackgroundColor;
  639. end;
  640. function GetScalingFactor: Integer;
  641. begin
  642. if WizardForm.Font.PixelsPerInch >= 192 then Result := 200
  643. else
  644. if WizardForm.Font.PixelsPerInch >= 144 then Result := 150
  645. else
  646. if WizardForm.Font.PixelsPerInch >= 120 then Result := 125
  647. else Result := 100;
  648. end;
  649. procedure LoadEmbededScaledBitmap(Image: TBitmapImage; NameBase: string; SizeBase: Integer; BackgroundColor: TColor);
  650. var
  651. Name: String;
  652. begin
  653. Name := Format('%s %d.bmp', [NameBase, SizeBase * GetScalingFactor div 100]);
  654. LoadEmbededBitmap(Image, Name, BackgroundColor);
  655. end;
  656. procedure LoadEmbededScaledIcon(Image: TBitmapImage; NameBase: string; SizeBase: Integer; BackgroundColor: TColor);
  657. begin
  658. LoadEmbededScaledBitmap(Image, NameBase, SizeBase, BackgroundColor);
  659. Image.AutoSize := True;
  660. end;
  661. // WORKAROUND
  662. // Checkboxes and Radio buttons created on runtime do
  663. // not scale their height automatically
  664. procedure ScaleFixedHeightControl(Control: TButtonControl);
  665. begin
  666. Control.Height := ScaleY(Control.Height);
  667. end;
  668. function GetBottom(Control: TControl): Integer;
  669. begin
  670. Result := Control.Top + Control.Height;
  671. end;
  672. function CmdLineParamExists(const Value: string): Boolean;
  673. var
  674. I: Integer;
  675. begin
  676. Result := False;
  677. for I := 1 to ParamCount do
  678. begin
  679. if CompareText(ParamStr(I), Value) = 0 then
  680. begin
  681. Result := True;
  682. Exit;
  683. end;
  684. end;
  685. end;
  686. function InitializeSetup: Boolean;
  687. var
  688. WaitInterval: Integer;
  689. Wait: Integer;
  690. begin
  691. AutomaticUpdate := CmdLineParamExists('/AutomaticUpdate');
  692. if AutomaticUpdate then
  693. begin
  694. Log('Automatic update');
  695. Wait := 10000;
  696. end
  697. else
  698. begin
  699. Wait := 0;
  700. end;
  701. WaitInterval := 250;
  702. while (Wait > 0) and CheckForMutexes('{#AppMutex}') do
  703. begin
  704. Log('Application is still running, waiting');
  705. Sleep(WaitInterval);
  706. Wait := Wait - WaitInterval;
  707. end;
  708. while CheckForMutexes('{#AppMutex}') do
  709. begin
  710. if MsgBox(
  711. FmtMessage(SetupMessage(msgSetupAppRunningError), ['WinSCP']),
  712. mbError, MB_OKCANCEL) <> IDOK then
  713. begin
  714. Abort;
  715. end;
  716. end;
  717. Result := True;
  718. end;
  719. function IsElevated: Boolean;
  720. begin
  721. Result := IsAdminLoggedOn or IsPowerUserLoggedOn;
  722. end;
  723. function HaveWriteAccessToApp: Boolean;
  724. var
  725. FileName: string;
  726. begin
  727. FileName := AddBackslash(WizardDirValue) + 'writetest.tmp';
  728. Result := SaveStringToFile(FileName, 'test', False);
  729. if Result then
  730. begin
  731. Log(Format('Have write access to the last installation path [%s]', [WizardDirValue]));
  732. DeleteFile(FileName);
  733. end
  734. else
  735. begin
  736. Log(Format('Does not have write access to the last installation path [%s]', [WizardDirValue]));
  737. end;
  738. end;
  739. procedure ExitProcess(uExitCode: UINT);
  740. external '[email protected] stdcall';
  741. function ShellExecute(hwnd: HWND; lpOperation: string; lpFile: string;
  742. lpParameters: string; lpDirectory: string; nShowCmd: Integer): THandle;
  743. external '[email protected] stdcall';
  744. function Elevate: Boolean;
  745. var
  746. I: Integer;
  747. RetVal: Integer;
  748. Params: string;
  749. S: string;
  750. begin
  751. // Collect current instance parameters
  752. for I := 1 to ParamCount do
  753. begin
  754. S := ParamStr(I);
  755. // Unique log file name for the elevated instance
  756. if CompareText(Copy(S, 1, 5), '/LOG=') = 0 then
  757. begin
  758. S := S + '-elevated';
  759. end;
  760. // Do not pass our /SL5 switch
  761. if CompareText(Copy(S, 1, 5), '/SL5=') <> 0 then
  762. begin
  763. Params := Params + AddQuotes(S) + ' ';
  764. end;
  765. end;
  766. // ... and add selected language
  767. Params := Params + '/LANG=' + ActiveLanguage;
  768. Log(Format('Elevating setup with parameters [%s]', [Params]));
  769. RetVal := ShellExecute(0, 'runas', ExpandConstant('{srcexe}'), Params, '', SW_SHOW);
  770. Log(Format('Running elevated setup returned [%d]', [RetVal]));
  771. Result := (RetVal > 32);
  772. // if elevated executing of this setup succeeded, then...
  773. if Result then
  774. begin
  775. Log('Elevation succeeded');
  776. // exit this non-elevated setup instance
  777. ExitProcess(0);
  778. end
  779. else
  780. begin
  781. Log(Format('Elevation failed [%s]', [SysErrorMessage(RetVal)]));
  782. end;
  783. end;
  784. procedure InitializeWizard;
  785. var
  786. DefaultLang: Boolean;
  787. UserInterface: Cardinal;
  788. UpdatesPeriod: Cardinal;
  789. Caption: TLabel;
  790. Image: TBitmapImage;
  791. HelpButton: TButton;
  792. P: Integer;
  793. S: string;
  794. Completeness: Integer;
  795. begin
  796. InstallationDone := False;
  797. LicenseAccepted := False;
  798. InitInterface := -1;
  799. DefaultLang := (ActiveLanguage = '{#DefaultLang}');
  800. Upgrade :=
  801. RegQueryStringValue(HKLM, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S) or
  802. RegQueryStringValue(HKCU, '{#InnoSetupReg}', '{#InnoSetupAppPathReg}', S);
  803. if Upgrade then
  804. begin
  805. Log('Upgrade');
  806. end
  807. else
  808. begin
  809. Log('New installation');
  810. end;
  811. if Upgrade and GetVersionNumbersString(AddBackslash(WizardDirValue) + '{#MainFileName}', PrevVersion) and
  812. (PrevVersion[2] = '.') and (PrevVersion[4] = '.') and (PrevVersion[6] = '.') then
  813. begin
  814. PrevVersion := Copy(PrevVersion, 1, 5);
  815. end;
  816. Completeness := LanguageCompleteness(ActiveLanguage);
  817. if Completeness < 100 then
  818. begin
  819. ShowMessage(FmtMessage(CustomMessage('IncompleteTranslation'), [IntToStr(Completeness)]));
  820. end;
  821. ProcessMissingTranslations(@CollectNames);
  822. WizardForm.KeyPreview := True;
  823. WizardForm.OnKeyDown := @FormKeyDown;
  824. // to accomodate one more task
  825. WizardForm.TasksList.Height := WizardForm.TasksList.Height + ScaleY(8);
  826. // allow installation without requiring user to accept license
  827. WizardForm.LicenseAcceptedRadio.Checked := True;
  828. WizardForm.LicenseAcceptedRadio.Visible := False;
  829. WizardForm.LicenseNotAcceptedRadio.Visible := False;
  830. WizardForm.LicenseMemo.Height :=
  831. GetBottom(WizardForm.LicenseNotAcceptedRadio) -
  832. WizardForm.LicenseMemo.Top - 5;
  833. // hide installation types combo
  834. WizardForm.TypesCombo.Visible := False;
  835. WizardForm.ComponentsList.Height :=
  836. GetBottom(WizardForm.ComponentsList) -
  837. WizardForm.TypesCombo.Top;
  838. WizardForm.ComponentsList.Top := WizardForm.TypesCombo.Top;
  839. // add help button
  840. HelpButton := TButton.Create(WizardForm);
  841. HelpButton.Parent := WizardForm;
  842. HelpButton.Left :=
  843. WizardForm.ClientWidth -
  844. (WizardForm.CancelButton.Left + WizardForm.CancelButton.Width);
  845. HelpButton.Top := WizardForm.CancelButton.Top;
  846. HelpButton.Width := WizardForm.CancelButton.Width;
  847. HelpButton.Height := WizardForm.CancelButton.Height;
  848. HelpButton.Caption := CustomMessage('HelpButton');
  849. HelpButton.OnClick := @HelpButtonClick;
  850. // elevate
  851. if not IsWinVista then
  852. begin
  853. Log(Format('This version of Windows [%x] does not support elevation', [GetWindowsVersion]));
  854. end
  855. else
  856. if IsElevated then
  857. begin
  858. Log('Running elevated');
  859. end
  860. else
  861. begin
  862. Log('Running non-elevated');
  863. if Upgrade then
  864. begin
  865. if not HaveWriteAccessToApp then
  866. begin
  867. Elevate;
  868. end;
  869. end
  870. else
  871. begin
  872. if not Elevate then
  873. begin
  874. WizardForm.DirEdit.Text := ExpandConstant('{localappdata}\WinSCP');
  875. Log(Format('Falling back to local application user folder [%s]', [WizardForm.DirEdit.Text]));
  876. end;
  877. end;
  878. end;
  879. // installation type page
  880. SetupTypePage := CreateCustomPage(wpLicense,
  881. CustomMessage('SetupTypeTitle'),
  882. CustomMessage('SetupTypePrompt'));
  883. TypicalTypeButton := TRadioButton.Create(SetupTypePage);
  884. if not Upgrade then
  885. S := CustomMessage('TypicalType')
  886. else
  887. S := CustomMessage('TypicalUpgradeType');
  888. TypicalTypeButton.Caption :=
  889. FmtMessage(CustomMessage('Recommended'), [S]);
  890. // check typical install, if typical install was installed before or
  891. // when version without setup type support was installed with
  892. // "full" installation or when there were no installation before
  893. // ("full" installation is default)
  894. TypicalTypeButton.Checked :=
  895. ((GetPreviousData('{#SetupTypeData}', '') = 'typical')) or
  896. ((GetPreviousData('{#SetupTypeData}', '') = '') and
  897. (WizardSetupType(False) = 'full'));
  898. TypicalTypeButton.Left := ScaleX(4);
  899. TypicalTypeButton.Width := SetupTypePage.SurfaceWidth -
  900. TypicalTypeButton.Left;
  901. ScaleFixedHeightControl(TypicalTypeButton);
  902. TypicalTypeButton.Parent := SetupTypePage.Surface;
  903. Caption := TLabel.Create(SetupTypePage);
  904. Caption.WordWrap := True;
  905. if not Upgrade then
  906. begin
  907. if DefaultLang then
  908. S := CustomMessage('TypicalType2Eng')
  909. else
  910. S := FmtMessage(CustomMessage('TypicalType2Intl'), [CustomMessage('LocalLanguageName')]);
  911. Caption.Caption :=
  912. CustomMessage('TypicalType1') + NewLine +
  913. S + NewLine +
  914. CustomMessage('TypicalType3');
  915. end
  916. else
  917. begin
  918. if Length(MissingTranslations) > 0 then
  919. begin
  920. #if AnyLanguageComplete
  921. S := FmtMessage(CustomMessage('TypicalUpgradeTypeMissingTransl'), [MissingTranslations]);
  922. #else
  923. S := CustomMessage('TypicalUpgradeTypeNoTransl');
  924. #endif
  925. S := NewLine + S;
  926. end
  927. else S := '';
  928. Caption.Caption :=
  929. CustomMessage('TypicalUpgradeType1') + S;
  930. end;
  931. Caption.Left := ScaleX(4) + ScaleX(20);
  932. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  933. Caption.Top := GetBottom(TypicalTypeButton) + ScaleY(6);
  934. Caption.Parent := SetupTypePage.Surface;
  935. Caption.FocusControl := TypicalTypeButton;
  936. Caption.OnClick := @CaptionClick;
  937. CustomTypeButton := TRadioButton.Create(SetupTypePage);
  938. if not Upgrade then
  939. CustomTypeButton.Caption := CustomMessage('CustomType')
  940. else
  941. CustomTypeButton.Caption := CustomMessage('CustomUpgradeType');
  942. CustomTypeButton.Checked := (not TypicalTypeButton.Checked);
  943. CustomTypeButton.Left := ScaleX(4);
  944. CustomTypeButton.Width := SetupTypePage.SurfaceWidth -
  945. CustomTypeButton.Left;
  946. CustomTypeButton.Top := GetBottom(Caption) + ScaleY(10);
  947. ScaleFixedHeightControl(CustomTypeButton);
  948. CustomTypeButton.Parent := SetupTypePage.Surface;
  949. Caption := TLabel.Create(SetupTypePage);
  950. Caption.WordWrap := True;
  951. if not Upgrade then
  952. begin
  953. Caption.Caption :=
  954. CustomMessage('CustomType1');
  955. end
  956. else
  957. begin
  958. Caption.Caption :=
  959. CustomMessage('CustomUpgradeType1') + NewLine +
  960. CustomMessage('CustomUpgradeType2');
  961. end;
  962. Caption.Left := ScaleX(4) + ScaleX(20);
  963. Caption.Width := SetupTypePage.SurfaceWidth - Caption.Left;
  964. Caption.Top := GetBottom(CustomTypeButton) + ScaleY(6);
  965. Caption.Parent := SetupTypePage.Surface;
  966. Caption.FocusControl := CustomTypeButton;
  967. Caption.OnClick := @CaptionClick;
  968. // interface page
  969. InterfacePage := CreateCustomPage(wpSelectTasks,
  970. CustomMessage('UserSettingsTitle'),
  971. CustomMessage('UserSettingsPrompt'));
  972. UpdatesPeriod := 0;
  973. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface\Updates',
  974. 'Period', UpdatesPeriod);
  975. AreUpdatesEnabled := (UpdatesPeriod <> 0);
  976. UserInterface := 0; { default is commander }
  977. RegQueryDWordValue(HKCU, '{#RegistryKey}\Configuration\Interface',
  978. 'Interface', UserInterface);
  979. Caption := TLabel.Create(InterfacePage);
  980. Caption.Caption := CustomMessage('UserInterfaceStyle');
  981. Caption.Width := InterfacePage.SurfaceWidth;
  982. Caption.Parent := InterfacePage.Surface;
  983. CommanderRadioButton := TRadioButton.Create(InterfacePage);
  984. CommanderRadioButton.Caption := CustomMessage('NortonCommanderInterfaceC');
  985. CommanderRadioButton.Checked := (UserInterface = 0);
  986. CommanderRadioButton.Left := ScaleX(4);
  987. CommanderRadioButton.Width := ScaleX(116);
  988. CommanderRadioButton.Top := GetBottom(Caption) + ScaleY(6);
  989. ScaleFixedHeightControl(CommanderRadioButton);
  990. CommanderRadioButton.Parent := InterfacePage.Surface;
  991. Image := TBitmapImage.Create(InterfacePage);
  992. Image.Top := GetBottom(CommanderRadioButton) + ScaleY(6);
  993. Image.Left := CommanderRadioButton.Left + ScaleX(45);
  994. Image.Parent := InterfacePage.Surface;
  995. LoadEmbededScaledIcon(Image, '{#CommanderFileBase}', 32, InterfacePage.Surface.Color);
  996. Image.OnClick := @ImageClick;
  997. Image.Tag := Integer(CommanderRadioButton);
  998. Caption := TLabel.Create(InterfacePage);
  999. Caption.WordWrap := True;
  1000. Caption.Caption :=
  1001. CustomMessage('NortonCommanderInterface1') + NewLine +
  1002. CustomMessage('NortonCommanderInterface2') + NewLine +
  1003. CustomMessage('NortonCommanderInterface3');
  1004. Caption.Left := CommanderRadioButton.Left + CommanderRadioButton.Width;
  1005. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  1006. Caption.Top := CommanderRadioButton.Top;
  1007. Caption.Parent := InterfacePage.Surface;
  1008. Caption.FocusControl := CommanderRadioButton;
  1009. Caption.OnClick := @CaptionClick;
  1010. ExplorerRadioButton := TRadioButton.Create(InterfacePage);
  1011. ExplorerRadioButton.Caption := CustomMessage('ExplorerInterfaceC');
  1012. ExplorerRadioButton.Checked := (UserInterface <> 0);
  1013. ExplorerRadioButton.Left := ScaleX(4);
  1014. ExplorerRadioButton.Width := CommanderRadioButton.Width;
  1015. ExplorerRadioButton.Top := GetBottom(Caption) + ScaleY(10);
  1016. ScaleFixedHeightControl(ExplorerRadioButton);
  1017. ExplorerRadioButton.Parent := InterfacePage.Surface;
  1018. Image := TBitmapImage.Create(InterfacePage);
  1019. Image.Top := GetBottom(ExplorerRadioButton) + ScaleY(6);
  1020. Image.Left := ExplorerRadioButton.Left + ScaleX(45);
  1021. Image.Parent := InterfacePage.Surface;
  1022. LoadEmbededScaledIcon(Image, '{#ExplorerFileBase}', 32, InterfacePage.Surface.Color);
  1023. Image.OnClick := @ImageClick;
  1024. Image.Tag := Integer(ExplorerRadioButton);
  1025. Caption := TLabel.Create(InterfacePage);
  1026. Caption.WordWrap := True;
  1027. Caption.Caption :=
  1028. CustomMessage('ExplorerInterface1') + NewLine +
  1029. CustomMessage('ExplorerInterface2') + NewLine +
  1030. CustomMessage('ExplorerInterface3');
  1031. Caption.Left := ExplorerRadioButton.Left + ExplorerRadioButton.Width;
  1032. Caption.Width := InterfacePage.SurfaceWidth - Caption.Left;
  1033. Caption.Top := ExplorerRadioButton.Top;
  1034. Caption.Parent := InterfacePage.Surface;
  1035. Caption.FocusControl := ExplorerRadioButton;
  1036. Caption.OnClick := @CaptionClick;
  1037. // run checkbox
  1038. LaunchCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  1039. LaunchCheckbox.Caption := CustomMessage('Launch');
  1040. LaunchCheckbox.Checked := True;
  1041. LaunchCheckbox.Left := WizardForm.YesRadio.Left;
  1042. LaunchCheckbox.Width := WizardForm.YesRadio.Width;
  1043. ScaleFixedHeightControl(LaunchCheckbox);
  1044. LaunchCheckbox.Parent := WizardForm.FinishedPage;
  1045. OpenGettingStartedCheckbox := TCheckbox.Create(WizardForm.FinishedPage);
  1046. OpenGettingStartedCheckbox.Caption := CustomMessage('OpenGettingStarted');
  1047. OpenGettingStartedCheckbox.Checked := True;
  1048. OpenGettingStartedCheckbox.Left := WizardForm.YesRadio.Left;
  1049. OpenGettingStartedCheckbox.Width := WizardForm.YesRadio.Width;
  1050. ScaleFixedHeightControl(OpenGettingStartedCheckbox);
  1051. OpenGettingStartedCheckbox.Parent := WizardForm.FinishedPage;
  1052. #ifdef Donations
  1053. DonationPanel := TPanel.Create(WizardForm.FinishedPage);
  1054. DonationPanel.Left := WizardForm.YesRadio.Left;
  1055. DonationPanel.Width := WizardForm.YesRadio.Width;
  1056. DonationPanel.Parent := WizardForm.FinishedPage;
  1057. DonationPanel.BevelInner := bvNone;
  1058. DonationPanel.BevelOuter := bvNone;
  1059. DonationPanel.Color := WizardForm.FinishedPage.Color;
  1060. Caption := TLabel.Create(DonationPanel);
  1061. Caption.WordWrap := True;
  1062. Caption.Caption := CustomMessage('PleaseDonate');
  1063. Caption.Left := 0;
  1064. Caption.Top := 0;
  1065. Caption.Width := DonationPanel.Width;
  1066. Caption.Parent := DonationPanel;
  1067. P := GetBottom(Caption) + ScaleY(12);
  1068. CreateDonateLink( 9, 0, P);
  1069. CreateDonateLink(19, 1, P);
  1070. CreateDonateLink(29, 2, P);
  1071. CreateDonateLink(49, 3, P);
  1072. AboutDonationCaption := TLabel.Create(DonationPanel);
  1073. AboutDonationCaption.Left := 0;
  1074. AboutDonationCaption.Top := P + 3 * ScaleY(16) + ScaleY(24);
  1075. AboutDonationCaption.Parent := DonationPanel;
  1076. AboutDonationCaption.Caption := CustomMessage('AboutDonations');
  1077. AboutDonationCaption.OnClick := @AboutDonationsLinkClick;
  1078. LinkLabel(AboutDonationCaption);
  1079. Image := TBitmapImage.Create(DonationPanel);
  1080. LoadEmbededBitmap(Image, '{#PayPalCardImage}', DonationPanel.Color);
  1081. Image.AutoSize := True;
  1082. Image.Cursor := crHand;
  1083. Image.Parent := DonationPanel;
  1084. Image.Left := ScaleX(100);
  1085. Image.Top := P + ScaleX(8);
  1086. Image.Hint := CustomMessage('AboutDonations');
  1087. Image.ShowHint := True;
  1088. Image.OnClick := @AboutDonationsLinkClick;
  1089. DonationPanel.Height := GetBottom(AboutDonationCaption);
  1090. #endif
  1091. WizardForm.YesRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  1092. WizardForm.NoRadio.OnClick := @UpdatePostInstallRunCheckboxes;
  1093. UpdatePostInstallRunCheckboxes(nil);
  1094. // 100% images are automatically loaded by
  1095. // WizardImageFile and WizardSmallImageFile
  1096. if GetScalingFactor > 100 then
  1097. begin
  1098. LoadEmbededScaledBitmap(WizardForm.WizardBitmapImage, '{#WizardImageFileBase}', 100, 0);
  1099. LoadEmbededScaledBitmap(WizardForm.WizardBitmapImage2, '{#WizardImageFileBase}', 100, 0);
  1100. LoadEmbededScaledBitmap(WizardForm.WizardSmallBitmapImage, '{#WizardSmallImageFileBase}', 100, 0);
  1101. end;
  1102. // Text does not scale as quick as with DPI,
  1103. // so the icon may overlap the labels. Shift them.
  1104. P := WizardForm.SelectDirBitmapImage.Width;
  1105. LoadEmbededScaledIcon(WizardForm.SelectDirBitmapImage, '{#SelectDirFileBase}', 32, WizardForm.SelectDirPage.Color);
  1106. P := (WizardForm.SelectDirBitmapImage.Width - P);
  1107. // Vertical change should be the same as horizontal
  1108. WizardForm.SelectDirLabel.Left := WizardForm.SelectDirLabel.Left + P;
  1109. WizardForm.SelectDirBrowseLabel.Top := WizardForm.SelectDirBrowseLabel.Top + P;
  1110. WizardForm.DirEdit.Top := WizardForm.DirEdit.Top + P;
  1111. WizardForm.DirBrowseButton.Top := WizardForm.DirBrowseButton.Top + P;
  1112. end;
  1113. procedure RegisterPreviousData(PreviousDataKey: Integer);
  1114. var
  1115. S: string;
  1116. begin
  1117. if IsTypicalInstallation then S := 'typical'
  1118. else S := 'custom';
  1119. SetPreviousData(PreviousDataKey, '{#SetupTypeData}', S);
  1120. end;
  1121. function SaveCheckListBoxState(ListBox: TNewCheckListBox): string;
  1122. var
  1123. I: Integer;
  1124. begin
  1125. for I := 0 to ListBox.Items.Count - 1 do
  1126. begin
  1127. Result := Result + IntToStr(Integer(ListBox.State[I]));
  1128. end;
  1129. end;
  1130. procedure CurPageChanged(CurPageID: Integer);
  1131. var
  1132. Delta: Integer;
  1133. LineHeight: Integer;
  1134. LaunchCheckboxTop: Integer;
  1135. begin
  1136. if CurPageID = wpLicense then
  1137. begin
  1138. WizardForm.NextButton.Caption := CustomMessage('AcceptButton')
  1139. end;
  1140. if CurPageID = wpSelectDir then
  1141. begin
  1142. if InitDir = '' then
  1143. InitDir := WizardForm.DirEdit.Text;
  1144. end
  1145. else
  1146. if CurPageID = wpSelectComponents then
  1147. begin
  1148. if InitComponents = '' then
  1149. InitComponents := SaveCheckListBoxState(WizardForm.ComponentsList);
  1150. end
  1151. else
  1152. if CurPageID = wpSelectTasks then
  1153. begin
  1154. if InitTasks = '' then
  1155. InitTasks := SaveCheckListBoxState(WizardForm.TasksList);
  1156. end
  1157. else
  1158. if CurPageID = InterfacePage.ID then
  1159. begin
  1160. if InitInterface < 0 then
  1161. InitInterface := Integer(CommanderRadioButton.Checked);
  1162. end
  1163. else
  1164. if CurPageID = wpFinished then
  1165. begin
  1166. LineHeight := (WizardForm.NoRadio.Top - WizardForm.YesRadio.Top);
  1167. // Are we at the "Restart?" screen
  1168. if IsRestartPage then
  1169. begin
  1170. WizardForm.FinishedLabel.Caption :=
  1171. CustomMessage('FinishedRestartDragExtLabel') + NewLine;
  1172. Delta := WizardForm.AdjustLabelHeight(WizardForm.FinishedLabel);
  1173. WizardForm.YesRadio.Top := WizardForm.YesRadio.Top + Delta;
  1174. WizardForm.NoRadio.Top := WizardForm.NoRadio.Top + Delta;
  1175. LaunchCheckboxTop := WizardForm.NoRadio.Top + LineHeight;
  1176. #ifdef Donations
  1177. DonationPanel.Visible := False;
  1178. #endif
  1179. end
  1180. else
  1181. begin
  1182. LaunchCheckboxTop := WizardForm.RunList.Top;
  1183. end;
  1184. LaunchCheckbox.Top := LaunchCheckboxTop;
  1185. OpenGettingStartedCheckbox.Top := LaunchCheckbox.Top + LineHeight;
  1186. UpdatePostInstallRunCheckboxes(nil);
  1187. #ifdef Donations
  1188. if DonationPanel.Visible then
  1189. begin
  1190. DonationPanel.Top := GetBottom(OpenGettingStartedCheckbox) + ScaleY(12);
  1191. // Hide "about donations" if it does not fit nicely
  1192. // (happens on "long" languages, as German)
  1193. if (DonationPanel.Top + GetBottom(AboutDonationCaption)) >
  1194. (WizardForm.FinishedPage.Height - ScaleY(8)) then
  1195. begin
  1196. AboutDonationCaption.Visible := False;
  1197. end;
  1198. end;
  1199. #endif
  1200. end
  1201. else
  1202. if CurPageID = SetupTypePage.ID then
  1203. begin
  1204. Log('License accepted');
  1205. LicenseAccepted := True;
  1206. end
  1207. else
  1208. if CurPageID = wpPreparing then
  1209. begin
  1210. // Are we at the "Restart applications?" screen.
  1211. // If PreparingMemo is hidden, it's "installation/removal was not completed" screen
  1212. if IsRestartingApplicationsPage then
  1213. begin
  1214. WizardForm.PreparingLabel.Caption :=
  1215. CustomMessage('ApplicationsFoundDragExt');
  1216. WizardForm.IncTopDecHeight(WizardForm.PreparingMemo,
  1217. WizardForm.AdjustLabelHeight(WizardForm.PreparingLabel));
  1218. end;
  1219. end;
  1220. end;
  1221. function AskedRestart: Boolean;
  1222. begin
  1223. Result := IsRestartPage;
  1224. end;
  1225. procedure DeinitializeSetup;
  1226. var
  1227. WinHttpReq: Variant;
  1228. ReportUrl: string;
  1229. ReportData: string;
  1230. begin
  1231. // cannot send report, unless user already accepted license
  1232. // (with privacy policy)
  1233. if LicenseAccepted then
  1234. begin
  1235. Log('Preparing intallation report');
  1236. ReportData := Format(
  1237. 'installed=%d&silent=%d&ver=%s&lang=%s&prevver=%s&', [
  1238. Integer(InstallationDone), Integer(WizardSilent),
  1239. '{#VersionOnly}', ActiveLanguage,
  1240. PrevVersion]);
  1241. try
  1242. ReportUrl := '{#WebReport}?' + ReportData;
  1243. Log('Sending installation report: ' + ReportUrl);
  1244. WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
  1245. WinHttpReq.Open('GET', ReportUrl, False);
  1246. WinHttpReq.Send('');
  1247. Log('Installation report send result: ' + IntToStr(WinHttpReq.Status) + ' ' + WinHttpReq.StatusText);
  1248. except
  1249. Log('Error sending installation report: ' + GetExceptionMessage);
  1250. end;
  1251. end;
  1252. end;
  1253. procedure ExecApp(Params: string; ShowCmd: Integer; Wait: TExecWait);
  1254. var
  1255. Path: string;
  1256. ErrorCode: Integer;
  1257. begin
  1258. Path := ExpandConstant('{app}\{#MainFileName}');
  1259. ExecAsOriginalUser(Path, Params, '', ShowCmd, Wait, ErrorCode)
  1260. end;
  1261. procedure OpenBrowserGettingStarted;
  1262. var
  1263. WebGettingStarted: string;
  1264. begin
  1265. WebGettingStarted :=
  1266. ExpandConstant('{#WebGettingStarted}') + PrevVersion +
  1267. '&automatic=' + IntToStr(Integer(AutomaticUpdate));
  1268. Log('Opening getting started page: ' + WebGettingStarted);
  1269. OpenBrowser(WebGettingStarted);
  1270. end;
  1271. procedure CurStepChanged(CurStep: TSetupStep);
  1272. var
  1273. ShowCmd: Integer;
  1274. OpenGettingStarted: Boolean;
  1275. UsageData: string;
  1276. CanPostInstallRuns: Boolean;
  1277. Installations: Cardinal;
  1278. begin
  1279. if CurStep = ssPostInstall then
  1280. begin
  1281. Log('Post install');
  1282. if Length(MissingTranslations) > 0 then
  1283. begin
  1284. Log('Removing obsolete translations');
  1285. WizardForm.StatusLabel.Caption :=
  1286. CustomMessage('RemovingObsoleteTranslations');
  1287. ProcessMissingTranslations(@DeleteTranslation);
  1288. end;
  1289. InstallationDone := True;
  1290. end
  1291. else
  1292. if CurStep = ssDone then
  1293. begin
  1294. Log('Done');
  1295. // bug in InnoSetup causes it using ssDone even when
  1296. // setup failed because machine was not restarted to complete previous
  1297. // installation. double check that ssPostInstall was called
  1298. if InstallationDone then
  1299. begin
  1300. CanPostInstallRuns := (not WizardSilent) and (not WillRestart);
  1301. OpenGettingStarted :=
  1302. OpenGettingStartedCheckbox.Enabled and
  1303. OpenGettingStartedCheckbox.Checked;
  1304. UsageData := '/Usage=';
  1305. // old style counter
  1306. UsageData := UsageData + Format('TypicalInstallation:%d,', [Integer(IsTypicalInstallation)]);
  1307. UsageData := UsageData + 'InstallationsUser+,';
  1308. Installations := 0; // default, if the counter does not exist
  1309. RegQueryDWordValue(HKEY_LOCAL_MACHINE, '{#RegistryKey}', 'Installations', Installations);
  1310. Inc(Installations);
  1311. if not RegWriteDWordValue(HKEY_LOCAL_MACHINE, '{#RegistryKey}', 'Installations', Installations) then
  1312. begin
  1313. Log('Cannot increment administrator installations counter, probably a non-elevated installation');
  1314. end;
  1315. // new style counters
  1316. if not Upgrade then
  1317. begin
  1318. if IsTypicalInstallation then
  1319. UsageData := UsageData + 'InstallationsFirstTypical+,'
  1320. else
  1321. UsageData := UsageData + 'InstallationsFirstCustom+,';
  1322. end
  1323. else
  1324. begin
  1325. if AutomaticUpdate then
  1326. UsageData := UsageData + 'InstallationsUpgradeAutomatic+,'
  1327. else if IsTypicalInstallation then
  1328. UsageData := UsageData + 'InstallationsUpgradeTypical+,'
  1329. else
  1330. UsageData := UsageData + 'InstallationsUpgradeCustom+,';
  1331. end;
  1332. if (InitDir <> '') and (InitDir <> WizardForm.DirEdit.Text) then
  1333. UsageData := UsageData + 'InstallationsCustomDir+,';
  1334. if (InitComponents <> '') and (InitComponents <> SaveCheckListBoxState(WizardForm.ComponentsList)) then
  1335. UsageData := UsageData + 'InstallationsCustomComponents+,';
  1336. if (InitTasks <> '') and (InitTasks <> SaveCheckListBoxState(WizardForm.TasksList)) then
  1337. UsageData := UsageData + 'InstallationsCustomTasks+,';
  1338. if (InitInterface >= 0) and (InitInterface <> Integer(CommanderRadioButton.Checked)) then
  1339. UsageData := UsageData + 'InstallationsCustomInterface+,';
  1340. if CanPostInstallRuns and OpenGettingStarted then
  1341. UsageData := UsageData + 'InstallationsGettingStarted+,';
  1342. if CanPostInstallRuns and LaunchCheckbox.Checked then
  1343. UsageData := UsageData + 'InstallationsLaunch+,';
  1344. if WizardSilent then
  1345. UsageData := UsageData + 'InstallationsSilent+,';
  1346. if AskedRestart then
  1347. UsageData := UsageData + 'InstallationsNeedRestart+,';
  1348. if WillRestart then
  1349. UsageData := UsageData + 'InstallationsRestart+,';
  1350. if Donated then
  1351. UsageData := UsageData + 'InstallationsDonate+,';
  1352. if not IsElevated then
  1353. UsageData := UsageData + 'InstallationsNonElevated+,';
  1354. // have to do this before running WinSCP GUI instance below,
  1355. // otherwise it loads the empty/previous counters and overwrites our changes,
  1356. // when it's closed
  1357. Log('Recording installer usage statistics: ' + UsageData);
  1358. // make sure we write the counters using the "normal" account
  1359. // (the account that will be used to report the counters)
  1360. ExecApp(UsageData, SW_HIDE, ewWaitUntilTerminated);
  1361. if AutomaticUpdate then
  1362. begin
  1363. Log('Launching WinSCP after automatic update');
  1364. ExecApp('', SW_SHOWNORMAL, ewNoWait);
  1365. if CmdLineParamExists('/OpenGettingStarted') then
  1366. begin
  1367. OpenBrowserGettingStarted;
  1368. end;
  1369. end
  1370. else
  1371. if CanPostInstallRuns then
  1372. begin
  1373. if OpenGettingStarted then
  1374. begin
  1375. OpenBrowserGettingStarted;
  1376. end;
  1377. if LaunchCheckbox.Checked then
  1378. begin
  1379. if OpenGettingStarted then
  1380. begin
  1381. Log('Will launch WinSCP minimized');
  1382. ShowCmd := SW_SHOWMINIMIZED
  1383. end
  1384. else
  1385. begin
  1386. ShowCmd := SW_SHOWNORMAL;
  1387. end;
  1388. Log('Launching WinSCP');
  1389. ExecApp('', ShowCmd, ewNoWait);
  1390. end;
  1391. end;
  1392. end;
  1393. end;
  1394. end;
  1395. function ShouldSkipPage(PageID: Integer): Boolean;
  1396. begin
  1397. Result :=
  1398. { Hide most pages during typical installation }
  1399. IsTypicalInstallation and
  1400. ((PageID = wpSelectDir) or (PageID = wpSelectComponents) or
  1401. (PageID = wpSelectTasks) or
  1402. { Hide Interface page for upgrades only, show for fresh installs }
  1403. ((PageID = InterfacePage.ID) and Upgrade));
  1404. end;
  1405. function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo,
  1406. MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: string): string;
  1407. var
  1408. S: string;
  1409. S2: string;
  1410. begin
  1411. S := '';
  1412. S := S + MemoDirInfo + NewLine + NewLine;
  1413. if not Upgrade then
  1414. begin
  1415. if IsTypicalInstallation then S2 := CustomMessage('TypicalType')
  1416. else S2 := CustomMessage('CustomType');
  1417. end
  1418. else
  1419. begin
  1420. if IsTypicalInstallation then S2 := CustomMessage('TypicalUpgradeType')
  1421. else S2 := CustomMessage('CustomUpgradeType');
  1422. end;
  1423. StringChange(S2, '&', '');
  1424. S := S + SetupMessage(msgReadyMemoType) + NewLine + Space + S2 + NewLine + NewLine;
  1425. S := S + MemoComponentsInfo + NewLine + NewLine;
  1426. if Length(MemoGroupInfo) > 0 then
  1427. S := S + MemoGroupInfo + NewLine + NewLine;
  1428. if Length(MemoTasksInfo) > 0 then
  1429. S := S + MemoTasksInfo + NewLine + NewLine;
  1430. S := S + CustomMessage('UserSettingsOverview') + NewLine;
  1431. S := S + Space;
  1432. if CommanderRadioButton.Checked then S2 := CustomMessage('NortonCommanderInterfaceC')
  1433. else S2 := CustomMessage('ExplorerInterfaceC');
  1434. StringChange(S2, '&', '');
  1435. S := S + S2;
  1436. S := S + NewLine;
  1437. Result := S;
  1438. end;
  1439. function InitializeUninstall: Boolean;
  1440. begin
  1441. // let application know that we are running silent uninstall,
  1442. // this turns UninstallCleanup to noop
  1443. if UninstallSilent then
  1444. CreateMutex('WinSCPSilentUninstall');
  1445. Result := True;
  1446. end;
  1447. function HasUserPrograms: Boolean;
  1448. begin
  1449. // To avoid the installer failing when the {userprograms}
  1450. // cannot be resolved (when installing via system account or SCCM)
  1451. try
  1452. ExpandConstant('{userprograms}');
  1453. Log('Have user programs');
  1454. Result := True;
  1455. except
  1456. Log('Does not have user programs');
  1457. Result := False;
  1458. end;
  1459. end;
  1460. #expr SaveToFile(AddBackslash(SourcePath) + "Preprocessed.iss")