You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Setup.dpr 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. {*
  2. * This application launches the dmdirc java-based installer.
  3. *
  4. * DMDirc - Open Source IRC Client
  5. * Copyright (c) 2006-2010 Chris Smith, Shane Mc Cormack, Gregory Holmes,
  6. * Michael Nixon
  7. *
  8. * Permission is hereby granted, free of charge, to any person obtaining a copy
  9. * of this software and associated documentation files (the "Software"), to deal
  10. * in the Software without restriction, including without limitation the rights
  11. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. * copies of the Software, and to permit persons to whom the Software is
  13. * furnished to do so, subject to the following conditions:
  14. *
  15. * The above copyright notice and this permission notice shall be included in
  16. * all copies or substantial portions of the Software.
  17. *
  18. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  24. * SOFTWARE.
  25. *}
  26. (* Current DMDirc windows setup flow:
  27. *
  28. * 1) Outer wrapper EXE that extracts a 7zip SFX to windows temp dir
  29. * 2) 7zip SFX unpacks
  30. * 3) Wrapper EXE starts Setup.exe (this program)
  31. * 4) Setup checks for existence of the JRE and offers to download/install it
  32. * 5) Setup starts the java portion of the DMDirc installer
  33. *)
  34. program Setup;
  35. // Resource file - icon, versioninfo, manifest
  36. {$R most.res}
  37. { ---------------------------------------------------------------------------- }
  38. {$IFDEF FPC}
  39. {$MODE Delphi}
  40. {$ENDIF}
  41. // Use this instead of {$APPTYPE XXX}
  42. // APP_XXX is the same as {$APPTYPE XXX}
  43. // Defaults to console
  44. // This is a work-around for a bug in FPC Cross Compiling to windows in delphi
  45. // mode (IsConsole is always true)
  46. {$DEFINE APP_GUI}
  47. // This block actually does the work for the above work-around
  48. {$IFDEF APP_GUI}
  49. {$APPTYPE GUI}
  50. {$ELSE}
  51. {$IFDEF APP_FS}
  52. {$APPTYPE FS}
  53. {$ELSE}
  54. {$IFDEF APP_TOOL}
  55. {$DEFINE APP_CONSOLE}
  56. {$APPTYPE TOOL}
  57. {$ELSE}
  58. {$DEFINE APP_CONSOLE}
  59. {$APPTYPE CONSOLE}
  60. {$ENDIF}
  61. {$ENDIF}
  62. {$ENDIF}
  63. { ----------------------------------------------------------------------------
  64. Debugging specific compiler directives
  65. ---------------------------------------------------------------------------- }
  66. // If defined the JRE will always be downloaded as if it didn't exist. Used for
  67. // testing the JRE download dialog.
  68. // {$DEFINE FORCEJREDOWNLOAD}
  69. uses
  70. kol, shared, Vista, Windows, SysUtils, classes, registry;
  71. const
  72. // SetupConsts holds build information for this release
  73. {$I SetupConsts.inc}
  74. // This is also part of the above IsConsole workaround.
  75. {$IFDEF APP_CONSOLE}
  76. IsConsole: boolean = true;
  77. {$ELSE}
  78. IsConsole: boolean = false;
  79. {$ENDIF}
  80. var
  81. { --------------------------------------------------------------------------
  82. KOL form objects
  83. -------------------------------------------------------------------------- }
  84. frmmain: pcontrol;
  85. progressbar, btncancel: pcontrol;
  86. label1, label2, label3, label4, labelurl, labelspeed, labelprogress: pcontrol;
  87. { --------------------------------------------------------------------------
  88. Other globals
  89. -------------------------------------------------------------------------- }
  90. terminateDownload: boolean = false;
  91. procedure InitCommonControls; stdcall; External 'comctl32.dll' name 'InitCommonControls';
  92. { ----------------------------------------------------------------------------
  93. Main form: Cancel button clicked event
  94. ---------------------------------------------------------------------------- }
  95. procedure btnCancel_Click(Dummy: Pointer; Sender: PControl);
  96. begin
  97. terminateDownload := true;
  98. end;
  99. { ----------------------------------------------------------------------------
  100. Main form: Set progress percentage to <value> and display in label <msg>
  101. ---------------------------------------------------------------------------- }
  102. procedure setProgress(value: integer; msg: string);
  103. begin
  104. ProgressBar.progress := value;
  105. labelprogress.Caption := msg;
  106. applet.processmessages;
  107. end;
  108. { ----------------------------------------------------------------------------
  109. Initialise KOL and create the main window
  110. ---------------------------------------------------------------------------- }
  111. procedure CreateMainWindow;
  112. var
  113. screenw, screenh: longint;
  114. iconhandle: thandle;
  115. begin
  116. { This call is required for common control 6 DLL to be correctly imported.
  117. Without it strange things happen on windows XP }
  118. InitCommonControls;
  119. { Load the icon to assign to our window }
  120. iconhandle := LoadIcon(hInstance, 'icon.ico');
  121. { We need the screen size to centre the window later }
  122. screenw := GetSystemMetrics(SM_CXSCREEN);
  123. screenh := GetSystemMetrics(SM_CYSCREEN);
  124. { KOL programs ideally need an Applet created }
  125. Applet := NewApplet('DMDirc Setup');
  126. Applet.Visible := true;
  127. Applet.Icon := iconhandle;
  128. { Create main form and set sane defaults. If we don't set the font here then
  129. all child objects will have a rubbish font as a holdover from Windows 3.1! }
  130. frmmain := NewForm(Applet, 'DMDirc Setup').SetClientSize(400, 184);
  131. frmmain.CreateVisible := True;
  132. frmmain.CanResize := False;
  133. frmmain.Style := frmmain.style and (not WS_MAXIMIZEBOX);
  134. frmmain.Font.FontName := 'Ms Sans Serif';
  135. frmmain.Font.FontHeight := 8;
  136. frmmain.SetPosition((screenw div 2) - (frmmain.Width div 2), (screenh div 2) - (frmmain.height div 2));
  137. frmmain.Icon := iconhandle;
  138. progressbar := NewProgressBar(frmmain).SetPosition(16, 114);
  139. progressbar.SetSize(frmmain.clientWidth - (progressbar.Left * 2), 16);
  140. progressbar.MaxProgress := 100;
  141. progressbar.Progress := 0;
  142. progressbar.Visible := true;
  143. btncancel := NewButton(frmmain, 'Cancel').SetPosition(progressbar.Left +
  144. progressbar.width - 60, progressbar.Top + progressbar.Height + 14);
  145. btncancel.SetSize(60, 24);
  146. label1 := NewLabel(frmmain, 'Downloading Java Runtime Environment').SetPosition(16, 16);
  147. label1.SetSize(frmmain.ClientWidth - 32, 16);
  148. label1.Font.FontStyle := [fsBold];
  149. label2 := NewLabel(frmmain, 'Address:').SetPosition(16, label1.top + 28);
  150. label2.SetSize(frmmain.ClientWidth - 32, 16);
  151. label3 := NewLabel(frmmain, 'Speed:').SetPosition(16, label2.top + 20);
  152. label3.SetSize(frmmain.ClientWidth - 32, 16);
  153. label4 := NewLabel(frmmain, 'Progress:').SetPosition(16, label3.top + 20);
  154. label4.SetSize(frmmain.ClientWidth - 32, 16);
  155. { BringToFront calls are needed on the following labels because the labels
  156. created earlier are as wide as the form and cover them as they are not
  157. transparent. It seems windows creates controls in a backwards order so newer
  158. controls are behind older ones. I could rearrange this order in the code but
  159. it would look messy. }
  160. labelurl := NewLabel(frmmain, '').SetPosition(70, label1.top + 28);
  161. labelurl.SetSize(frmmain.ClientWidth - 32, 16);
  162. labelurl.BringToFront;
  163. labelspeed := NewLabel(frmmain, '').SetPosition(70, label2.top + 20);
  164. labelspeed.SetSize(frmmain.ClientWidth - 32, 16);
  165. labelspeed.BringToFront;
  166. labelprogress := NewLabel(frmmain, '').SetPosition(70, label3.top + 20);
  167. labelprogress.SetSize(frmmain.ClientWidth - 32, 16);
  168. labelprogress.BringToFront;
  169. { Assign UI methods }
  170. btncancel.OnClick := TOnEvent(MakeMethod(nil, @btnCancel_Click ));
  171. { The window will not appear until the messageloop is started with Run() but
  172. this means we must yield this thread to the UI. This is unacceptable for
  173. such a simple program. Calling CreateWindow here will cause the window to
  174. appear but the message loop does not run; consequently the app must service
  175. messages by hand at a timely interval to avoid windows from marking the
  176. program as unresponsive. This is a hack but acceptable here. }
  177. { /!\ WARNING /!\ Run() can no longer be used to enter the message loop after
  178. this call or a nasty crash will occur. }
  179. applet.createwindow;
  180. end;
  181. { ----------------------------------------------------------------------------
  182. Return the size in bytes of the file specified by <name>
  183. Returns -1 on error
  184. ---------------------------------------------------------------------------- }
  185. function GetFileSizeByName(name: String): Integer;
  186. var
  187. hand: THandle;
  188. begin
  189. hand := 0;
  190. Result := 0;
  191. if FileExists(name) then begin
  192. try
  193. hand := CreateFile(PChar(name), GENERIC_READ, FILE_SHARE_WRITE or FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  194. Result := GetFileSize(hand, nil);
  195. finally
  196. try
  197. if (hand <> 0) then CloseHandle(hand);
  198. except
  199. Result := -1;
  200. end;
  201. end;
  202. end;
  203. end;
  204. {$IFNDEF VER150}
  205. { ----------------------------------------------------------------------------
  206. Return part of a string
  207. ---------------------------------------------------------------------------- }
  208. function AnsiMidStr(Source: String; Start: Integer; Count: Integer): String;
  209. begin
  210. // Not perfectly accurate, but does the job
  211. { ^ What does that mean? // Zipplet }
  212. Result := Copy(Source, Start, Count);
  213. end;
  214. {$ENDIF}
  215. { ----------------------------------------------------------------------------
  216. Downloads the JRE. Returns TRUE if the user installed it. False otherwise
  217. ---------------------------------------------------------------------------- }
  218. function downloadJRE(message: String = 'Would you like to download the java JRE?'): boolean;
  219. var
  220. ProcessInfo: TProcessInformation;
  221. processResult: Longword;
  222. url: String;
  223. dir: String;
  224. line: String;
  225. f: TextFile;
  226. bits: TStringList;
  227. match: boolean;
  228. wantedsize: double;
  229. currentsize: double;
  230. lastsize: double;
  231. i: double;
  232. c: longint;
  233. begin
  234. dir := IncludeTrailingPathDelimiter(ExtractFileDir(paramstr(0)));
  235. url := 'http://www.dmdirc.com/getjava/windows/all';
  236. Result := false;
  237. { First we will determine the approximate size of the download.
  238. In my opinion we should not do this until we have asked the user if they
  239. would like to download the JRE. Might change this later.
  240. We obtain the size by asking wget to find out. }
  241. ExecAndWait('wget.exe -o "'+dir+'wgetoutput" --spider '+url, true);
  242. { Just incase wget fails ... }
  243. if not fileexists(dir+'wgetoutput') then begin
  244. showerror('Internal error: wget returned no output.', 'DMDirc Setup');
  245. result := false;
  246. exit;
  247. end;
  248. { Parse the output and grab the approximate size }
  249. AssignFile(f, dir+'wgetoutput');
  250. Reset(f);
  251. line := '';
  252. match := false;
  253. while not Eof(f) do begin
  254. ReadLn(f, line);
  255. if length(line) > 8 then begin
  256. if copy(line, 1, 7) = 'Length:' then begin
  257. match := true;
  258. break;
  259. end;
  260. end;
  261. end;
  262. if match then begin
  263. bits := TStringList.create;
  264. try
  265. bits.Clear;
  266. bits.Delimiter := ' ';
  267. bits.DelimitedText := line;
  268. try
  269. wantedsize := strtoint(StringReplace(bits[1], ',', '', [rfReplaceAll]))
  270. except
  271. wantedsize := 0;
  272. end;
  273. { We ask the user if they wish to download the JRE }
  274. if askQuestion(message+' (Download Size: '+AnsiMidStr(bits[2], 2, length(bits[2])-2)+')', 'DMDirc Setup') then begin
  275. { Create progress window and show it }
  276. CreateMainWindow;
  277. { Get wget to start the download }
  278. ProcessInfo := Launch('wget.exe '+url+' -O jre.exe', true);
  279. labelurl.caption := url;
  280. labelspeed.caption := 'Connecting to site...';
  281. { Why is this case needed ?! }
  282. if wantedsize <= 0 then begin
  283. progressbar.progress := 50;
  284. end;
  285. getExitCodeProcess(ProcessInfo.hProcess, processResult);
  286. lastsize := 0;
  287. c := 0;
  288. i := 0;
  289. while (processResult = STILL_ACTIVE) and (not terminateDownload) do begin
  290. if wantedsize > 0 then begin
  291. currentsize := GetFileSizeByName(dir + 'jre.exe');
  292. inc(c);
  293. if (c >= 5) then begin
  294. i := (i + currentsize - lastsize) / 2;
  295. labelspeed.caption := nicesize(round(i * 2)) + '/sec';
  296. lastsize := currentsize;
  297. c := 0;
  298. end;
  299. if (currentsize > 0) then setProgress(Round((currentsize/wantedsize)*100),
  300. nicesize(currentsize) + ' of ' + nicesize(wantedsize) +
  301. ' (' + inttostr(Round((currentsize/wantedsize)*100)) + '%)');
  302. end;
  303. { We must process the message loop or the window wont respond to the user }
  304. applet.ProcessMessages;
  305. { Sleep to prevent 100% CPU usage }
  306. sleep(100);
  307. GetExitCodeProcess(ProcessInfo.hProcess, processResult);
  308. end;
  309. frmmain.visible := false;
  310. applet.visible := false;
  311. if (terminateDownload) then begin
  312. Result := false;
  313. TerminateProcess(ProcessInfo.hProcess, 0);
  314. showError('JRE Download was aborted', 'DMDirc Setup', false);
  315. end
  316. else Result := processResult = 0;
  317. if not Result then begin
  318. if not terminateDownload then begin
  319. showError('JRE Download Failed', 'DMDirc Setup', false);
  320. end
  321. else begin
  322. // If the download was cancelled by the form, this error will already
  323. // have been given.
  324. { No action needed here anymore }
  325. end;
  326. end;
  327. end;
  328. finally
  329. bits.free;
  330. end;
  331. end;
  332. end;
  333. { ----------------------------------------------------------------------------
  334. Begin the JRE download/install.
  335. ---------------------------------------------------------------------------- }
  336. function installJRE(isUpgrade: boolean): boolean;
  337. var
  338. question: String;
  339. needDownload: boolean;
  340. canContinue: boolean;
  341. begin
  342. Result := false;
  343. needDownload := not FileExists(IncludeTrailingPathDelimiter(ExtractFileDir(paramstr(0)))+'jre.exe');
  344. if needDownload then begin
  345. if not isUpgrade then question := 'Java was not detected on your machine. Would you like to download and install it now?'
  346. else question := 'The version of java detected on your machine is not compatible with DMDirc. Would you like to download and install a compatible version now?';
  347. end
  348. else begin
  349. if not isUpgrade then question := 'Java was not detected on your machine. Would you like to install it now?'
  350. else question := 'The version of java detected on your machine is not compatible with DMDirc. Would you like to install a compatible version now?';
  351. end;
  352. canContinue := true;
  353. if (needDownload) then begin
  354. canContinue := downloadJRE(question);
  355. end;
  356. if canContinue then begin
  357. // Final result of this function is the return value of installing java.
  358. if needDownload or askQuestion(question, 'DMDirc Setup') then begin
  359. showmessage('The Java installer will now run. Please follow the instructions given. '+#13#10+'The DMDirc installation will continue afterwards.', 'DMDirc Setup');
  360. Result := (ExecAndWait('jre.exe') = 0);
  361. end;
  362. end
  363. end;
  364. { ----------------------------------------------------------------------------
  365. MAIN PROGRAM
  366. ---------------------------------------------------------------------------- }
  367. var
  368. errorMessage: String;
  369. javaCommand: String = 'javaw.exe';
  370. params: String = '';
  371. dir: String = '';
  372. Reg: TRegistry;
  373. result: Integer;
  374. begin
  375. errorMessage := '';
  376. if FileExists('DMDirc.jar') then begin
  377. {$IFDEF FORCEJREDOWNLOAD}if (1 <> 0) then begin{$ELSE}if (ExecAndWait(javaCommand+' -version') <> 0) then begin{$ENDIF}
  378. if not installJRE(false) then begin
  379. showError('DMDirc setup can not continue without Java. Please install Java and try again.', 'DMDirc Setup', false, false);
  380. exit;
  381. end;
  382. end;
  383. Reg := TRegistry.Create;
  384. Reg.RootKey := HKEY_LOCAL_MACHINE;
  385. if Reg.OpenKey('SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\DMDirc', false) then begin
  386. dir := Reg.ReadString('InstallDir');
  387. if (dir <> '') then begin
  388. params := params+' --directory "'+dir+'"';
  389. end;
  390. end;
  391. Reg.CloseKey;
  392. Reg.Free;
  393. if (ReleaseNumber <> '') then begin
  394. params := params+' --release '+ReleaseNumber;
  395. end;
  396. // Check if the installer runs
  397. if (ExecAndWait(javaCommand+' -cp DMDirc.jar com.dmdirc.installer.Main --help') <> 0) then begin
  398. if not installJRE(true) then begin
  399. showError('Sorry, DMDirc setup can not continue without an updated version of java.', 'DMDirc Setup', false, false);
  400. exit;
  401. end
  402. else begin
  403. // Try again now that java is installed.
  404. result := ExecAndWait(javaCommand+' -cp DMDirc.jar com.dmdirc.installer.Main '+params);
  405. end;
  406. end
  407. else begin
  408. // Java is the right version, run the installer
  409. result := ExecAndWait(javaCommand+' -cp DMDirc.jar com.dmdirc.installer.Main '+params);
  410. end;
  411. end
  412. else begin
  413. errorMessage := errorMessage+'DMDirc.jar was not found.';
  414. errorMessage := errorMessage+#13#10;
  415. errorMessage := errorMessage+#13#10+'This is likely because of a corrupt installer build.';
  416. errorMessage := errorMessage+#13#10+'Please check http://www.dmdirc.com/ for an updated build.';
  417. showError(errorMessage, 'DMDirc Setup');
  418. end;
  419. end.