blob: 53e6a514f5882d6742af7adabc42e768b6040083 [file] [log] [blame]
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001/*
Thomas Hellerd1d92ea2004-07-14 15:17:04 +00002 IMPORTANT NOTE: IF THIS FILE IS CHANGED, WININST-6.EXE MUST BE RECOMPILED
3 WITH THE MSVC6 WININST.DSW WORKSPACE FILE MANUALLY, AND WININST-7.1.EXE MUST
4 BE RECOMPILED WITH THE MSVC 2003.NET WININST-7.1.VCPROJ FILE MANUALLY.
5
6 IF CHANGES TO THIS FILE ARE CHECKED INTO PYTHON CVS, THE RECOMPILED BINARIES
7 MUST BE CHECKED IN AS WELL!
8*/
9
10/*
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000011 * Written by Thomas Heller, May 2000
12 *
13 * $Id$
14 */
15
16/*
17 * Windows Installer program for distutils.
18 *
19 * (a kind of self-extracting zip-file)
20 *
21 * At runtime, the exefile has appended:
22 * - compressed setup-data in ini-format, containing the following sections:
23 * [metadata]
24 * author=Greg Ward
25 * author_email=gward@python.net
26 * description=Python Distribution Utilities
27 * licence=Python
28 * name=Distutils
29 * url=http://www.python.org/sigs/distutils-sig/
30 * version=0.9pre
31 *
32 * [Setup]
33 * info= text to be displayed in the edit-box
34 * title= to be displayed by this program
35 * target_version = if present, python version required
36 * pyc_compile = if 0, do not compile py to pyc
37 * pyo_compile = if 0, do not compile py to pyo
38 *
39 * - a struct meta_data_hdr, describing the above
40 * - a zip-file, containing the modules to be installed.
41 * for the format see http://www.pkware.com/appnote.html
42 *
43 * What does this program do?
44 * - the setup-data is uncompressed and written to a temporary file.
45 * - setup-data is queried with GetPrivateProfile... calls
46 * - [metadata] - info is displayed in the dialog box
47 * - The registry is searched for installations of python
48 * - The user can select the python version to use.
49 * - The python-installation directory (sys.prefix) is displayed
50 * - When the start-button is pressed, files from the zip-archive
51 * are extracted to the file system. All .py filenames are stored
52 * in a list.
53 */
54/*
55 * Includes now an uninstaller.
56 */
57
58/*
59 * To Do:
60 *
61 * display some explanation when no python version is found
62 * instead showing the user an empty listbox to select something from.
63 *
64 * Finish the code so that we can use other python installations
65 * additionaly to those found in the registry,
66 * and then #define USE_OTHER_PYTHON_VERSIONS
67 *
68 * - install a help-button, which will display something meaningful
69 * to the poor user.
70 * text to the user
71 * - should there be a possibility to display a README file
72 * before starting the installation (if one is present in the archive)
73 * - more comments about what the code does(?)
74 *
75 * - evolve this into a full blown installer (???)
76 */
77
78#include <windows.h>
79#include <commctrl.h>
80#include <imagehlp.h>
81#include <objbase.h>
82#include <shlobj.h>
83#include <objidl.h>
84#include "resource.h"
85
86#include <stdio.h>
87#include <stdlib.h>
88#include <stdarg.h>
89#include <string.h>
90#include <time.h>
Thomas Heller9f2e3be2005-02-03 20:35:10 +000091#include <sys/types.h>
92#include <sys/stat.h>
93#include <malloc.h>
94#include <io.h>
95#include <fcntl.h>
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000096
97#include "archive.h"
98
99/* Only for debugging!
100 static int dprintf(char *fmt, ...)
101 {
102 char Buffer[4096];
103 va_list marker;
104 int result;
105
106 va_start(marker, fmt);
107 result = wvsprintf(Buffer, fmt, marker);
108 OutputDebugString(Buffer);
109 return result;
110 }
111*/
112
113/* Bah: global variables */
114FILE *logfile;
115
116char modulename[MAX_PATH];
Mark Hammond891f2632009-01-29 13:08:01 +0000117wchar_t wmodulename[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000118
119HWND hwndMain;
120HWND hDialog;
121
122char *ini_file; /* Full pathname of ini-file */
123/* From ini-file */
124char info[4096]; /* [Setup] info= */
125char title[80]; /* [Setup] title=, contains package name
126 including version: "Distutils-1.0.1" */
127char target_version[10]; /* [Setup] target_version=, required python
128 version or empty string */
129char build_info[80]; /* [Setup] build_info=, distutils version
130 and build date */
131
132char meta_name[80]; /* package name without version like
133 'Distutils' */
134char install_script[MAX_PATH];
Thomas Hellera19cdad2004-02-20 14:43:21 +0000135char *pre_install_script; /* run before we install a single file */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000136
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000137char user_access_control[10]; // one of 'auto', 'force', otherwise none.
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000138
139int py_major, py_minor; /* Python version selected for installation */
140
141char *arc_data; /* memory mapped archive */
142DWORD arc_size; /* number of bytes in archive */
143int exe_size; /* number of bytes for exe-file portion */
144char python_dir[MAX_PATH];
145char pythondll[MAX_PATH];
146BOOL pyc_compile, pyo_compile;
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000147/* Either HKLM or HKCU, depending on where Python itself is registered, and
148 the permissions of the current user. */
149HKEY hkey_root = (HKEY)-1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000150
151BOOL success; /* Installation successfull? */
Thomas Hellera19cdad2004-02-20 14:43:21 +0000152char *failure_reason = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000153
154HANDLE hBitmap;
155char *bitmap_bytes;
156
157
158#define WM_NUMFILES WM_USER+1
159/* wParam: 0, lParam: total number of files */
160#define WM_NEXTFILE WM_USER+2
161/* wParam: number of this file */
162/* lParam: points to pathname */
163
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000164static BOOL notify(int code, char *fmt, ...);
165
166/* Note: If scheme.prefix is nonempty, it must end with a '\'! */
167/* Note: purelib must be the FIRST entry! */
168SCHEME old_scheme[] = {
169 { "PURELIB", "" },
170 { "PLATLIB", "" },
171 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
172 { "SCRIPTS", "Scripts\\" },
173 { "DATA", "" },
174 { NULL, NULL },
175};
176
177SCHEME new_scheme[] = {
178 { "PURELIB", "Lib\\site-packages\\" },
179 { "PLATLIB", "Lib\\site-packages\\" },
180 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
181 { "SCRIPTS", "Scripts\\" },
182 { "DATA", "" },
183 { NULL, NULL },
184};
185
186static void unescape(char *dst, char *src, unsigned size)
187{
188 char *eon;
189 char ch;
190
191 while (src && *src && (size > 2)) {
192 if (*src == '\\') {
193 switch (*++src) {
194 case 'n':
195 ++src;
196 *dst++ = '\r';
197 *dst++ = '\n';
198 size -= 2;
199 break;
200 case 'r':
201 ++src;
202 *dst++ = '\r';
203 --size;
204 break;
205 case '0': case '1': case '2': case '3':
206 ch = (char)strtol(src, &eon, 8);
207 if (ch == '\n') {
208 *dst++ = '\r';
209 --size;
210 }
211 *dst++ = ch;
212 --size;
213 src = eon;
214 }
215 } else {
216 *dst++ = *src++;
217 --size;
218 }
219 }
220 *dst = '\0';
221}
222
223static struct tagFile {
224 char *path;
225 struct tagFile *next;
226} *file_list = NULL;
227
Thomas Hellera19cdad2004-02-20 14:43:21 +0000228static void set_failure_reason(char *reason)
229{
230 if (failure_reason)
231 free(failure_reason);
232 failure_reason = strdup(reason);
233 success = FALSE;
234}
235static char *get_failure_reason()
236{
237 if (!failure_reason)
238 return "Installation failed.";
239 return failure_reason;
240}
241
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000242static void add_to_filelist(char *path)
243{
244 struct tagFile *p;
245 p = (struct tagFile *)malloc(sizeof(struct tagFile));
246 p->path = strdup(path);
247 p->next = file_list;
248 file_list = p;
249}
250
251static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),
252 int optimize)
253{
254 struct tagFile *p;
255 int total, n;
256 char Buffer[MAX_PATH + 64];
257 int errors = 0;
258
259 total = 0;
260 p = file_list;
261 while (p) {
262 ++total;
263 p = p->next;
264 }
265 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0,
266 MAKELPARAM(0, total));
267 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0);
268
269 n = 0;
270 p = file_list;
271 while (p) {
272 ++n;
273 wsprintf(Buffer,
274 "import py_compile; py_compile.compile (r'%s')",
275 p->path);
276 if (PyRun_SimpleString(Buffer)) {
277 ++errors;
278 }
279 /* We send the notification even if the files could not
280 * be created so that the uninstaller will remove them
281 * in case they are created later.
282 */
283 wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c');
284 notify(FILE_CREATED, Buffer);
285
286 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
287 SetDlgItemText(hDialog, IDC_INFO, p->path);
288 p = p->next;
289 }
290 return errors;
291}
292
293#define DECLPROC(dll, result, name, args)\
294 typedef result (*__PROC__##name) args;\
295 result (*name)args = (__PROC__##name)GetProcAddress(dll, #name)
296
297
298#define DECLVAR(dll, type, name)\
299 type *name = (type*)GetProcAddress(dll, #name)
300
301typedef void PyObject;
302
Mark Hammond891f2632009-01-29 13:08:01 +0000303// Convert a "char *" string to "whcar_t *", or NULL on error.
304// Result string must be free'd
305wchar_t *widen_string(char *src)
306{
307 wchar_t *result;
308 DWORD dest_cch;
309 int src_len = strlen(src) + 1; // include NULL term in all ops
310 /* use MultiByteToWideChar() to see how much we need. */
311 /* NOTE: this will include the null-term in the length */
312 dest_cch = MultiByteToWideChar(CP_ACP, 0, src, src_len, NULL, 0);
313 // alloc the buffer
314 result = (wchar_t *)malloc(dest_cch * sizeof(wchar_t));
315 if (result==NULL)
316 return NULL;
317 /* do the conversion */
318 if (0==MultiByteToWideChar(CP_ACP, 0, src, src_len, result, dest_cch)) {
319 free(result);
320 return NULL;
321 }
322 return result;
323}
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000324
325/*
326 * Returns number of files which failed to compile,
327 * -1 if python could not be loaded at all
328 */
329static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag)
330{
331 DECLPROC(hPython, void, Py_Initialize, (void));
Mark Hammond891f2632009-01-29 13:08:01 +0000332 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000333 DECLPROC(hPython, void, Py_Finalize, (void));
334 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
335 DECLPROC(hPython, PyObject *, PySys_GetObject, (char *));
336 DECLVAR(hPython, int, Py_OptimizeFlag);
337
338 int errors = 0;
339 struct tagFile *p = file_list;
340
341 if (!p)
342 return 0;
343
344 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize)
345 return -1;
346
347 if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag)
348 return -1;
349
350 *Py_OptimizeFlag = optimize_flag ? 1 : 0;
Mark Hammond891f2632009-01-29 13:08:01 +0000351 Py_SetProgramName(wmodulename);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000352 Py_Initialize();
353
354 errors += do_compile_files(PyRun_SimpleString, optimize_flag);
355 Py_Finalize();
356
357 return errors;
358}
359
360typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
361
362struct PyMethodDef {
363 char *ml_name;
364 PyCFunction ml_meth;
365 int ml_flags;
366 char *ml_doc;
367};
368typedef struct PyMethodDef PyMethodDef;
369
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000370// XXX - all of these are potentially fragile! We load and unload
371// the Python DLL multiple times - so storing functions pointers
372// is dangerous (although things *look* OK at present)
373// Better might be to roll prepare_script_environment() into
374// LoadPythonDll(), and create a new UnloadPythonDLL() which also
375// clears the global pointers.
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000376void *(*g_Py_BuildValue)(char *, ...);
377int (*g_PyArg_ParseTuple)(PyObject *, char *, ...);
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000378PyObject * (*g_PyLong_FromVoidPtr)(void *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000379
380PyObject *g_PyExc_ValueError;
381PyObject *g_PyExc_OSError;
382
383PyObject *(*g_PyErr_Format)(PyObject *, char *, ...);
384
385#define DEF_CSIDL(name) { name, #name }
386
387struct {
388 int nFolder;
389 char *name;
390} csidl_names[] = {
391 /* Startup menu for all users.
392 NT only */
393 DEF_CSIDL(CSIDL_COMMON_STARTMENU),
394 /* Startup menu. */
395 DEF_CSIDL(CSIDL_STARTMENU),
396
397/* DEF_CSIDL(CSIDL_COMMON_APPDATA), */
398/* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
399 /* Repository for application-specific data.
400 Needs Internet Explorer 4.0 */
401 DEF_CSIDL(CSIDL_APPDATA),
402
403 /* The desktop for all users.
404 NT only */
405 DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY),
406 /* The desktop. */
407 DEF_CSIDL(CSIDL_DESKTOPDIRECTORY),
408
409 /* Startup folder for all users.
410 NT only */
411 DEF_CSIDL(CSIDL_COMMON_STARTUP),
412 /* Startup folder. */
413 DEF_CSIDL(CSIDL_STARTUP),
414
415 /* Programs item in the start menu for all users.
416 NT only */
417 DEF_CSIDL(CSIDL_COMMON_PROGRAMS),
418 /* Program item in the user's start menu. */
419 DEF_CSIDL(CSIDL_PROGRAMS),
420
421/* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
422/* DEF_CSIDL(CSIDL_PROGRAM_FILES), */
423
424 /* Virtual folder containing fonts. */
425 DEF_CSIDL(CSIDL_FONTS),
426};
427
428#define DIM(a) (sizeof(a) / sizeof((a)[0]))
429
430static PyObject *FileCreated(PyObject *self, PyObject *args)
431{
432 char *path;
433 if (!g_PyArg_ParseTuple(args, "s", &path))
434 return NULL;
435 notify(FILE_CREATED, path);
436 return g_Py_BuildValue("");
437}
438
439static PyObject *DirectoryCreated(PyObject *self, PyObject *args)
440{
441 char *path;
442 if (!g_PyArg_ParseTuple(args, "s", &path))
443 return NULL;
444 notify(DIR_CREATED, path);
445 return g_Py_BuildValue("");
446}
447
448static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args)
449{
450 char *name;
451 char lpszPath[MAX_PATH];
452 int i;
453 static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd,
454 LPTSTR lpszPath,
455 int nFolder,
456 BOOL fCreate);
457
458 if (!My_SHGetSpecialFolderPath) {
459 HINSTANCE hLib = LoadLibrary("shell32.dll");
460 if (!hLib) {
461 g_PyErr_Format(g_PyExc_OSError,
462 "function not available");
463 return NULL;
464 }
465 My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR,
466 int, BOOL))
467 GetProcAddress(hLib,
468 "SHGetSpecialFolderPathA");
469 }
470
471 if (!g_PyArg_ParseTuple(args, "s", &name))
472 return NULL;
473
474 if (!My_SHGetSpecialFolderPath) {
475 g_PyErr_Format(g_PyExc_OSError, "function not available");
476 return NULL;
477 }
478
479 for (i = 0; i < DIM(csidl_names); ++i) {
480 if (0 == strcmpi(csidl_names[i].name, name)) {
481 int nFolder;
482 nFolder = csidl_names[i].nFolder;
483 if (My_SHGetSpecialFolderPath(NULL, lpszPath,
484 nFolder, 0))
485 return g_Py_BuildValue("s", lpszPath);
486 else {
487 g_PyErr_Format(g_PyExc_OSError,
488 "no such folder (%s)", name);
489 return NULL;
490 }
491
492 }
493 };
494 g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name);
495 return NULL;
496}
497
498static PyObject *CreateShortcut(PyObject *self, PyObject *args)
499{
500 char *path; /* path and filename */
501 char *description;
502 char *filename;
503
504 char *arguments = NULL;
505 char *iconpath = NULL;
506 int iconindex = 0;
507 char *workdir = NULL;
508
509 WCHAR wszFilename[MAX_PATH];
510
511 IShellLink *ps1 = NULL;
512 IPersistFile *pPf = NULL;
513
514 HRESULT hr;
515
516 hr = CoInitialize(NULL);
517 if (FAILED(hr)) {
518 g_PyErr_Format(g_PyExc_OSError,
519 "CoInitialize failed, error 0x%x", hr);
520 goto error;
521 }
522
523 if (!g_PyArg_ParseTuple(args, "sss|sssi",
524 &path, &description, &filename,
525 &arguments, &workdir, &iconpath, &iconindex))
526 return NULL;
527
528 hr = CoCreateInstance(&CLSID_ShellLink,
529 NULL,
530 CLSCTX_INPROC_SERVER,
531 &IID_IShellLink,
532 &ps1);
533 if (FAILED(hr)) {
534 g_PyErr_Format(g_PyExc_OSError,
535 "CoCreateInstance failed, error 0x%x", hr);
536 goto error;
537 }
538
539 hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile,
540 (void **)&pPf);
541 if (FAILED(hr)) {
542 g_PyErr_Format(g_PyExc_OSError,
543 "QueryInterface(IPersistFile) error 0x%x", hr);
544 goto error;
545 }
546
547
548 hr = ps1->lpVtbl->SetPath(ps1, path);
549 if (FAILED(hr)) {
550 g_PyErr_Format(g_PyExc_OSError,
551 "SetPath() failed, error 0x%x", hr);
552 goto error;
553 }
554
555 hr = ps1->lpVtbl->SetDescription(ps1, description);
556 if (FAILED(hr)) {
557 g_PyErr_Format(g_PyExc_OSError,
558 "SetDescription() failed, error 0x%x", hr);
559 goto error;
560 }
561
562 if (arguments) {
563 hr = ps1->lpVtbl->SetArguments(ps1, arguments);
564 if (FAILED(hr)) {
565 g_PyErr_Format(g_PyExc_OSError,
566 "SetArguments() error 0x%x", hr);
567 goto error;
568 }
569 }
570
571 if (iconpath) {
572 hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex);
573 if (FAILED(hr)) {
574 g_PyErr_Format(g_PyExc_OSError,
575 "SetIconLocation() error 0x%x", hr);
576 goto error;
577 }
578 }
579
580 if (workdir) {
581 hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir);
582 if (FAILED(hr)) {
583 g_PyErr_Format(g_PyExc_OSError,
584 "SetWorkingDirectory() error 0x%x", hr);
585 goto error;
586 }
587 }
588
589 MultiByteToWideChar(CP_ACP, 0,
590 filename, -1,
591 wszFilename, MAX_PATH);
592
593 hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
594 if (FAILED(hr)) {
595 g_PyErr_Format(g_PyExc_OSError,
Thomas Hellera19cdad2004-02-20 14:43:21 +0000596 "Failed to create shortcut '%s' - error 0x%x", filename, hr);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000597 goto error;
598 }
599
600 pPf->lpVtbl->Release(pPf);
601 ps1->lpVtbl->Release(ps1);
602 CoUninitialize();
603 return g_Py_BuildValue("");
604
605 error:
606 if (pPf)
607 pPf->lpVtbl->Release(pPf);
608
609 if (ps1)
610 ps1->lpVtbl->Release(ps1);
611
612 CoUninitialize();
613
614 return NULL;
615}
616
Thomas Hellera19cdad2004-02-20 14:43:21 +0000617static PyObject *PyMessageBox(PyObject *self, PyObject *args)
618{
619 int rc;
620 char *text, *caption;
621 int flags;
622 if (!g_PyArg_ParseTuple(args, "ssi", &text, &caption, &flags))
623 return NULL;
624 rc = MessageBox(GetFocus(), text, caption, flags);
625 return g_Py_BuildValue("i", rc);
626}
627
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000628static PyObject *GetRootHKey(PyObject *self)
629{
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000630 return g_PyLong_FromVoidPtr(hkey_root);
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000631}
632
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000633#define METH_VARARGS 0x0001
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000634#define METH_NOARGS 0x0004
635typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000636
637PyMethodDef meth[] = {
638 {"create_shortcut", CreateShortcut, METH_VARARGS, NULL},
639 {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000640 {"get_root_hkey", (PyCFunction)GetRootHKey, METH_NOARGS, NULL},
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000641 {"file_created", FileCreated, METH_VARARGS, NULL},
642 {"directory_created", DirectoryCreated, METH_VARARGS, NULL},
Thomas Hellera19cdad2004-02-20 14:43:21 +0000643 {"message_box", PyMessageBox, METH_VARARGS, NULL},
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000644};
645
Thomas Heller48340392004-06-18 17:03:38 +0000646static HINSTANCE LoadPythonDll(char *fname)
647{
648 char fullpath[_MAX_PATH];
649 LONG size = sizeof(fullpath);
Thomas Heller0f25b722004-12-22 17:24:14 +0000650 char subkey_name[80];
Thomas Heller8abe7bf2005-02-03 20:11:28 +0000651 char buffer[260 + 12];
652 HINSTANCE h;
653
654 /* make sure PYTHONHOME is set, to that sys.path is initialized correctly */
655 wsprintf(buffer, "PYTHONHOME=%s", python_dir);
656 _putenv(buffer);
657 h = LoadLibrary(fname);
Thomas Heller48340392004-06-18 17:03:38 +0000658 if (h)
659 return h;
Thomas Heller9cc5cb72004-12-01 18:18:08 +0000660 wsprintf(subkey_name,
Thomas Heller8992b9b2004-12-01 19:43:02 +0000661 "SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath",
Thomas Heller9cc5cb72004-12-01 18:18:08 +0000662 py_major, py_minor);
663 if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000664 fullpath, &size) &&
665 ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
666 fullpath, &size))
Thomas Heller48340392004-06-18 17:03:38 +0000667 return NULL;
668 strcat(fullpath, "\\");
669 strcat(fullpath, fname);
670 return LoadLibrary(fullpath);
671}
672
Thomas Hellera19cdad2004-02-20 14:43:21 +0000673static int prepare_script_environment(HINSTANCE hPython)
674{
675 PyObject *mod;
676 DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
677 DECLPROC(hPython, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
678 DECLPROC(hPython, PyObject *, PyObject_GetAttrString, (PyObject *, char *));
679 DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *));
680 DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
681 DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
682 DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000683 DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
Thomas Hellera19cdad2004-02-20 14:43:21 +0000684 if (!PyImport_ImportModule || !PyObject_GetAttrString ||
685 !PyObject_SetAttrString || !PyCFunction_New)
686 return 1;
687 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
688 return 1;
689
Georg Brandl1a3284e2007-12-02 09:40:06 +0000690 mod = PyImport_ImportModule("builtins");
Thomas Hellera19cdad2004-02-20 14:43:21 +0000691 if (mod) {
692 int i;
693 g_PyExc_ValueError = PyObject_GetAttrString(mod, "ValueError");
694 g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
695 for (i = 0; i < DIM(meth); ++i) {
696 PyObject_SetAttrString(mod, meth[i].ml_name,
697 PyCFunction_New(&meth[i], NULL));
698 }
699 }
700 g_Py_BuildValue = Py_BuildValue;
701 g_PyArg_ParseTuple = PyArg_ParseTuple;
702 g_PyErr_Format = PyErr_Format;
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000703 g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000704
705 return 0;
706}
707
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000708/*
709 * This function returns one of the following error codes:
710 * 1 if the Python-dll does not export the functions we need
711 * 2 if no install-script is specified in pathname
712 * 3 if the install-script file could not be opened
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000713 * the return value of PyRun_SimpleString() otherwise,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000714 * which is 0 if everything is ok, -1 if an exception had occurred
715 * in the install-script.
716 */
717
718static int
Mark Hammond6d0e9752009-01-29 12:36:50 +0000719do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000720{
Mark Hammond891f2632009-01-29 13:08:01 +0000721 int fh, result, i;
722 static wchar_t *wargv[256];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000723 DECLPROC(hPython, void, Py_Initialize, (void));
Mark Hammond891f2632009-01-29 13:08:01 +0000724 DECLPROC(hPython, int, PySys_SetArgv, (int, wchar_t **));
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000725 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000726 DECLPROC(hPython, void, Py_Finalize, (void));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000727 DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
728 DECLPROC(hPython, PyObject *, PyCFunction_New,
729 (PyMethodDef *, PyObject *));
730 DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
731 DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
732
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000733 if (!Py_Initialize || !PySys_SetArgv
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000734 || !PyRun_SimpleString || !Py_Finalize)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000735 return 1;
736
Thomas Hellera19cdad2004-02-20 14:43:21 +0000737 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000738 return 1;
739
740 if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
741 return 1;
742
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000743 if (pathname == NULL || pathname[0] == '\0')
744 return 2;
745
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000746 fh = open(pathname, _O_RDONLY);
747 if (-1 == fh) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000748 fprintf(stderr, "Could not open postinstall-script %s\n",
749 pathname);
750 return 3;
751 }
752
753 SetDlgItemText(hDialog, IDC_INFO, "Running Script...");
Mark Hammond6d0e9752009-01-29 12:36:50 +0000754
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000755 Py_Initialize();
756
Thomas Hellera19cdad2004-02-20 14:43:21 +0000757 prepare_script_environment(hPython);
Mark Hammond891f2632009-01-29 13:08:01 +0000758 // widen the argv array for py3k.
759 memset(wargv, 0, sizeof(wargv));
760 for (i=0;i<argc;i++)
761 wargv[i] = argv[i] ? widen_string(argv[i]) : NULL;
762 PySys_SetArgv(argc, wargv);
763 // free the strings we just widened.
764 for (i=0;i<argc;i++)
765 if (wargv[i])
766 free(wargv[i]);
767
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000768 result = 3;
769 {
770 struct _stat statbuf;
771 if(0 == _fstat(fh, &statbuf)) {
772 char *script = alloca(statbuf.st_size + 5);
773 int n = read(fh, script, statbuf.st_size);
774 if (n > 0) {
775 script[n] = '\n';
776 script[n+1] = 0;
777 result = PyRun_SimpleString(script);
778 }
779 }
780 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000781 Py_Finalize();
782
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000783 close(fh);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000784 return result;
785}
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000786
Mark Hammond6d0e9752009-01-29 12:36:50 +0000787static int
788run_installscript(char *pathname, int argc, char **argv, char **pOutput)
789{
790 HINSTANCE hPython;
791 int result = 1;
792 int out_buf_size;
793 HANDLE redirected, old_stderr, old_stdout;
794 char *tempname;
795
796 *pOutput = NULL;
797
798 tempname = tempnam(NULL, NULL);
799 // We use a static CRT while the Python version we load uses
800 // the CRT from one of various possibile DLLs. As a result we
801 // need to redirect the standard handles using the API rather
802 // than the CRT.
803 redirected = CreateFile(
804 tempname,
805 GENERIC_WRITE | GENERIC_READ,
806 FILE_SHARE_READ,
807 NULL,
808 CREATE_ALWAYS,
809 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
810 NULL);
811 old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
812 old_stderr = GetStdHandle(STD_ERROR_HANDLE);
813 SetStdHandle(STD_OUTPUT_HANDLE, redirected);
814 SetStdHandle(STD_ERROR_HANDLE, redirected);
815
816 hPython = LoadPythonDll(pythondll);
817 if (hPython) {
818 result = do_run_installscript(hPython, pathname, argc, argv);
819 FreeLibrary(hPython);
820 } else {
821 fprintf(stderr, "*** Could not load Python ***");
822 }
823 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
824 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
825 out_buf_size = min(GetFileSize(redirected, NULL), 4096);
826 *pOutput = malloc(out_buf_size+1);
827 if (*pOutput) {
828 DWORD nread = 0;
829 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
830 ReadFile(redirected, *pOutput, out_buf_size, &nread, NULL);
831 (*pOutput)[nread] = '\0';
832 }
833 CloseHandle(redirected);
834 DeleteFile(tempname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000835 return result;
836}
837
Thomas Hellera19cdad2004-02-20 14:43:21 +0000838static int do_run_simple_script(HINSTANCE hPython, char *script)
839{
840 int rc;
841 DECLPROC(hPython, void, Py_Initialize, (void));
Mark Hammond891f2632009-01-29 13:08:01 +0000842 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
Thomas Hellera19cdad2004-02-20 14:43:21 +0000843 DECLPROC(hPython, void, Py_Finalize, (void));
844 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
845 DECLPROC(hPython, void, PyErr_Print, (void));
846
847 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
848 !PyRun_SimpleString || !PyErr_Print)
849 return -1;
850
Mark Hammond891f2632009-01-29 13:08:01 +0000851 Py_SetProgramName(wmodulename);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000852 Py_Initialize();
853 prepare_script_environment(hPython);
854 rc = PyRun_SimpleString(script);
855 if (rc)
856 PyErr_Print();
857 Py_Finalize();
858 return rc;
859}
860
861static int run_simple_script(char *script)
862{
863 int rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000864 HINSTANCE hPython;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000865 char *tempname = tempnam(NULL, NULL);
866 // Redirect output using win32 API - see comments above...
867 HANDLE redirected = CreateFile(
868 tempname,
869 GENERIC_WRITE | GENERIC_READ,
870 FILE_SHARE_READ,
871 NULL,
872 CREATE_ALWAYS,
873 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
874 NULL);
875 HANDLE old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
876 HANDLE old_stderr = GetStdHandle(STD_ERROR_HANDLE);
877 SetStdHandle(STD_OUTPUT_HANDLE, redirected);
878 SetStdHandle(STD_ERROR_HANDLE, redirected);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000879
Thomas Heller48340392004-06-18 17:03:38 +0000880 hPython = LoadPythonDll(pythondll);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000881 if (!hPython) {
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000882 char reason[128];
883 wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
884 set_failure_reason(reason);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000885 return -1;
886 }
887 rc = do_run_simple_script(hPython, script);
888 FreeLibrary(hPython);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000889 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
890 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000891 /* We only care about the output when we fail. If the script works
892 OK, then we discard it
893 */
894 if (rc) {
895 int err_buf_size;
896 char *err_buf;
897 const char *prefix = "Running the pre-installation script failed\r\n";
898 int prefix_len = strlen(prefix);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000899 err_buf_size = GetFileSize(redirected, NULL);
900 if (err_buf_size==INVALID_FILE_SIZE) // an error - let's try anyway...
901 err_buf_size = 4096;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000902 err_buf = malloc(prefix_len + err_buf_size + 1);
903 if (err_buf) {
Mark Hammond6d0e9752009-01-29 12:36:50 +0000904 DWORD n = 0;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000905 strcpy(err_buf, prefix);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000906 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
907 ReadFile(redirected, err_buf+prefix_len, err_buf_size, &n, NULL);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000908 err_buf[prefix_len+n] = '\0';
Thomas Hellera19cdad2004-02-20 14:43:21 +0000909 set_failure_reason(err_buf);
910 free(err_buf);
911 } else {
912 set_failure_reason("Out of memory!");
913 }
914 }
Mark Hammond6d0e9752009-01-29 12:36:50 +0000915 CloseHandle(redirected);
916 DeleteFile(tempname);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000917 return rc;
918}
919
920
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000921static BOOL SystemError(int error, char *msg)
922{
923 char Buffer[1024];
924 int n;
925
926 if (error) {
927 LPVOID lpMsgBuf;
928 FormatMessage(
929 FORMAT_MESSAGE_ALLOCATE_BUFFER |
930 FORMAT_MESSAGE_FROM_SYSTEM,
931 NULL,
932 error,
933 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
934 (LPSTR)&lpMsgBuf,
935 0,
936 NULL
937 );
938 strncpy(Buffer, lpMsgBuf, sizeof(Buffer));
939 LocalFree(lpMsgBuf);
940 } else
941 Buffer[0] = '\0';
942 n = lstrlen(Buffer);
943 _snprintf(Buffer+n, sizeof(Buffer)-n, msg);
944 MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
945 return FALSE;
946}
947
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000948static BOOL notify (int code, char *fmt, ...)
949{
950 char Buffer[1024];
951 va_list marker;
952 BOOL result = TRUE;
953 int a, b;
954 char *cp;
955
956 va_start(marker, fmt);
957 _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
958
959 switch (code) {
960/* Questions */
961 case CAN_OVERWRITE:
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000962 break;
963
964/* Information notification */
965 case DIR_CREATED:
966 if (logfile)
967 fprintf(logfile, "100 Made Dir: %s\n", fmt);
968 break;
969
970 case FILE_CREATED:
971 if (logfile)
972 fprintf(logfile, "200 File Copy: %s\n", fmt);
973 goto add_to_filelist_label;
974 break;
975
976 case FILE_OVERWRITTEN:
977 if (logfile)
978 fprintf(logfile, "200 File Overwrite: %s\n", fmt);
979 add_to_filelist_label:
980 if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
981 add_to_filelist(fmt);
982 break;
983
984/* Error Messages */
985 case ZLIB_ERROR:
986 MessageBox(GetFocus(), Buffer, "Error",
987 MB_OK | MB_ICONWARNING);
988 break;
989
990 case SYSTEM_ERROR:
991 SystemError(GetLastError(), Buffer);
992 break;
993
994 case NUM_FILES:
995 a = va_arg(marker, int);
996 b = va_arg(marker, int);
997 SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a));
998 SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt);
999 }
1000 va_end(marker);
1001
1002 return result;
1003}
1004
1005static char *MapExistingFile(char *pathname, DWORD *psize)
1006{
1007 HANDLE hFile, hFileMapping;
1008 DWORD nSizeLow, nSizeHigh;
1009 char *data;
1010
1011 hFile = CreateFile(pathname,
1012 GENERIC_READ, FILE_SHARE_READ, NULL,
1013 OPEN_EXISTING,
1014 FILE_ATTRIBUTE_NORMAL, NULL);
1015 if (hFile == INVALID_HANDLE_VALUE)
1016 return NULL;
1017 nSizeLow = GetFileSize(hFile, &nSizeHigh);
1018 hFileMapping = CreateFileMapping(hFile,
1019 NULL, PAGE_READONLY, 0, 0, NULL);
1020 CloseHandle(hFile);
1021
1022 if (hFileMapping == INVALID_HANDLE_VALUE)
1023 return NULL;
1024
1025 data = MapViewOfFile(hFileMapping,
1026 FILE_MAP_READ, 0, 0, 0);
1027
1028 CloseHandle(hFileMapping);
1029 *psize = nSizeLow;
1030 return data;
1031}
1032
1033
1034static void create_bitmap(HWND hwnd)
1035{
1036 BITMAPFILEHEADER *bfh;
1037 BITMAPINFO *bi;
1038 HDC hdc;
1039
1040 if (!bitmap_bytes)
1041 return;
1042
1043 if (hBitmap)
1044 return;
1045
1046 hdc = GetDC(hwnd);
1047
1048 bfh = (BITMAPFILEHEADER *)bitmap_bytes;
1049 bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
1050
1051 hBitmap = CreateDIBitmap(hdc,
1052 &bi->bmiHeader,
1053 CBM_INIT,
1054 bitmap_bytes + bfh->bfOffBits,
1055 bi,
1056 DIB_RGB_COLORS);
1057 ReleaseDC(hwnd, hdc);
1058}
1059
Thomas Hellera19cdad2004-02-20 14:43:21 +00001060/* Extract everything we need to begin the installation. Currently this
1061 is the INI filename with install data, and the raw pre-install script
1062*/
1063static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
1064 char **out_ini_file, char **out_preinstall_script)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001065{
1066 /* read the end of central directory record */
1067 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
1068 (struct eof_cdir)];
1069
1070 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
1071 pe->ofsCDir;
1072
1073 int ofs = arc_start - sizeof (struct meta_data_hdr);
1074
1075 /* read meta_data info */
1076 struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
1077 char *src, *dst;
1078 char *ini_file;
1079 char tempdir[MAX_PATH];
1080
Thomas Hellera19cdad2004-02-20 14:43:21 +00001081 /* ensure that if we fail, we don't have garbage out pointers */
1082 *out_ini_file = *out_preinstall_script = NULL;
1083
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001084 if (pe->tag != 0x06054b50) {
Thomas Hellera19cdad2004-02-20 14:43:21 +00001085 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001086 }
1087
Thomas Heller876d9902004-07-19 09:57:58 +00001088 if (pmd->tag != 0x1234567B) {
1089 return SystemError(0,
1090 "Invalid cfgdata magic number (see bdist_wininst.py)");
1091 }
1092 if (ofs < 0) {
Thomas Hellera19cdad2004-02-20 14:43:21 +00001093 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001094 }
1095
1096 if (pmd->bitmap_size) {
1097 /* Store pointer to bitmap bytes */
1098 bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
1099 }
1100
1101 *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size;
1102
1103 src = ((char *)pmd) - pmd->uncomp_size;
1104 ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
1105 if (!ini_file)
Thomas Hellera19cdad2004-02-20 14:43:21 +00001106 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001107 if (!GetTempPath(sizeof(tempdir), tempdir)
1108 || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
1109 SystemError(GetLastError(),
1110 "Could not create temporary file");
Thomas Hellera19cdad2004-02-20 14:43:21 +00001111 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001112 }
1113
1114 dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
1115 0, 0, NULL/*notify*/);
1116 if (!dst)
Thomas Hellera19cdad2004-02-20 14:43:21 +00001117 return FALSE;
1118 /* Up to the first \0 is the INI file data. */
1119 strncpy(dst, src, pmd->uncomp_size);
1120 src += strlen(dst) + 1;
1121 /* Up to next \0 is the pre-install script */
1122 *out_preinstall_script = strdup(src);
1123 *out_ini_file = ini_file;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001124 UnmapViewOfFile(dst);
Thomas Hellera19cdad2004-02-20 14:43:21 +00001125 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001126}
1127
1128static void PumpMessages(void)
1129{
1130 MSG msg;
1131 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1132 TranslateMessage(&msg);
1133 DispatchMessage(&msg);
1134 }
1135}
1136
1137LRESULT CALLBACK
1138WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1139{
1140 HDC hdc;
1141 HFONT hFont;
1142 int h;
1143 PAINTSTRUCT ps;
1144 switch (msg) {
1145 case WM_PAINT:
1146 hdc = BeginPaint(hwnd, &ps);
1147 h = GetSystemMetrics(SM_CYSCREEN) / 10;
1148 hFont = CreateFont(h, 0, 0, 0, 700, TRUE,
1149 0, 0, 0, 0, 0, 0, 0, "Times Roman");
1150 hFont = SelectObject(hdc, hFont);
1151 SetBkMode(hdc, TRANSPARENT);
1152 TextOut(hdc, 15, 15, title, strlen(title));
1153 SetTextColor(hdc, RGB(255, 255, 255));
1154 TextOut(hdc, 10, 10, title, strlen(title));
1155 DeleteObject(SelectObject(hdc, hFont));
1156 EndPaint(hwnd, &ps);
1157 return 0;
1158 }
1159 return DefWindowProc(hwnd, msg, wParam, lParam);
1160}
1161
1162static HWND CreateBackground(char *title)
1163{
1164 WNDCLASS wc;
1165 HWND hwnd;
1166 char buffer[4096];
1167
1168 wc.style = CS_VREDRAW | CS_HREDRAW;
1169 wc.lpfnWndProc = WindowProc;
1170 wc.cbWndExtra = 0;
1171 wc.cbClsExtra = 0;
1172 wc.hInstance = GetModuleHandle(NULL);
1173 wc.hIcon = NULL;
1174 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1175 wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128));
1176 wc.lpszMenuName = NULL;
1177 wc.lpszClassName = "SetupWindowClass";
1178
1179 if (!RegisterClass(&wc))
1180 MessageBox(hwndMain,
1181 "Could not register window class",
1182 "Setup.exe", MB_OK);
1183
1184 wsprintf(buffer, "Setup %s", title);
1185 hwnd = CreateWindow("SetupWindowClass",
1186 buffer,
1187 0,
1188 0, 0,
1189 GetSystemMetrics(SM_CXFULLSCREEN),
1190 GetSystemMetrics(SM_CYFULLSCREEN),
1191 NULL,
1192 NULL,
1193 GetModuleHandle(NULL),
1194 NULL);
1195 ShowWindow(hwnd, SW_SHOWMAXIMIZED);
1196 UpdateWindow(hwnd);
1197 return hwnd;
1198}
1199
1200/*
1201 * Center a window on the screen
1202 */
1203static void CenterWindow(HWND hwnd)
1204{
1205 RECT rc;
1206 int w, h;
1207
1208 GetWindowRect(hwnd, &rc);
1209 w = GetSystemMetrics(SM_CXSCREEN);
1210 h = GetSystemMetrics(SM_CYSCREEN);
1211 MoveWindow(hwnd,
1212 (w - (rc.right-rc.left))/2,
1213 (h - (rc.bottom-rc.top))/2,
1214 rc.right-rc.left, rc.bottom-rc.top, FALSE);
1215}
1216
1217#include <prsht.h>
1218
1219BOOL CALLBACK
1220IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1221{
1222 LPNMHDR lpnm;
1223 char Buffer[4096];
1224
1225 switch (msg) {
1226 case WM_INITDIALOG:
1227 create_bitmap(hwnd);
1228 if(hBitmap)
1229 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1230 IMAGE_BITMAP, (LPARAM)hBitmap);
1231 CenterWindow(GetParent(hwnd));
1232 wsprintf(Buffer,
1233 "This Wizard will install %s on your computer. "
1234 "Click Next to continue "
1235 "or Cancel to exit the Setup Wizard.",
1236 meta_name);
1237 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1238 SetDlgItemText(hwnd, IDC_INTRO_TEXT, info);
1239 SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info);
1240 return FALSE;
1241
1242 case WM_NOTIFY:
1243 lpnm = (LPNMHDR) lParam;
1244
1245 switch (lpnm->code) {
1246 case PSN_SETACTIVE:
1247 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
1248 break;
1249
1250 case PSN_WIZNEXT:
1251 break;
1252
1253 case PSN_RESET:
1254 break;
1255
1256 default:
1257 break;
1258 }
1259 }
1260 return FALSE;
1261}
1262
1263#ifdef USE_OTHER_PYTHON_VERSIONS
1264/* These are really private variables used to communicate
1265 * between StatusRoutine and CheckPythonExe
1266 */
1267char bound_image_dll[_MAX_PATH];
1268int bound_image_major;
1269int bound_image_minor;
1270
1271static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason,
1272 PSTR ImageName,
1273 PSTR DllName,
1274 ULONG Va,
1275 ULONG Parameter)
1276{
1277 char fname[_MAX_PATH];
1278 int int_version;
1279
1280 switch(reason) {
1281 case BindOutOfMemory:
1282 case BindRvaToVaFailed:
1283 case BindNoRoomInImage:
1284 case BindImportProcedureFailed:
1285 break;
1286
1287 case BindImportProcedure:
1288 case BindForwarder:
1289 case BindForwarderNOT:
1290 case BindImageModified:
1291 case BindExpandFileHeaders:
1292 case BindImageComplete:
1293 case BindSymbolsNotUpdated:
1294 case BindMismatchedSymbols:
1295 case BindImportModuleFailed:
1296 break;
1297
1298 case BindImportModule:
1299 if (1 == sscanf(DllName, "python%d", &int_version)) {
1300 SearchPath(NULL, DllName, NULL, sizeof(fname),
1301 fname, NULL);
1302 strcpy(bound_image_dll, fname);
1303 bound_image_major = int_version / 10;
1304 bound_image_minor = int_version % 10;
1305 OutputDebugString("BOUND ");
1306 OutputDebugString(fname);
1307 OutputDebugString("\n");
1308 }
1309 break;
1310 }
1311 return TRUE;
1312}
1313
1314/*
1315 */
1316static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
1317{
1318 void (__cdecl * Py_Initialize)(void);
1319 void (__cdecl * Py_SetProgramName)(char *);
1320 void (__cdecl * Py_Finalize)(void);
1321 void* (__cdecl * PySys_GetObject)(char *);
1322 void (__cdecl * PySys_SetArgv)(int, char **);
1323 char* (__cdecl * Py_GetPrefix)(void);
1324 char* (__cdecl * Py_GetPath)(void);
1325 HINSTANCE hPython;
1326 LPSTR prefix = NULL;
1327 int (__cdecl * PyRun_SimpleString)(char *);
1328
1329 {
1330 char Buffer[256];
1331 wsprintf(Buffer, "PYTHONHOME=%s", exe);
1332 *strrchr(Buffer, '\\') = '\0';
1333// MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
1334 _putenv(Buffer);
1335 _putenv("PYTHONPATH=");
1336 }
1337
1338 hPython = LoadLibrary(dll);
1339 if (!hPython)
1340 return NULL;
1341 Py_Initialize = (void (*)(void))GetProcAddress
1342 (hPython,"Py_Initialize");
1343
1344 PySys_SetArgv = (void (*)(int, char **))GetProcAddress
1345 (hPython,"PySys_SetArgv");
1346
1347 PyRun_SimpleString = (int (*)(char *))GetProcAddress
1348 (hPython,"PyRun_SimpleString");
1349
1350 Py_SetProgramName = (void (*)(char *))GetProcAddress
1351 (hPython,"Py_SetProgramName");
1352
1353 PySys_GetObject = (void* (*)(char *))GetProcAddress
1354 (hPython,"PySys_GetObject");
1355
1356 Py_GetPrefix = (char * (*)(void))GetProcAddress
1357 (hPython,"Py_GetPrefix");
1358
1359 Py_GetPath = (char * (*)(void))GetProcAddress
1360 (hPython,"Py_GetPath");
1361
1362 Py_Finalize = (void (*)(void))GetProcAddress(hPython,
1363 "Py_Finalize");
1364 Py_SetProgramName(exe);
1365 Py_Initialize();
1366 PySys_SetArgv(1, &exe);
1367
1368 MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
1369 MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
1370
1371 Py_Finalize();
1372 FreeLibrary(hPython);
1373
1374 return prefix;
1375}
1376
1377static BOOL
1378CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
1379{
1380 bound_image_dll[0] = '\0';
1381 if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
1382 pathname,
1383 NULL,
1384 NULL,
1385 StatusRoutine))
1386 return SystemError(0, "Could not bind image");
1387 if (bound_image_dll[0] == '\0')
1388 return SystemError(0, "Does not seem to be a python executable");
1389 *pmajor = bound_image_major;
1390 *pminor = bound_image_minor;
1391 if (version && *version) {
1392 char core_version[12];
1393 wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor);
1394 if (strcmp(version, core_version))
1395 return SystemError(0, "Wrong Python version");
1396 }
1397 get_sys_prefix(pathname, bound_image_dll);
1398 return TRUE;
1399}
1400
1401/*
1402 * Browse for other python versions. Insert it into the listbox specified
1403 * by hwnd. version, if not NULL or empty, is the version required.
1404 */
1405static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version)
1406{
1407 char vers_name[_MAX_PATH + 80];
1408 DWORD itemindex;
1409 OPENFILENAME of;
1410 char pathname[_MAX_PATH];
1411 DWORD result;
1412
1413 strcpy(pathname, "python.exe");
1414
1415 memset(&of, 0, sizeof(of));
1416 of.lStructSize = sizeof(OPENFILENAME);
1417 of.hwndOwner = GetParent(hwnd);
1418 of.hInstance = NULL;
1419 of.lpstrFilter = "python.exe\0python.exe\0";
1420 of.lpstrCustomFilter = NULL;
1421 of.nMaxCustFilter = 0;
1422 of.nFilterIndex = 1;
1423 of.lpstrFile = pathname;
1424 of.nMaxFile = sizeof(pathname);
1425 of.lpstrFileTitle = NULL;
1426 of.nMaxFileTitle = 0;
1427 of.lpstrInitialDir = NULL;
1428 of.lpstrTitle = "Python executable";
1429 of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
1430 of.lpstrDefExt = "exe";
1431
1432 result = GetOpenFileName(&of);
1433 if (result) {
1434 int major, minor;
1435 if (!CheckPythonExe(pathname, version, &major, &minor)) {
1436 return FALSE;
1437 }
1438 *strrchr(pathname, '\\') = '\0';
1439 wsprintf(vers_name, "Python Version %d.%d in %s",
1440 major, minor, pathname);
1441 itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1,
1442 (LPARAM)(LPSTR)vers_name);
1443 SendMessage(hwnd, LB_SETCURSEL, itemindex, 0);
1444 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1445 (LPARAM)(LPSTR)strdup(pathname));
1446 return TRUE;
1447 }
1448 return FALSE;
1449}
1450#endif /* USE_OTHER_PYTHON_VERSIONS */
1451
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001452typedef struct _InstalledVersionInfo {
1453 char prefix[MAX_PATH+1]; // sys.prefix directory.
1454 HKEY hkey; // Is this Python in HKCU or HKLM?
1455} InstalledVersionInfo;
1456
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001457
1458/*
1459 * Fill the listbox specified by hwnd with all python versions found
1460 * in the registry. version, if not NULL or empty, is the version
1461 * required.
1462 */
1463static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version)
1464{
1465 DWORD index = 0;
1466 char core_version[80];
1467 HKEY hKey;
1468 BOOL result = TRUE;
1469 DWORD bufsize;
1470
1471 if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot,
1472 "Software\\Python\\PythonCore",
1473 0, KEY_READ, &hKey))
1474 return FALSE;
1475 bufsize = sizeof(core_version);
1476 while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index,
1477 core_version, &bufsize, NULL,
1478 NULL, NULL, NULL)) {
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001479 char subkey_name[80], vers_name[80];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001480 int itemindex;
1481 DWORD value_size;
1482 HKEY hk;
1483
1484 bufsize = sizeof(core_version);
1485 ++index;
1486 if (version && *version && strcmp(version, core_version))
1487 continue;
1488
1489 wsprintf(vers_name, "Python Version %s (found in registry)",
1490 core_version);
1491 wsprintf(subkey_name,
1492 "Software\\Python\\PythonCore\\%s\\InstallPath",
1493 core_version);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001494 if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001495 InstalledVersionInfo *ivi =
1496 (InstalledVersionInfo *)malloc(sizeof(InstalledVersionInfo));
1497 value_size = sizeof(ivi->prefix);
1498 if (ivi &&
1499 ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL,
1500 ivi->prefix, &value_size)) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001501 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001502 (LPARAM)(LPSTR)vers_name);
1503 ivi->hkey = hkRoot;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001504 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001505 (LPARAM)(LPSTR)ivi);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001506 }
1507 RegCloseKey(hk);
1508 }
1509 }
1510 RegCloseKey(hKey);
1511 return result;
1512}
1513
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001514/* Determine if the current user can write to HKEY_LOCAL_MACHINE */
1515BOOL HasLocalMachinePrivs()
1516{
1517 HKEY hKey;
1518 DWORD result;
1519 static char KeyName[] =
1520 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1521
1522 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1523 KeyName,
1524 0,
1525 KEY_CREATE_SUB_KEY,
1526 &hKey);
1527 if (result==0)
1528 RegCloseKey(hKey);
1529 return result==0;
1530}
1531
1532// Check the root registry key to use - either HKLM or HKCU.
1533// If Python is installed in HKCU, then our extension also must be installed
1534// in HKCU - as Python won't be available for other users, we shouldn't either
1535// (and will fail if we are!)
1536// If Python is installed in HKLM, then we will also prefer to use HKLM, but
1537// this may not be possible - so we silently fall back to HKCU.
1538//
1539// We assume hkey_root is already set to where Python itself is installed.
1540void CheckRootKey(HWND hwnd)
1541{
1542 if (hkey_root==HKEY_CURRENT_USER) {
1543 ; // as above, always install ourself in HKCU too.
1544 } else if (hkey_root==HKEY_LOCAL_MACHINE) {
1545 // Python in HKLM, but we may or may not have permissions there.
1546 // Open the uninstall key with 'create' permissions - if this fails,
1547 // we don't have permission.
1548 if (!HasLocalMachinePrivs())
1549 hkey_root = HKEY_CURRENT_USER;
1550 } else {
1551 MessageBox(hwnd, "Don't know Python's installation type",
1552 "Strange", MB_OK | MB_ICONSTOP);
1553 /* Default to wherever they can, but preferring HKLM */
1554 hkey_root = HasLocalMachinePrivs() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1555 }
1556}
1557
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001558/* Return the installation scheme depending on Python version number */
1559SCHEME *GetScheme(int major, int minor)
1560{
1561 if (major > 2)
1562 return new_scheme;
1563 else if((major == 2) && (minor >= 2))
1564 return new_scheme;
1565 return old_scheme;
1566}
1567
1568BOOL CALLBACK
1569SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1570{
1571 LPNMHDR lpnm;
1572
1573 switch (msg) {
1574 case WM_INITDIALOG:
1575 if (hBitmap)
1576 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1577 IMAGE_BITMAP, (LPARAM)hBitmap);
1578 GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1579 HKEY_LOCAL_MACHINE, target_version);
1580 GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1581 HKEY_CURRENT_USER, target_version);
1582 { /* select the last entry which is the highest python
1583 version found */
1584 int count;
1585 count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1586 LB_GETCOUNT, 0, 0);
1587 if (count && count != LB_ERR)
1588 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
1589 count-1, 0);
1590
1591 /* If a specific Python version is required,
1592 * display a prominent notice showing this fact.
1593 */
1594 if (target_version && target_version[0]) {
1595 char buffer[4096];
1596 wsprintf(buffer,
1597 "Python %s is required for this package. "
1598 "Select installation to use:",
1599 target_version);
1600 SetDlgItemText(hwnd, IDC_TITLE, buffer);
1601 }
1602
1603 if (count == 0) {
1604 char Buffer[4096];
1605 char *msg;
1606 if (target_version && target_version[0]) {
1607 wsprintf(Buffer,
1608 "Python version %s required, which was not found"
1609 " in the registry.", target_version);
1610 msg = Buffer;
1611 } else
1612 msg = "No Python installation found in the registry.";
1613 MessageBox(hwnd, msg, "Cannot install",
1614 MB_OK | MB_ICONSTOP);
1615 }
1616 }
1617 goto UpdateInstallDir;
1618 break;
1619
1620 case WM_COMMAND:
1621 switch (LOWORD(wParam)) {
1622/*
1623 case IDC_OTHERPYTHON:
1624 if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1625 target_version))
1626 goto UpdateInstallDir;
1627 break;
1628*/
1629 case IDC_VERSIONS_LIST:
1630 switch (HIWORD(wParam)) {
1631 int id;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001632 case LBN_SELCHANGE:
1633 UpdateInstallDir:
1634 PropSheet_SetWizButtons(GetParent(hwnd),
1635 PSWIZB_BACK | PSWIZB_NEXT);
1636 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1637 LB_GETCURSEL, 0, 0);
1638 if (id == LB_ERR) {
1639 PropSheet_SetWizButtons(GetParent(hwnd),
1640 PSWIZB_BACK);
1641 SetDlgItemText(hwnd, IDC_PATH, "");
1642 SetDlgItemText(hwnd, IDC_INSTALL_PATH, "");
1643 strcpy(python_dir, "");
1644 strcpy(pythondll, "");
1645 } else {
1646 char *pbuf;
1647 int result;
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001648 InstalledVersionInfo *ivi;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001649 PropSheet_SetWizButtons(GetParent(hwnd),
1650 PSWIZB_BACK | PSWIZB_NEXT);
1651 /* Get the python directory */
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001652 ivi = (InstalledVersionInfo *)
1653 SendDlgItemMessage(hwnd,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001654 IDC_VERSIONS_LIST,
1655 LB_GETITEMDATA,
1656 id,
1657 0);
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001658 hkey_root = ivi->hkey;
1659 strcpy(python_dir, ivi->prefix);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001660 SetDlgItemText(hwnd, IDC_PATH, python_dir);
1661 /* retrieve the python version and pythondll to use */
1662 result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1663 LB_GETTEXTLEN, (WPARAM)id, 0);
1664 pbuf = (char *)malloc(result + 1);
1665 if (pbuf) {
1666 /* guess the name of the python-dll */
1667 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1668 LB_GETTEXT, (WPARAM)id,
1669 (LPARAM)pbuf);
1670 result = sscanf(pbuf, "Python Version %d.%d",
1671 &py_major, &py_minor);
Thomas Heller96142192004-04-15 18:19:02 +00001672 if (result == 2) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001673#ifdef _DEBUG
Thomas Hellera19cdad2004-02-20 14:43:21 +00001674 wsprintf(pythondll, "python%d%d_d.dll",
Thomas Heller96142192004-04-15 18:19:02 +00001675 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001676#else
Thomas Heller96142192004-04-15 18:19:02 +00001677 wsprintf(pythondll, "python%d%d.dll",
1678 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001679#endif
Thomas Heller96142192004-04-15 18:19:02 +00001680 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001681 free(pbuf);
1682 } else
1683 strcpy(pythondll, "");
1684 /* retrieve the scheme for this version */
1685 {
1686 char install_path[_MAX_PATH];
1687 SCHEME *scheme = GetScheme(py_major, py_minor);
1688 strcpy(install_path, python_dir);
1689 if (install_path[strlen(install_path)-1] != '\\')
1690 strcat(install_path, "\\");
1691 strcat(install_path, scheme[0].prefix);
1692 SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
1693 }
1694 }
1695 }
1696 break;
1697 }
1698 return 0;
1699
1700 case WM_NOTIFY:
1701 lpnm = (LPNMHDR) lParam;
1702
1703 switch (lpnm->code) {
1704 int id;
1705 case PSN_SETACTIVE:
1706 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1707 LB_GETCURSEL, 0, 0);
1708 if (id == LB_ERR)
1709 PropSheet_SetWizButtons(GetParent(hwnd),
1710 PSWIZB_BACK);
1711 else
1712 PropSheet_SetWizButtons(GetParent(hwnd),
1713 PSWIZB_BACK | PSWIZB_NEXT);
1714 break;
1715
1716 case PSN_WIZNEXT:
1717 break;
1718
1719 case PSN_WIZFINISH:
1720 break;
1721
1722 case PSN_RESET:
1723 break;
1724
1725 default:
1726 break;
1727 }
1728 }
1729 return 0;
1730}
1731
1732static BOOL OpenLogfile(char *dir)
1733{
1734 char buffer[_MAX_PATH+1];
1735 time_t ltime;
1736 struct tm *now;
1737 long result;
1738 HKEY hKey, hSubkey;
1739 char subkey_name[256];
1740 static char KeyName[] =
1741 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001742 const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
1743 "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001744 DWORD disposition;
1745
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001746 /* Use Create, as the Uninstall subkey may not exist under HKCU.
1747 Use CreateKeyEx, so we can specify a SAM specifying write access
1748 */
1749 result = RegCreateKeyEx(hkey_root,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001750 KeyName,
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001751 0, /* reserved */
1752 NULL, /* class */
1753 0, /* options */
1754 KEY_CREATE_SUB_KEY, /* sam */
1755 NULL, /* security */
1756 &hKey, /* result key */
1757 NULL); /* disposition */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001758 if (result != ERROR_SUCCESS) {
1759 if (result == ERROR_ACCESS_DENIED) {
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001760 /* This should no longer be able to happen - we have already
1761 checked if they have permissions in HKLM, and all users
1762 should have write access to HKCU.
1763 */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001764 MessageBox(GetFocus(),
1765 "You do not seem to have sufficient access rights\n"
1766 "on this machine to install this software",
1767 NULL,
1768 MB_OK | MB_ICONSTOP);
1769 return FALSE;
1770 } else {
1771 MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK);
1772 }
1773 }
1774
1775 sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name);
1776 logfile = fopen(buffer, "a");
1777 time(&ltime);
1778 now = localtime(&ltime);
1779 strftime(buffer, sizeof(buffer),
1780 "*** Installation started %Y/%m/%d %H:%M ***\n",
1781 localtime(&ltime));
1782 fprintf(logfile, buffer);
1783 fprintf(logfile, "Source: %s\n", modulename);
1784
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001785 /* Root key must be first entry processed by uninstaller. */
1786 fprintf(logfile, "999 Root Key: %s\n", root_name);
1787
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001788 sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
1789
1790 result = RegCreateKeyEx(hKey, subkey_name,
1791 0, NULL, 0,
1792 KEY_WRITE,
1793 NULL,
1794 &hSubkey,
1795 &disposition);
1796
1797 if (result != ERROR_SUCCESS)
1798 MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
1799
1800 RegCloseKey(hKey);
1801
1802 if (disposition == REG_CREATED_NEW_KEY)
1803 fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
1804
1805 sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
1806
1807 result = RegSetValueEx(hSubkey, "DisplayName",
1808 0,
1809 REG_SZ,
1810 buffer,
1811 strlen(buffer)+1);
1812
1813 if (result != ERROR_SUCCESS)
1814 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1815
1816 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1817 KeyName, subkey_name, "DisplayName", buffer);
1818
1819 {
1820 FILE *fp;
1821 sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
1822 fp = fopen(buffer, "wb");
1823 fwrite(arc_data, exe_size, 1, fp);
1824 fclose(fp);
1825
1826 sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1827 dir, meta_name, dir, meta_name);
1828
1829 result = RegSetValueEx(hSubkey, "UninstallString",
1830 0,
1831 REG_SZ,
1832 buffer,
1833 strlen(buffer)+1);
1834
1835 if (result != ERROR_SUCCESS)
1836 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1837
1838 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1839 KeyName, subkey_name, "UninstallString", buffer);
1840 }
1841 return TRUE;
1842}
1843
1844static void CloseLogfile(void)
1845{
1846 char buffer[_MAX_PATH+1];
1847 time_t ltime;
1848 struct tm *now;
1849
1850 time(&ltime);
1851 now = localtime(&ltime);
1852 strftime(buffer, sizeof(buffer),
1853 "*** Installation finished %Y/%m/%d %H:%M ***\n",
1854 localtime(&ltime));
1855 fprintf(logfile, buffer);
1856 if (logfile)
1857 fclose(logfile);
1858}
1859
1860BOOL CALLBACK
1861InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1862{
1863 LPNMHDR lpnm;
1864 char Buffer[4096];
1865 SCHEME *scheme;
1866
1867 switch (msg) {
1868 case WM_INITDIALOG:
1869 if (hBitmap)
1870 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1871 IMAGE_BITMAP, (LPARAM)hBitmap);
1872 wsprintf(Buffer,
1873 "Click Next to begin the installation of %s. "
1874 "If you want to review or change any of your "
1875 " installation settings, click Back. "
1876 "Click Cancel to exit the wizard.",
1877 meta_name);
1878 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
Thomas Hellera19cdad2004-02-20 14:43:21 +00001879 SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001880 break;
1881
1882 case WM_NUMFILES:
1883 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1884 PumpMessages();
1885 return TRUE;
1886
1887 case WM_NEXTFILE:
1888 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
1889 0);
1890 SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
1891 PumpMessages();
1892 return TRUE;
1893
1894 case WM_NOTIFY:
1895 lpnm = (LPNMHDR) lParam;
1896
1897 switch (lpnm->code) {
1898 case PSN_SETACTIVE:
1899 PropSheet_SetWizButtons(GetParent(hwnd),
1900 PSWIZB_BACK | PSWIZB_NEXT);
1901 break;
1902
1903 case PSN_WIZFINISH:
1904 break;
1905
1906 case PSN_WIZNEXT:
1907 /* Handle a Next button click here */
1908 hDialog = hwnd;
Thomas Hellera19cdad2004-02-20 14:43:21 +00001909 success = TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001910
Thomas Heller32b8f802004-07-02 08:02:40 +00001911 /* Disable the buttons while we work. Sending CANCELTOCLOSE has
1912 the effect of disabling the cancel button, which is a) as we
1913 do everything synchronously we can't cancel, and b) the next
1914 step is 'finished', when it is too late to cancel anyway.
1915 The next step being 'Finished' means we also don't need to
1916 restore the button state back */
1917 PropSheet_SetWizButtons(GetParent(hwnd), 0);
1918 SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001919 /* Make sure the installation directory name ends in a */
1920 /* backslash */
1921 if (python_dir[strlen(python_dir)-1] != '\\')
1922 strcat(python_dir, "\\");
1923 /* Strip the trailing backslash again */
1924 python_dir[strlen(python_dir)-1] = '\0';
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001925
Thomas Heller9cc5cb72004-12-01 18:18:08 +00001926 CheckRootKey(hwnd);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001927
1928 if (!OpenLogfile(python_dir))
1929 break;
1930
1931/*
1932 * The scheme we have to use depends on the Python version...
1933 if sys.version < "2.2":
1934 WINDOWS_SCHEME = {
1935 'purelib': '$base',
1936 'platlib': '$base',
1937 'headers': '$base/Include/$dist_name',
1938 'scripts': '$base/Scripts',
1939 'data' : '$base',
1940 }
1941 else:
1942 WINDOWS_SCHEME = {
1943 'purelib': '$base/Lib/site-packages',
1944 'platlib': '$base/Lib/site-packages',
1945 'headers': '$base/Include/$dist_name',
1946 'scripts': '$base/Scripts',
1947 'data' : '$base',
1948 }
1949*/
1950 scheme = GetScheme(py_major, py_minor);
Thomas Hellera19cdad2004-02-20 14:43:21 +00001951 /* Run the pre-install script. */
1952 if (pre_install_script && *pre_install_script) {
1953 SetDlgItemText (hwnd, IDC_TITLE,
1954 "Running pre-installation script");
1955 run_simple_script(pre_install_script);
1956 }
1957 if (!success) {
1958 break;
1959 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001960 /* Extract all files from the archive */
1961 SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
Thomas Hellera19cdad2004-02-20 14:43:21 +00001962 if (!unzip_archive (scheme,
1963 python_dir, arc_data,
1964 arc_size, notify))
1965 set_failure_reason("Failed to unzip installation files");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001966 /* Compile the py-files */
Thomas Hellera19cdad2004-02-20 14:43:21 +00001967 if (success && pyc_compile) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001968 int errors;
1969 HINSTANCE hPython;
1970 SetDlgItemText(hwnd, IDC_TITLE,
1971 "Compiling files to .pyc...");
1972
1973 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
Thomas Heller48340392004-06-18 17:03:38 +00001974 hPython = LoadPythonDll(pythondll);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001975 if (hPython) {
1976 errors = compile_filelist(hPython, FALSE);
1977 FreeLibrary(hPython);
1978 }
1979 /* Compilation errors are intentionally ignored:
1980 * Python2.0 contains a bug which will result
1981 * in sys.path containing garbage under certain
1982 * circumstances, and an error message will only
1983 * confuse the user.
1984 */
1985 }
Thomas Hellera19cdad2004-02-20 14:43:21 +00001986 if (success && pyo_compile) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001987 int errors;
1988 HINSTANCE hPython;
1989 SetDlgItemText(hwnd, IDC_TITLE,
1990 "Compiling files to .pyo...");
1991
1992 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
Thomas Heller48340392004-06-18 17:03:38 +00001993 hPython = LoadPythonDll(pythondll);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001994 if (hPython) {
1995 errors = compile_filelist(hPython, TRUE);
1996 FreeLibrary(hPython);
1997 }
1998 /* Errors ignored: see above */
1999 }
2000
2001
2002 break;
2003
2004 case PSN_RESET:
2005 break;
2006
2007 default:
2008 break;
2009 }
2010 }
2011 return 0;
2012}
2013
2014
2015BOOL CALLBACK
2016FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2017{
2018 LPNMHDR lpnm;
2019
2020 switch (msg) {
2021 case WM_INITDIALOG:
2022 if (hBitmap)
2023 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2024 IMAGE_BITMAP, (LPARAM)hBitmap);
2025 if (!success)
Thomas Hellera19cdad2004-02-20 14:43:21 +00002026 SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002027
2028 /* async delay: will show the dialog box completely before
2029 the install_script is started */
2030 PostMessage(hwnd, WM_USER, 0, 0L);
2031 return TRUE;
2032
2033 case WM_USER:
2034
Thomas Hellera19cdad2004-02-20 14:43:21 +00002035 if (success && install_script && install_script[0]) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002036 char fname[MAX_PATH];
Mark Hammond6d0e9752009-01-29 12:36:50 +00002037 char *buffer;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002038 HCURSOR hCursor;
Mark Hammond6d0e9752009-01-29 12:36:50 +00002039 int result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002040
2041 char *argv[3] = {NULL, "-install", NULL};
2042
2043 SetDlgItemText(hwnd, IDC_TITLE,
2044 "Please wait while running postinstall script...");
2045 strcpy(fname, python_dir);
2046 strcat(fname, "\\Scripts\\");
2047 strcat(fname, install_script);
2048
2049 if (logfile)
2050 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
2051
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002052 hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
2053
2054 argv[0] = fname;
2055
Mark Hammond6d0e9752009-01-29 12:36:50 +00002056 result = run_installscript(fname, 2, argv, &buffer);
2057 if (0 != result) {
2058 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002059 }
Mark Hammond6d0e9752009-01-29 12:36:50 +00002060 if (buffer)
2061 SetDlgItemText(hwnd, IDC_INFO, buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002062 SetDlgItemText(hwnd, IDC_TITLE,
2063 "Postinstall script finished.\n"
2064 "Click the Finish button to exit the Setup wizard.");
2065
Mark Hammond6d0e9752009-01-29 12:36:50 +00002066 free(buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002067 SetCursor(hCursor);
2068 CloseLogfile();
2069 }
2070
2071 return TRUE;
2072
2073 case WM_NOTIFY:
2074 lpnm = (LPNMHDR) lParam;
2075
2076 switch (lpnm->code) {
2077 case PSN_SETACTIVE: /* Enable the Finish button */
2078 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2079 break;
2080
2081 case PSN_WIZNEXT:
2082 break;
2083
2084 case PSN_WIZFINISH:
2085 break;
2086
2087 case PSN_RESET:
2088 break;
2089
2090 default:
2091 break;
2092 }
2093 }
2094 return 0;
2095}
2096
2097void RunWizard(HWND hwnd)
2098{
2099 PROPSHEETPAGE psp = {0};
2100 HPROPSHEETPAGE ahpsp[4] = {0};
2101 PROPSHEETHEADER psh = {0};
2102
2103 /* Display module information */
2104 psp.dwSize = sizeof(psp);
2105 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2106 psp.hInstance = GetModuleHandle (NULL);
2107 psp.lParam = 0;
2108 psp.pfnDlgProc = IntroDlgProc;
2109 psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO);
2110
2111 ahpsp[0] = CreatePropertySheetPage(&psp);
2112
2113 /* Select python version to use */
2114 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2115 psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON);
2116 psp.pfnDlgProc = SelectPythonDlgProc;
2117
2118 ahpsp[1] = CreatePropertySheetPage(&psp);
2119
2120 /* Install the files */
2121 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2122 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES);
2123 psp.pfnDlgProc = InstallFilesDlgProc;
2124
2125 ahpsp[2] = CreatePropertySheetPage(&psp);
2126
2127 /* Show success or failure */
2128 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2129 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED);
2130 psp.pfnDlgProc = FinishedDlgProc;
2131
2132 ahpsp[3] = CreatePropertySheetPage(&psp);
2133
2134 /* Create the property sheet */
2135 psh.dwSize = sizeof(psh);
2136 psh.hInstance = GetModuleHandle(NULL);
2137 psh.hwndParent = hwnd;
2138 psh.phpage = ahpsp;
2139 psh.dwFlags = PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
2140 psh.pszbmWatermark = NULL;
2141 psh.pszbmHeader = NULL;
2142 psh.nStartPage = 0;
2143 psh.nPages = 4;
2144
2145 PropertySheet(&psh);
2146}
2147
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002148// subtly different from HasLocalMachinePrivs(), in that after executing
2149// an 'elevated' process, we expect this to return TRUE - but there is no
2150// such implication for HasLocalMachinePrivs
2151BOOL MyIsUserAnAdmin()
2152{
2153 typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
2154 static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
2155 HMODULE shell32;
2156 // This function isn't guaranteed to be available (and it can't hurt
2157 // to leave the library loaded)
2158 if (0 == (shell32=LoadLibrary("shell32.dll")))
2159 return FALSE;
2160 if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
2161 return FALSE;
2162 return (*pfnIsUserAnAdmin)();
2163}
2164
2165// Some magic for Vista's UAC. If there is a target_version, and
2166// if that target version is installed in the registry under
2167// HKLM, and we are not current administrator, then
2168// re-execute ourselves requesting elevation.
2169// Split into 2 functions - "should we elevate" and "spawn elevated"
2170
2171// Returns TRUE if we should spawn an elevated child
2172BOOL NeedAutoUAC()
2173{
2174 HKEY hk;
2175 char key_name[80];
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002176 // no Python version info == we can't know yet.
2177 if (target_version[0] == '\0')
2178 return FALSE;
2179 // see how python is current installed
2180 wsprintf(key_name,
2181 "Software\\Python\\PythonCore\\%s\\InstallPath",
2182 target_version);
2183 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2184 key_name, 0, KEY_READ, &hk))
2185 return FALSE;
2186 RegCloseKey(hk);
2187 // Python is installed in HKLM - we must elevate.
2188 return TRUE;
2189}
2190
Georg Brandl26adf522008-07-16 02:02:25 +00002191// Returns TRUE if the platform supports UAC.
2192BOOL PlatformSupportsUAC()
2193{
2194 // Note that win2k does seem to support ShellExecute with 'runas',
2195 // but does *not* support IsUserAnAdmin - so we just pretend things
2196 // only work on XP and later.
2197 BOOL bIsWindowsXPorLater;
2198 OSVERSIONINFO winverinfo;
2199 winverinfo.dwOSVersionInfoSize = sizeof(winverinfo);
2200 if (!GetVersionEx(&winverinfo))
2201 return FALSE; // something bad has gone wrong
2202 bIsWindowsXPorLater =
2203 ( (winverinfo.dwMajorVersion > 5) ||
2204 ( (winverinfo.dwMajorVersion == 5) && (winverinfo.dwMinorVersion >= 1) ));
2205 return bIsWindowsXPorLater;
2206}
2207
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002208// Spawn ourself as an elevated application. On failure, a message is
2209// displayed to the user - but this app will always terminate, even
2210// on error.
2211void SpawnUAC()
2212{
2213 // interesting failure scenario that has been seen: initial executable
2214 // runs from a network drive - but once elevated, that network share
2215 // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2216 int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
2217 SW_SHOWNORMAL);
2218 if (ret <= 32) {
2219 char msg[128];
2220 wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
2221 MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
2222 }
2223}
2224
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002225int DoInstall(void)
2226{
2227 char ini_buffer[4096];
2228
2229 /* Read installation information */
2230 GetPrivateProfileString("Setup", "title", "", ini_buffer,
2231 sizeof(ini_buffer), ini_file);
2232 unescape(title, ini_buffer, sizeof(title));
2233
2234 GetPrivateProfileString("Setup", "info", "", ini_buffer,
2235 sizeof(ini_buffer), ini_file);
2236 unescape(info, ini_buffer, sizeof(info));
2237
2238 GetPrivateProfileString("Setup", "build_info", "", build_info,
2239 sizeof(build_info), ini_file);
2240
2241 pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2242 ini_file);
2243 pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2244 ini_file);
2245
2246 GetPrivateProfileString("Setup", "target_version", "",
2247 target_version, sizeof(target_version),
2248 ini_file);
2249
2250 GetPrivateProfileString("metadata", "name", "",
2251 meta_name, sizeof(meta_name),
2252 ini_file);
2253
2254 GetPrivateProfileString("Setup", "install_script", "",
2255 install_script, sizeof(install_script),
2256 ini_file);
2257
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002258 GetPrivateProfileString("Setup", "user_access_control", "",
2259 user_access_control, sizeof(user_access_control), ini_file);
2260
2261 // See if we need to do the Vista UAC magic.
2262 if (strcmp(user_access_control, "force")==0) {
Georg Brandl26adf522008-07-16 02:02:25 +00002263 if (PlatformSupportsUAC() && !MyIsUserAnAdmin()) {
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002264 SpawnUAC();
2265 return 0;
2266 }
2267 // already admin - keep going
2268 } else if (strcmp(user_access_control, "auto")==0) {
2269 // Check if it looks like we need UAC control, based
2270 // on how Python itself was installed.
Georg Brandl26adf522008-07-16 02:02:25 +00002271 if (PlatformSupportsUAC() && !MyIsUserAnAdmin() && NeedAutoUAC()) {
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002272 SpawnUAC();
2273 return 0;
2274 }
2275 } else {
2276 // display a warning about unknown values - only the developer
2277 // of the extension will see it (until they fix it!)
2278 if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
2279 MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
2280 // nothing to do.
2281 }
2282 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002283
2284 hwndMain = CreateBackground(title);
2285
2286 RunWizard(hwndMain);
2287
2288 /* Clean up */
2289 UnmapViewOfFile(arc_data);
2290 if (ini_file)
2291 DeleteFile(ini_file);
2292
2293 if (hBitmap)
2294 DeleteObject(hBitmap);
2295
2296 return 0;
2297}
2298
2299/*********************** uninstall section ******************************/
2300
2301static int compare(const void *p1, const void *p2)
2302{
2303 return strcmp(*(char **)p2, *(char **)p1);
2304}
2305
2306/*
2307 * Commit suicide (remove the uninstaller itself).
2308 *
2309 * Create a batch file to first remove the uninstaller
2310 * (will succeed after it has finished), then the batch file itself.
2311 *
2312 * This technique has been demonstrated by Jeff Richter,
2313 * MSJ 1/1996
2314 */
2315void remove_exe(void)
2316{
2317 char exename[_MAX_PATH];
2318 char batname[_MAX_PATH];
2319 FILE *fp;
2320 STARTUPINFO si;
2321 PROCESS_INFORMATION pi;
2322
2323 GetModuleFileName(NULL, exename, sizeof(exename));
2324 sprintf(batname, "%s.bat", exename);
2325 fp = fopen(batname, "w");
2326 fprintf(fp, ":Repeat\n");
2327 fprintf(fp, "del \"%s\"\n", exename);
2328 fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
2329 fprintf(fp, "del \"%s\"\n", batname);
2330 fclose(fp);
2331
2332 ZeroMemory(&si, sizeof(si));
2333 si.cb = sizeof(si);
2334 si.dwFlags = STARTF_USESHOWWINDOW;
2335 si.wShowWindow = SW_HIDE;
2336 if (CreateProcess(NULL,
2337 batname,
2338 NULL,
2339 NULL,
2340 FALSE,
2341 CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
2342 NULL,
2343 "\\",
2344 &si,
2345 &pi)) {
2346 SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
2347 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2348 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
2349 CloseHandle(pi.hProcess);
2350 ResumeThread(pi.hThread);
2351 CloseHandle(pi.hThread);
2352 }
2353}
2354
2355void DeleteRegistryKey(char *string)
2356{
2357 char *keyname;
2358 char *subkeyname;
2359 char *delim;
2360 HKEY hKey;
2361 long result;
2362 char *line;
2363
2364 line = strdup(string); /* so we can change it */
2365
2366 keyname = strchr(line, '[');
2367 if (!keyname)
2368 return;
2369 ++keyname;
2370
2371 subkeyname = strchr(keyname, ']');
2372 if (!subkeyname)
2373 return;
2374 *subkeyname++='\0';
2375 delim = strchr(subkeyname, '\n');
2376 if (delim)
2377 *delim = '\0';
2378
Mark Hammondf9bfdd82004-07-02 23:53:16 +00002379 result = RegOpenKeyEx(hkey_root,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002380 keyname,
2381 0,
2382 KEY_WRITE,
2383 &hKey);
2384
2385 if (result != ERROR_SUCCESS)
2386 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2387 else {
2388 result = RegDeleteKey(hKey, subkeyname);
Thomas Heller55a98642004-07-14 14:53:50 +00002389 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002390 MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2391 RegCloseKey(hKey);
2392 }
2393 free(line);
2394}
2395
2396void DeleteRegistryValue(char *string)
2397{
2398 char *keyname;
2399 char *valuename;
2400 char *value;
2401 HKEY hKey;
2402 long result;
2403 char *line;
2404
2405 line = strdup(string); /* so we can change it */
2406
2407/* Format is 'Reg DB Value: [key]name=value' */
2408 keyname = strchr(line, '[');
2409 if (!keyname)
2410 return;
2411 ++keyname;
2412 valuename = strchr(keyname, ']');
2413 if (!valuename)
2414 return;
2415 *valuename++ = '\0';
2416 value = strchr(valuename, '=');
2417 if (!value)
2418 return;
2419
2420 *value++ = '\0';
2421
Mark Hammondf9bfdd82004-07-02 23:53:16 +00002422 result = RegOpenKeyEx(hkey_root,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002423 keyname,
2424 0,
2425 KEY_WRITE,
2426 &hKey);
2427 if (result != ERROR_SUCCESS)
2428 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2429 else {
2430 result = RegDeleteValue(hKey, valuename);
Thomas Heller55a98642004-07-14 14:53:50 +00002431 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002432 MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2433 RegCloseKey(hKey);
2434 }
2435 free(line);
2436}
2437
2438BOOL MyDeleteFile(char *line)
2439{
2440 char *pathname = strchr(line, ':');
2441 if (!pathname)
2442 return FALSE;
2443 ++pathname;
2444 while (isspace(*pathname))
2445 ++pathname;
2446 return DeleteFile(pathname);
2447}
2448
2449BOOL MyRemoveDirectory(char *line)
2450{
2451 char *pathname = strchr(line, ':');
2452 if (!pathname)
2453 return FALSE;
2454 ++pathname;
2455 while (isspace(*pathname))
2456 ++pathname;
2457 return RemoveDirectory(pathname);
2458}
2459
2460BOOL Run_RemoveScript(char *line)
2461{
2462 char *dllname;
2463 char *scriptname;
2464 static char lastscript[MAX_PATH];
2465
2466/* Format is 'Run Scripts: [pythondll]scriptname' */
2467/* XXX Currently, pythondll carries no path!!! */
2468 dllname = strchr(line, '[');
2469 if (!dllname)
2470 return FALSE;
2471 ++dllname;
2472 scriptname = strchr(dllname, ']');
2473 if (!scriptname)
2474 return FALSE;
2475 *scriptname++ = '\0';
2476 /* this function may be called more than one time with the same
2477 script, only run it one time */
2478 if (strcmp(lastscript, scriptname)) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002479 char *argv[3] = {NULL, "-remove", NULL};
Mark Hammond6d0e9752009-01-29 12:36:50 +00002480 char *buffer = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002481
2482 argv[0] = scriptname;
2483
Mark Hammond6d0e9752009-01-29 12:36:50 +00002484 if (0 != run_installscript(scriptname, 2, argv, &buffer))
2485 fprintf(stderr, "*** Could not run installation script ***");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002486
Mark Hammond6d0e9752009-01-29 12:36:50 +00002487 if (buffer && buffer[0])
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002488 MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
Mark Hammond6d0e9752009-01-29 12:36:50 +00002489 free(buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002490
2491 strcpy(lastscript, scriptname);
2492 }
2493 return TRUE;
2494}
2495
2496int DoUninstall(int argc, char **argv)
2497{
2498 FILE *logfile;
2499 char buffer[4096];
2500 int nLines = 0;
2501 int i;
2502 char *cp;
2503 int nFiles = 0;
2504 int nDirs = 0;
2505 int nErrors = 0;
2506 char **lines;
2507 int lines_buffer_size = 10;
2508
2509 if (argc != 3) {
2510 MessageBox(NULL,
2511 "Wrong number of args",
2512 NULL,
2513 MB_OK);
2514 return 1; /* Error */
2515 }
2516 if (strcmp(argv[1], "-u")) {
2517 MessageBox(NULL,
2518 "2. arg is not -u",
2519 NULL,
2520 MB_OK);
2521 return 1; /* Error */
2522 }
2523
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002524 logfile = fopen(argv[2], "r");
2525 if (!logfile) {
2526 MessageBox(NULL,
2527 "could not open logfile",
2528 NULL,
2529 MB_OK);
2530 return 1; /* Error */
2531 }
2532
2533 lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2534 if (!lines)
2535 return SystemError(0, "Out of memory");
2536
2537 /* Read the whole logfile, realloacting the buffer */
2538 while (fgets(buffer, sizeof(buffer), logfile)) {
2539 int len = strlen(buffer);
2540 /* remove trailing white space */
2541 while (isspace(buffer[len-1]))
2542 len -= 1;
2543 buffer[len] = '\0';
2544 lines[nLines++] = strdup(buffer);
2545 if (nLines >= lines_buffer_size) {
2546 lines_buffer_size += 10;
2547 lines = (char **)realloc(lines,
2548 sizeof(char *) * lines_buffer_size);
2549 if (!lines)
2550 return SystemError(0, "Out of memory");
2551 }
2552 }
2553 fclose(logfile);
2554
2555 /* Sort all the lines, so that highest 3-digit codes are first */
2556 qsort(&lines[0], nLines, sizeof(char *),
2557 compare);
2558
2559 if (IDYES != MessageBox(NULL,
2560 "Are you sure you want to remove\n"
2561 "this package from your computer?",
2562 "Please confirm",
2563 MB_YESNO | MB_ICONQUESTION))
2564 return 0;
2565
Mark Hammondf9bfdd82004-07-02 23:53:16 +00002566 hkey_root = HKEY_LOCAL_MACHINE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002567 cp = "";
2568 for (i = 0; i < nLines; ++i) {
2569 /* Ignore duplicate lines */
2570 if (strcmp(cp, lines[i])) {
2571 int ign;
2572 cp = lines[i];
2573 /* Parse the lines */
Mark Hammondf9bfdd82004-07-02 23:53:16 +00002574 if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
2575 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
2576 hkey_root = HKEY_CURRENT_USER;
2577 else {
2578 // HKLM - check they have permissions.
2579 if (!HasLocalMachinePrivs()) {
2580 MessageBox(GetFocus(),
2581 "You do not seem to have sufficient access rights\n"
2582 "on this machine to uninstall this software",
2583 NULL,
2584 MB_OK | MB_ICONSTOP);
2585 return 1; /* Error */
2586 }
2587 }
2588 } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002589 if (MyRemoveDirectory(cp))
2590 ++nDirs;
2591 else {
2592 int code = GetLastError();
2593 if (code != 2 && code != 3) { /* file or path not found */
2594 ++nErrors;
2595 }
2596 }
2597 } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
2598 if (MyDeleteFile(cp))
2599 ++nFiles;
2600 else {
2601 int code = GetLastError();
2602 if (code != 2 && code != 3) { /* file or path not found */
2603 ++nErrors;
2604 }
2605 }
2606 } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
2607 if (MyDeleteFile(cp))
2608 ++nFiles;
2609 else {
2610 int code = GetLastError();
2611 if (code != 2 && code != 3) { /* file or path not found */
2612 ++nErrors;
2613 }
2614 }
2615 } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
2616 DeleteRegistryKey(cp);
2617 } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
2618 DeleteRegistryValue(cp);
2619 } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
2620 Run_RemoveScript(cp);
2621 }
2622 }
2623 }
2624
2625 if (DeleteFile(argv[2])) {
2626 ++nFiles;
2627 } else {
2628 ++nErrors;
2629 SystemError(GetLastError(), argv[2]);
2630 }
2631 if (nErrors)
2632 wsprintf(buffer,
2633 "%d files and %d directories removed\n"
2634 "%d files or directories could not be removed",
2635 nFiles, nDirs, nErrors);
2636 else
2637 wsprintf(buffer, "%d files and %d directories removed",
2638 nFiles, nDirs);
2639 MessageBox(NULL, buffer, "Uninstall Finished!",
2640 MB_OK | MB_ICONINFORMATION);
2641 remove_exe();
2642 return 0;
2643}
2644
2645int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
2646 LPSTR lpszCmdLine, INT nCmdShow)
2647{
2648 extern int __argc;
2649 extern char **__argv;
2650 char *basename;
2651
2652 GetModuleFileName(NULL, modulename, sizeof(modulename));
Mark Hammond891f2632009-01-29 13:08:01 +00002653 GetModuleFileNameW(NULL, wmodulename, sizeof(wmodulename)/sizeof(wmodulename[0]));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002654
2655 /* Map the executable file to memory */
2656 arc_data = MapExistingFile(modulename, &arc_size);
2657 if (!arc_data) {
2658 SystemError(GetLastError(), "Could not open archive");
2659 return 1;
2660 }
2661
2662 /* OK. So this program can act as installer (self-extracting
2663 * zip-file, or as uninstaller when started with '-u logfile'
2664 * command line flags.
2665 *
2666 * The installer is usually started without command line flags,
2667 * and the uninstaller is usually started with the '-u logfile'
2668 * flag. What to do if some innocent user double-clicks the
2669 * exe-file?
2670 * The following implements a defensive strategy...
2671 */
2672
2673 /* Try to extract the configuration data into a temporary file */
Thomas Hellera19cdad2004-02-20 14:43:21 +00002674 if (ExtractInstallData(arc_data, arc_size, &exe_size,
2675 &ini_file, &pre_install_script))
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002676 return DoInstall();
2677
2678 if (!ini_file && __argc > 1) {
2679 return DoUninstall(__argc, __argv);
2680 }
2681
2682
2683 basename = strrchr(modulename, '\\');
2684 if (basename)
2685 ++basename;
2686
2687 /* Last guess about the purpose of this program */
2688 if (basename && (0 == strncmp(basename, "Remove", 6)))
2689 SystemError(0, "This program is normally started by windows");
2690 else
2691 SystemError(0, "Setup program invalid or damaged");
2692 return 1;
2693}