]> cygwin.com Git - cygwin-apps/setup.git/blame - install.cc
2003-01-31 Max Bowsher <maxb@ukf.net>
[cygwin-apps/setup.git] / install.cc
CommitLineData
23c9e63c
DD
1/*
2 * Copyright (c) 2000, Red Hat, Inc.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * A copy of the GNU General Public License can be found at
10 * http://www.gnu.org/
11 *
12 * Written by DJ Delorie <dj@cygnus.com>
13 *
14 */
15
8e9aa511 16/* The purpose of this file is to install all the packages selected in
23c9e63c
DD
17 the install list (in ini.h). Note that we use a separate thread to
18 maintain the progress dialog, so we avoid the complexity of
19 handling two tasks in one thread. We also create or update all the
b24c88b3 20 files in /etc/setup/\* and create the mount points. */
23c9e63c 21
b24c88b3 22#if 0
ad3c7385 23static const char *cvsid = "\n%%% $Id$\n";
b24c88b3 24#endif
8507f105 25
23c9e63c
DD
26#include "win32.h"
27#include "commctrl.h"
28
29#include <stdio.h>
904d24fe 30#include <stdlib.h>
4a83b7b0 31#include <unistd.h>
23c9e63c
DD
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <errno.h>
ab57ceaa
RC
35#include <process.h>
36
87c42361 37#include "zlib/zlib.h"
23c9e63c
DD
38
39#include "resource.h"
23c9e63c 40#include "dialog.h"
23c9e63c 41#include "geturl.h"
23c9e63c 42#include "state.h"
23c9e63c
DD
43#include "diskfull.h"
44#include "msg.h"
45#include "mount.h"
89b1a15b 46#include "log.h"
a351e48c 47#include "mount.h"
c46a33a9 48#include "filemanip.h"
b24c88b3
RC
49#include "io_stream.h"
50#include "compress.h"
51#include "compress_gz.h"
52#include "archive.h"
53#include "archive_tar.h"
8e9aa511 54#include "script.h"
4a83b7b0 55
fa0c0d10
RC
56#include "package_db.h"
57#include "package_meta.h"
bb849dbd
RC
58#include "package_version.h"
59#include "package_source.h"
fa0c0d10 60
4a83b7b0 61#include "port.h"
23c9e63c 62
ab57ceaa 63#include "threebar.h"
58ee6135
RC
64
65#include "md5.h"
66
67#include "Exception.h"
a75ed5ce 68#include "getopt++/BoolOption.h"
58ee6135 69
6625e635
RC
70using namespace std;
71
ab57ceaa 72extern ThreeBarProgressPage Progress;
23c9e63c
DD
73
74static int total_bytes = 0;
75static int total_bytes_sofar = 0;
76static int package_bytes = 0;
77
a75ed5ce
RC
78static BoolOption NoReplaceOnReboot (false, 'r', "no-replaceonreboot",
79 "Disable replacing in-use files on next "
80 "reboot.");
81
23c9e63c
DD
82static void
83init_dialog ()
84{
ab57ceaa
RC
85 Progress.SetText2 ("");
86 Progress.SetText3 ("");
23c9e63c
DD
87}
88
89static void
90progress (int bytes)
91{
ab57ceaa 92 if (package_bytes > 0)
23c9e63c 93 {
ab57ceaa 94 Progress.SetBar1 (bytes, package_bytes);
23c9e63c
DD
95 }
96
ab57ceaa 97 if (total_bytes > 0)
23c9e63c 98 {
ab57ceaa 99 Progress.SetBar2 (total_bytes_sofar + bytes, total_bytes);
23c9e63c 100 }
23c9e63c
DD
101}
102
b24c88b3 103static const char *standard_dirs[] = {
1a18aed7 104 "/bin",
904d24fe 105 "/etc",
1a18aed7 106 "/lib",
904d24fe 107 "/tmp",
4a83b7b0 108 "/usr",
1a18aed7
DD
109 "/usr/bin",
110 "/usr/lib",
ef45c299 111 "/usr/src",
4a83b7b0 112 "/usr/local",
1a18aed7
DD
113 "/usr/local/bin",
114 "/usr/local/etc",
115 "/usr/local/lib",
904d24fe 116 "/usr/tmp",
1a18aed7 117 "/var/run",
904d24fe
DD
118 "/var/tmp",
119 0
120};
121
ad3c7385
RC
122static int num_installs, num_replacements, num_uninstalls;
123static void uninstall_one (packagemeta &);
124static int replace_one (packagemeta &);
3c054baf
RC
125static int install_one_source (packagemeta &, packagesource &, String const &,
126 String const &, package_type_t);
ad3c7385 127static bool rebootneeded;
3b9077d4 128
fa0c0d10 129/* FIXME: upgrades should be a method too */
3b9077d4 130static void
cbfc4215 131uninstall_one (packagemeta & pkgm)
3b9077d4 132{
ab57ceaa 133 Progress.SetText1 ("Uninstalling...");
3c054baf 134 Progress.SetText2 (pkgm.name.cstr_oneuse());
1ac649ed 135 log (LOG_PLAIN, String("Uninstalling ") + pkgm.name);
ab57ceaa
RC
136 pkgm.uninstall ();
137 num_uninstalls++;
3b9077d4
DD
138}
139
ad3c7385
RC
140/* uninstall and install a package, preserving configuration
141 * files and the like.
142 * This method should also know about replacing in-use file.
143 * ASSUMPTIONS: pkgm is installed.
144 * pkgm has a desired package.
145 */
146static int
147replace_one (packagemeta & pkg)
148{
149 int errors = 0;
150 Progress.SetText1 ("Replacing...");
3c054baf 151 Progress.SetText2 (pkg.name.cstr_oneuse());
1ac649ed 152 log (LOG_PLAIN, String( "Replacing ") + pkg.name);
ad3c7385
RC
153 pkg.uninstall ();
154
155 errors +=
3c196821 156 install_one_source (pkg, *pkg.desired.source(), "cygfile://","/", package_binary);
ad3c7385
RC
157 if (!errors)
158 pkg.installed = pkg.desired;
159 num_replacements++;
160 return errors;
161}
42bf5b92 162
bb849dbd
RC
163
164/* install one source at a given prefix. */
3b9077d4 165static int
bb849dbd 166install_one_source (packagemeta & pkgm, packagesource & source,
3c054baf 167 String const &prefixURL, String const &prefixPath, package_type_t type)
3b9077d4
DD
168{
169 int errors = 0;
ab57ceaa 170 Progress.SetText2 (source.Base ());
7c6ef2c3 171 if (!source.Cached () || !io_stream::exists (source.Cached ()))
c46a33a9 172 {
ab57ceaa 173 note (NULL, IDS_ERR_OPEN_READ, source.Cached (), "No such file");
bb849dbd 174 return 1;
c46a33a9 175 }
bb849dbd
RC
176 io_stream *lst = 0;
177 if (type == package_binary)
c46a33a9 178 {
bb849dbd
RC
179 io_stream *tmp =
180 io_stream::
3c054baf 181 open (String ("cygfile:///etc/setup/") + pkgm.name + ".lst.gz",
bb849dbd
RC
182 "wb");
183 lst = new compress_gz (tmp, "w9");
184 if (lst->error ())
185 {
186 delete lst;
187 lst = NULL;
188 }
c46a33a9 189 }
b24c88b3 190
bb849dbd 191 package_bytes = source.size;
3b9077d4 192
bb849dbd
RC
193 char msg[64];
194 strcpy (msg, "Installing");
ab57ceaa 195 Progress.SetText1 (msg);
1ac649ed 196 log (LOG_PLAIN, String (msg) + " " + source.Cached ());
58ee6135
RC
197
198 if (source.md5.isSet())
199 {
200 // check the MD5 sum of the cached file here
201 io_stream *thefile = io_stream::open (source.Cached (), "rb");
202 if (!thefile)
203 return 0;
204 md5_state_t pns;
205 md5_init (&pns);
206
207 unsigned char buffer[16384];
208 ssize_t count;
209 while ((count = thefile->read (buffer, 16384)) > 0)
210 md5_append (&pns, buffer, count);
211 delete thefile;
212 if (count < 0)
213 throw new Exception ("__LINE__ __FILE__", (String ("IO Error reading ") + source.Cached()).cstr_oneuse(), APPERR_IO_ERROR);
214
215 md5_byte_t tempdigest[16];
216 md5_finish(&pns, tempdigest);
217 md5 tempMD5;
218 tempMD5.set (tempdigest);
219
3a8d1127 220 log (LOG_BABBLE, String ("For file ") + source.Cached() + " ini digest is " + source.md5.print() + " file digest is " + tempMD5.print());
58ee6135
RC
221
222 if (source.md5 != tempMD5)
223 throw new Exception ("__LINE__ __FILE__", (String ("Checksum failure for ") + source.Cached()).cstr_oneuse(), APPERR_CORRUPT_PACKAGE);
224 }
bb849dbd
RC
225 io_stream *tmp = io_stream::open (source.Cached (), "rb");
226 archive *thefile = 0;
227 if (tmp)
d4a4527d 228 {
bb849dbd
RC
229 io_stream *tmp2 = compress::decompress (tmp);
230 if (tmp2)
231 thefile = archive::extract (tmp2);
232 else
233 thefile = archive::extract (tmp);
3b9077d4 234 }
bb849dbd
RC
235 /* FIXME: potential leak of either *tmp or *tmp2 */
236 if (thefile)
d4a4527d 237 {
3c054baf
RC
238 String fn;
239 while ((fn = thefile->next_file_name ()).size())
bb849dbd
RC
240 {
241 if (lst)
3c054baf
RC
242 {
243 String tmp=fn + "\n";
2fa7c5a4 244 lst->write (tmp.cstr_oneuse(), tmp.size());
3c054baf
RC
245 }
246
247 String canonicalfn = prefixPath + fn;
bb849dbd 248
3c054baf
RC
249 Progress.SetText3 (canonicalfn.cstr_oneuse());
250 log (LOG_BABBLE, String("Installing file ") + prefixURL + prefixPath + fn);
6dc75764 251 if (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
bb849dbd 252 {
a75ed5ce
RC
253 if (NoReplaceOnReboot)
254 {
255 ++errors;
256 log (LOG_PLAIN, String("Not replacing in-use file ") +
257 prefixURL + prefixPath + fn);
258 }
259 else
ad3c7385 260 //extract to temp location
6dc75764 261 if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
ad3c7385 262 {
1ac649ed
RC
263 log (LOG_PLAIN,
264 String("Unable to install file ") +
265 prefixURL + prefixPath + fn);
ad3c7385
RC
266 errors++;
267 }
268 else
269 //switch Win32::OS
270 {
271 switch (Win32::OS ())
272 {
273 case Win32::Win9x:{
274 /* Get the short file names */
275 char source[MAX_PATH];
276 unsigned int len =
1ac649ed
RC
277 GetShortPathName (cygpath (String ("/") + fn +
278 ".new").cstr_oneuse(),
ad3c7385
RC
279 source, MAX_PATH);
280 if (!len || len > MAX_PATH)
281 {
3c054baf 282 log (LOG_TIMESTAMP,
ad3c7385 283 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
284 cygpath (String ("/") + fn).cstr_oneuse(),
285 cygpath (String ("/") + fn +
286 ".new").cstr_oneuse(),
ad3c7385
RC
287 GetLastError ());
288 ++errors;
289 }
290 else
291 {
292 char dest[MAX_PATH];
293 len =
1ac649ed
RC
294 GetShortPathName (cygpath (String ("/") +
295 fn).cstr_oneuse(),
296 dest, MAX_PATH);
ad3c7385
RC
297 if (!len || len > MAX_PATH)
298 {
3c054baf 299 log (LOG_TIMESTAMP,
ad3c7385 300 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
301 cygpath (String ("/") + fn).cstr_oneuse(),
302 cygpath (String ("/") + fn +
303 ".new").cstr_oneuse(),
ad3c7385
RC
304 GetLastError ());
305 ++errors;
306
307 }
308 else
309 /* trigger a replacement on reboot */
310 if (!WritePrivateProfileString
311 ("rename", dest, source, "WININIT.INI"))
312 {
3c054baf 313 log (LOG_TIMESTAMP,
ad3c7385 314 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
315 cygpath (String ("/") + fn).cstr_oneuse(),
316 cygpath (String ("/") + fn +
317 ".new").cstr_oneuse(),
ad3c7385
RC
318 GetLastError ());
319 ++errors;
320 }
321
322 }
323 }
324 break;
325 case Win32::WinNT:
326 /* XXX FIXME: prefix may not be / for in use files -
327 * although it most likely is
328 * - we need a io method to get win32 paths
329 * or to wrap this system call
330 */
1ac649ed
RC
331 if (!MoveFileEx (cygpath (String ("/") + fn +
332 ".new").cstr_oneuse(),
333 cygpath (String ("/") + fn).cstr_oneuse(),
ad3c7385
RC
334 MOVEFILE_DELAY_UNTIL_REBOOT |
335 MOVEFILE_REPLACE_EXISTING))
336 {
3c054baf 337 log (LOG_TIMESTAMP,
ad3c7385 338 "Unable to schedule reboot replacement of file %s with %s (Win32 Error %ld)",
1ac649ed
RC
339 cygpath (String ("/") + fn).cstr_oneuse(),
340 cygpath (String ("/") + fn + ".new").cstr_oneuse(),
ad3c7385
RC
341 GetLastError ());
342 ++errors;
343 }
344 else
345 rebootneeded = true;
346 break;
347 }
348 }
bb849dbd
RC
349 }
350
351 progress (tmp->tell ());
352 num_installs++;
353 }
354 delete thefile;
355
356 total_bytes_sofar += package_bytes;
d4a4527d 357 }
3b9077d4 358
90d14922 359
bb849dbd
RC
360 progress (0);
361
3c054baf 362 int df = diskfull (get_root_dir ().cstr_oneuse());
ab57ceaa 363 Progress.SetBar3 (df);
bb849dbd
RC
364
365 if (lst)
366 delete lst;
367
368 return errors;
369}
370
371/* install a package, install both the binary and source aspects if needed */
372static int
373install_one (packagemeta & pkg)
374{
375 int errors = 0;
376
3c196821 377 if (pkg.installed != pkg.desired && pkg.desired.picked())
ab57ceaa 378 {
bb849dbd 379 errors +=
3c196821 380 install_one_source (pkg, *pkg.desired.source(), "cygfile://","/",
ab57ceaa
RC
381 package_binary);
382 if (!errors)
383 pkg.installed = pkg.desired;
384 }
3c196821 385 if (pkg.desired.sourcePackage().picked())
ab57ceaa 386 errors +=
3c196821 387 install_one_source (pkg, *pkg.desired.sourcePackage().source(), "cygfile://","/usr/src/",
ab57ceaa 388 package_source);
bb849dbd
RC
389
390 /* FIXME: make a upgrade method and reinstate this */
391#if 0
1ac649ed 392 String msg;
c46a33a9 393 if (!pkg->installed)
1ac649ed 394 msg = "Installing";
c46a33a9
CF
395 else
396 {
397 int n = strcmp (pi->version, pkg->installed->version);
398 if (n < 0)
1ac649ed 399 msg = "Reverting";
c46a33a9 400 else if (n == 0)
1ac649ed 401 msg = "Reinstalling";
c46a33a9 402 else
1ac649ed 403 msg = "Upgrading";
c46a33a9
CF
404 }
405
406 switch (pkg->action)
3b9077d4 407 {
c46a33a9 408 case ACTION_PREV:
1ac649ed 409 msg += " previous version...";
3b9077d4 410 break;
c46a33a9 411 case ACTION_CURR:
1ac649ed 412 msg += "...";
3b9077d4 413 break;
c46a33a9 414 case ACTION_TEST:
1ac649ed 415 msg += " test version...";
42bf5b92 416 break;
b24c88b3
RC
417 default:
418 /* FIXME: log this somehow */
419 break;
3b9077d4 420 }
1ac649ed
RC
421 SetWindowText (ins_action, msg.cstr_oneuse());
422 log (LOG_PLAIN, msg + " " + file);
bb849dbd 423#endif
c46a33a9 424
3b9077d4
DD
425 return errors;
426}
427
72fd1d1e
CF
428static void
429check_for_old_cygwin ()
430{
431 char buf[_MAX_PATH + sizeof ("\\cygwin1.dll")];
432 if (!GetSystemDirectory (buf, sizeof (buf)))
433 return;
434 strcat (buf, "\\cygwin1.dll");
435 if (_access (buf, 0) != 0)
436 return;
437
438 char msg[sizeof (buf) + 132];
b24c88b3
RC
439 sprintf (msg,
440 "An old version of cygwin1.dll was found here:\r\n%s\r\nDelete?",
441 buf);
442 switch (MessageBox
443 (NULL, msg, "What's that doing there?",
444 MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL))
72fd1d1e
CF
445 {
446 case IDYES:
447 if (!DeleteFile (buf))
448 {
449 sprintf (msg, "Couldn't delete file %s.\r\n"
b24c88b3 450 "Is the DLL in use by another application?\r\n"
7c7034e8
RC
451 "You should delete the old version of cygwin1.dll\r\n"
452 "at your earliest convenience.", buf);
b24c88b3
RC
453 MessageBox (NULL, buf, "Couldn't delete file",
454 MB_OK | MB_ICONEXCLAMATION | MB_TASKMODAL);
72fd1d1e
CF
455 }
456 break;
457 default:
b24c88b3 458 break;
72fd1d1e
CF
459 }
460
461 return;
462}
463
ab57ceaa
RC
464static void
465do_install_thread (HINSTANCE h, HWND owner)
23c9e63c 466{
3b9077d4 467 int i;
2a1a01e0
DD
468 int errors = 0;
469
ad3c7385
RC
470 num_installs = 0, num_uninstalls = 0, num_replacements = 0;
471 rebootneeded = false;
3b9077d4 472
50225eae 473 next_dialog = IDD_DESKTOP;
23c9e63c 474
490717ef 475 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + get_root_dir ());
904d24fe 476
b24c88b3 477 for (i = 0; standard_dirs[i]; i++)
904d24fe 478 {
1ac649ed 479 String p = cygpath (standard_dirs[i]);
3c054baf 480 if (p.size())
490717ef 481 io_stream::mkpath_p (PATH_TO_DIR, String ("file://") + p);
904d24fe
DD
482 }
483
1a18aed7 484 /* Create /var/run/utmp */
b24c88b3
RC
485 io_stream *utmp = io_stream::open ("cygfile:///var/run/utmp", "wb");
486 delete utmp;
1a18aed7 487
23c9e63c
DD
488 init_dialog ();
489
490 total_bytes = 0;
491 total_bytes_sofar = 0;
492
3c054baf 493 int df = diskfull (get_root_dir ().cstr_oneuse());
ab57ceaa 494 Progress.SetBar3 (df);
d07591a3 495
0af2d779
CF
496 int istext = (root_text == IDC_ROOT_TEXT) ? 1 : 0;
497 int issystem = (root_scope == IDC_ROOT_SYSTEM) ? 1 : 0;
498
1ac649ed
RC
499 create_mount ("/", get_root_dir (), istext, issystem);
500 create_mount ("/usr/bin", cygpath ("/bin"), istext, issystem);
501 create_mount ("/usr/lib", cygpath ("/lib"), istext, issystem);
0af2d779
CF
502 set_cygdrive_flags (istext, issystem);
503
8e9aa511
RC
504 /* Let's hope people won't uninstall packages before installing [b]ash */
505 init_run_script ();
506
bb849dbd 507 packagedb db;
cfae3b8d
RC
508 for (vector <packagemeta *>::iterator i = db.packages.begin ();
509 i != db.packages.end (); ++i)
cbfc4215 510 {
cfae3b8d 511 packagemeta & pkg = **i;
ab57ceaa 512
3c196821 513 if (pkg.desired.changeRequested())
ab57ceaa 514 {
3c196821
RC
515 if (pkg.desired.picked())
516 total_bytes += pkg.desired.source()->size;
517 if (pkg.desired.sourcePackage ().picked())
518 total_bytes += pkg.desired.sourcePackage ().source()->size;
ab57ceaa 519 }
cbfc4215 520 }
bb849dbd 521
ad3c7385 522 /* start with uninstalls - remove files that new packages may replace */
cfae3b8d
RC
523 for (vector <packagemeta *>::iterator i = db.packages.begin ();
524 i != db.packages.end (); ++i)
23c9e63c 525 {
cfae3b8d 526 packagemeta & pkg = **i;
cbfc4215 527 if (pkg.installed && (!pkg.desired || pkg.desired != pkg.installed))
ad3c7385
RC
528 uninstall_one (pkg);
529 }
530
531 /* now in-place binary upgrades/reinstalls, as these may remove fils
532 * that have been moved into new packages
533 */
534
cfae3b8d
RC
535 for (vector <packagemeta *>::iterator i = db.packages.begin ();
536 i != db.packages.end (); ++i)
ad3c7385 537 {
cfae3b8d 538 packagemeta & pkg = **i;
3c196821 539 if (pkg.installed && pkg.desired.picked())
4a83b7b0 540 {
58ee6135
RC
541 try {
542 int e = 0;
543 e += replace_one (pkg);
544 if (e)
545 errors++;
546 }
547 catch (exception *e)
548 {
549 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
550 {
551 log (LOG_TIMESTAMP, String ("User cancelled setup after install error"));
9f4a0c62 552 LogSingleton::GetInstance().exit (1);
58ee6135
RC
553 return;
554 }
555 }
4a83b7b0 556 }
ad3c7385
RC
557 }
558
cfae3b8d
RC
559 for (vector <packagemeta *>::iterator i = db.packages.begin ();
560 i != db.packages.end (); ++i)
ad3c7385 561 {
cfae3b8d 562 packagemeta & pkg = **i;
23c9e63c 563
3c196821 564 if (pkg.desired && pkg.desired.changeRequested())
23c9e63c 565 {
58ee6135 566 try
40aef45e 567 {
58ee6135
RC
568 int e = 0;
569 e += install_one (pkg);
570 if (e)
571 errors++;
572 }
573 catch (exception *e)
574 {
575 if (yesno (owner, IDS_INSTALL_ERROR, e->what()) != IDYES)
576 {
577 log (LOG_TIMESTAMP, String ("User cancelled setup after install error"));
9f4a0c62 578 LogSingleton::GetInstance().exit (1);
58ee6135
RC
579 return;
580 }
40aef45e 581 }
4a83b7b0 582 }
b24c88b3 583 } // end of big package loop
4a83b7b0 584
ad3c7385
RC
585 if (rebootneeded)
586 note (owner, IDS_REBOOT_REQUIRED);
587
7c7034e8
RC
588 int temperr;
589 if ((temperr = db.flush ()))
23c9e63c 590 {
7c7034e8 591 const char *err = strerror (temperr);
23c9e63c
DD
592 if (!err)
593 err = "(unknown error)";
3c054baf
RC
594 fatal (owner, IDS_ERR_OPEN_WRITE, "Package Database",
595 err);
23c9e63c
DD
596 }
597
72fd1d1e
CF
598 if (!errors)
599 check_for_old_cygwin ();
4a83b7b0
DD
600 if (num_installs == 0 && num_uninstalls == 0)
601 {
f2ff9838 602 if (!unattended_mode) exit_msg = IDS_NOTHING_INSTALLED;
4a83b7b0
DD
603 return;
604 }
605 if (num_installs == 0)
606 {
f2ff9838 607 if (!unattended_mode) exit_msg = IDS_UNINSTALL_COMPLETE;
4a83b7b0
DD
608 return;
609 }
610
2a1a01e0
DD
611 if (errors)
612 exit_msg = IDS_INSTALL_INCOMPLETE;
f2ff9838 613 else if (!unattended_mode)
2a1a01e0 614 exit_msg = IDS_INSTALL_COMPLETE;
23c9e63c 615}
ab57ceaa 616
45e01f23 617static DWORD WINAPI
ab57ceaa
RC
618do_install_reflector (void *p)
619{
620 HANDLE *context;
621 context = (HANDLE *) p;
622
623 do_install_thread ((HINSTANCE) context[0], (HWND) context[1]);
624
625 // Tell the progress page that we're done downloading
626 Progress.PostMessage (WM_APP_INSTALL_THREAD_COMPLETE);
627
45e01f23 628 ExitThread (0);
ab57ceaa
RC
629}
630
631static HANDLE context[2];
632
633void
634do_install (HINSTANCE h, HWND owner)
635{
636 context[0] = h;
637 context[1] = owner;
638
45e01f23
RC
639 DWORD threadID;
640 CreateThread (NULL, 0, do_install_reflector, context, 0, &threadID);
ab57ceaa 641}
This page took 0.116849 seconds and 5 git commands to generate.