winscpsetup.iss 52 KB

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