blob: 771922cfd6461a09bc9158b0266d1aa754d2d9bd [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:
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000023 * [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
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000031 *
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000032 * [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
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000038 *
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
Ezio Melotti42da6632011-03-15 05:18:48 +020065 * additionally to those found in the registry,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000066 * 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
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000122char *ini_file; /* Full pathname of ini-file */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000123/* From ini-file */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000124char 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 */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000131
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000132char meta_name[80]; /* package name without version like
133 'Distutils' */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000134char 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
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000139int py_major, py_minor; /* Python version selected for installation */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000140
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000141char *arc_data; /* memory mapped archive */
142DWORD arc_size; /* number of bytes in archive */
143int exe_size; /* number of bytes for exe-file portion */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000144char 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
Ezio Melotti13925002011-03-16 11:05:33 +0200151BOOL success; /* Installation successful? */
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[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000169 { "PURELIB", "" },
170 { "PLATLIB", "" },
171 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
172 { "SCRIPTS", "Scripts\\" },
173 { "DATA", "" },
174 { NULL, NULL },
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000175};
176
177SCHEME new_scheme[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000178 { "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 },
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000184};
185
186static void unescape(char *dst, char *src, unsigned size)
187{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000188 char *eon;
189 char ch;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000190
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000191 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';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000221}
222
223static struct tagFile {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000224 char *path;
225 struct tagFile *next;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000226} *file_list = NULL;
227
Thomas Hellera19cdad2004-02-20 14:43:21 +0000228static void set_failure_reason(char *reason)
229{
230 if (failure_reason)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000231 free(failure_reason);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000232 failure_reason = strdup(reason);
233 success = FALSE;
234}
235static char *get_failure_reason()
236{
237 if (!failure_reason)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000238 return "Installation failed.";
Thomas Hellera19cdad2004-02-20 14:43:21 +0000239 return failure_reason;
240}
241
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000242static void add_to_filelist(char *path)
243{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000244 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000249}
250
251static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000252 int optimize)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000253{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000254 struct tagFile *p;
255 int total, n;
256 char Buffer[MAX_PATH + 64];
257 int errors = 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000258
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000259 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000268
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000269 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000285
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000286 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
287 SetDlgItemText(hDialog, IDC_INFO, p->path);
288 p = p->next;
289 }
290 return errors;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000291}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000307 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;
Mark Hammond891f2632009-01-29 13:08:01 +0000323}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000331 DECLPROC(hPython, void, Py_Initialize, (void));
332 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
333 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000337
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000338 int errors = 0;
339 struct tagFile *p = file_list;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000340
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000341 if (!p)
342 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000343
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000344 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize)
345 return -1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000346
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000347 if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag)
348 return -1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000349
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000350 *Py_OptimizeFlag = optimize_flag ? 1 : 0;
351 Py_SetProgramName(wmodulename);
352 Py_Initialize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000353
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000354 errors += do_compile_files(PyRun_SimpleString, optimize_flag);
355 Py_Finalize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000356
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000357 return errors;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000358}
359
360typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
361
362struct PyMethodDef {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000363 char *ml_name;
364 PyCFunction ml_meth;
365 int ml_flags;
366 char *ml_doc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000367};
368typedef struct PyMethodDef PyMethodDef;
369
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000370// XXX - all of these are potentially fragile! We load and unload
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000371// the Python DLL multiple times - so storing functions pointers
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000372// 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 {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000388 int nFolder;
389 char *name;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000390} csidl_names[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000391 /* Startup menu for all users.
392 NT only */
393 DEF_CSIDL(CSIDL_COMMON_STARTMENU),
394 /* Startup menu. */
395 DEF_CSIDL(CSIDL_STARTMENU),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000396
397/* DEF_CSIDL(CSIDL_COMMON_APPDATA), */
398/* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000399 /* Repository for application-specific data.
400 Needs Internet Explorer 4.0 */
401 DEF_CSIDL(CSIDL_APPDATA),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000402
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000403 /* The desktop for all users.
404 NT only */
405 DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY),
406 /* The desktop. */
407 DEF_CSIDL(CSIDL_DESKTOPDIRECTORY),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000408
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000409 /* Startup folder for all users.
410 NT only */
411 DEF_CSIDL(CSIDL_COMMON_STARTUP),
412 /* Startup folder. */
413 DEF_CSIDL(CSIDL_STARTUP),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000414
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000415 /* 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),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000420
421/* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
422/* DEF_CSIDL(CSIDL_PROGRAM_FILES), */
423
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000424 /* Virtual folder containing fonts. */
425 DEF_CSIDL(CSIDL_FONTS),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000426};
427
428#define DIM(a) (sizeof(a) / sizeof((a)[0]))
429
430static PyObject *FileCreated(PyObject *self, PyObject *args)
431{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000432 char *path;
433 if (!g_PyArg_ParseTuple(args, "s", &path))
434 return NULL;
435 notify(FILE_CREATED, path);
436 return g_Py_BuildValue("");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000437}
438
439static PyObject *DirectoryCreated(PyObject *self, PyObject *args)
440{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000441 char *path;
442 if (!g_PyArg_ParseTuple(args, "s", &path))
443 return NULL;
444 notify(DIR_CREATED, path);
445 return g_Py_BuildValue("");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000446}
447
448static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args)
449{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000450 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000457
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000458 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000470
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000471 if (!g_PyArg_ParseTuple(args, "s", &name))
472 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000473
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000474 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000496}
497
498static PyObject *CreateShortcut(PyObject *self, PyObject *args)
499{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000500 char *path; /* path and filename */
501 char *description;
502 char *filename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000503
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000504 char *arguments = NULL;
505 char *iconpath = NULL;
506 int iconindex = 0;
507 char *workdir = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000508
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000509 WCHAR wszFilename[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000510
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000511 IShellLink *ps1 = NULL;
512 IPersistFile *pPf = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000513
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000514 HRESULT hr;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000515
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000516 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000522
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000523 if (!g_PyArg_ParseTuple(args, "sss|sssi",
524 &path, &description, &filename,
525 &arguments, &workdir, &iconpath, &iconindex))
526 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000527
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000528 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000538
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000539 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000546
547
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000548 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000554
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000555 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000561
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000562 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000570
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000571 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000579
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000580 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000588
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000589 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,
596 "Failed to create shortcut '%s' - error 0x%x", filename, hr);
597 goto error;
598 }
599
600 pPf->lpVtbl->Release(pPf);
601 ps1->lpVtbl->Release(ps1);
602 CoUninitialize();
603 return g_Py_BuildValue("");
604
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000605 error:
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000606 if (pPf)
607 pPf->lpVtbl->Release(pPf);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000608
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000609 if (ps1)
610 ps1->lpVtbl->Release(ps1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000611
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000612 CoUninitialize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000613
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000614 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000615}
616
Thomas Hellera19cdad2004-02-20 14:43:21 +0000617static PyObject *PyMessageBox(PyObject *self, PyObject *args)
618{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000619 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);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000626}
627
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000628static PyObject *GetRootHKey(PyObject *self)
629{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +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[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000638 {"create_shortcut", CreateShortcut, METH_VARARGS, NULL},
639 {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
640 {"get_root_hkey", (PyCFunction)GetRootHKey, METH_NOARGS, NULL},
641 {"file_created", FileCreated, METH_VARARGS, NULL},
642 {"directory_created", DirectoryCreated, METH_VARARGS, NULL},
643 {"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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000648 char fullpath[_MAX_PATH];
649 LONG size = sizeof(fullpath);
650 char subkey_name[80];
651 char buffer[260 + 12];
652 HINSTANCE h;
Thomas Heller8abe7bf2005-02-03 20:11:28 +0000653
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000654 /* 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);
658 if (h)
659 return h;
660 wsprintf(subkey_name,
661 "SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath",
662 py_major, py_minor);
663 if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
664 fullpath, &size) &&
665 ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
666 fullpath, &size))
667 return NULL;
668 strcat(fullpath, "\\");
669 strcat(fullpath, fname);
670 return LoadLibrary(fullpath);
Thomas Heller48340392004-06-18 17:03:38 +0000671}
672
Thomas Hellera19cdad2004-02-20 14:43:21 +0000673static int prepare_script_environment(HINSTANCE hPython)
674{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000675 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 *));
683 DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
684 if (!PyImport_ImportModule || !PyObject_GetAttrString ||
685 !PyObject_SetAttrString || !PyCFunction_New)
686 return 1;
687 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
688 return 1;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000689
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000690 mod = PyImport_ImportModule("builtins");
691 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;
703 g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000704
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000705 return 0;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000706}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000721 int fh, result, i;
722 static wchar_t *wargv[256];
723 DECLPROC(hPython, void, Py_Initialize, (void));
724 DECLPROC(hPython, int, PySys_SetArgv, (int, wchar_t **));
725 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
726 DECLPROC(hPython, void, Py_Finalize, (void));
727 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 *));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000732
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000733 if (!Py_Initialize || !PySys_SetArgv
734 || !PyRun_SimpleString || !Py_Finalize)
735 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000736
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000737 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
738 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000739
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000740 if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
741 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000742
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000743 if (pathname == NULL || pathname[0] == '\0')
744 return 2;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000745
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000746 fh = open(pathname, _O_RDONLY);
747 if (-1 == fh) {
748 fprintf(stderr, "Could not open postinstall-script %s\n",
749 pathname);
750 return 3;
751 }
Mark Hammond6d0e9752009-01-29 12:36:50 +0000752
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000753 SetDlgItemText(hDialog, IDC_INFO, "Running Script...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000754
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000755 Py_Initialize();
Mark Hammond891f2632009-01-29 13:08:01 +0000756
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000757 prepare_script_environment(hPython);
758 // 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]);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000767
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +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 }
781 Py_Finalize();
782
783 close(fh);
784 return result;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000785}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000790 HINSTANCE hPython;
791 int result = 1;
792 int out_buf_size;
793 HANDLE redirected, old_stderr, old_stdout;
794 char *tempname;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000795
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000796 *pOutput = NULL;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000797
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000798 tempname = tempnam(NULL, NULL);
799 // We use a static CRT while the Python version we load uses
Ezio Melotti13925002011-03-16 11:05:33 +0200800 // the CRT from one of various possible DLLs. As a result we
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000801 // 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);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000815
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000816 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);
835 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000836}
837
Thomas Hellera19cdad2004-02-20 14:43:21 +0000838static int do_run_simple_script(HINSTANCE hPython, char *script)
839{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000840 int rc;
841 DECLPROC(hPython, void, Py_Initialize, (void));
842 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
843 DECLPROC(hPython, void, Py_Finalize, (void));
844 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
845 DECLPROC(hPython, void, PyErr_Print, (void));
Thomas Hellera19cdad2004-02-20 14:43:21 +0000846
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000847 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
848 !PyRun_SimpleString || !PyErr_Print)
849 return -1;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000850
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000851 Py_SetProgramName(wmodulename);
852 Py_Initialize();
853 prepare_script_environment(hPython);
854 rc = PyRun_SimpleString(script);
855 if (rc)
856 PyErr_Print();
857 Py_Finalize();
858 return rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000859}
860
861static int run_simple_script(char *script)
862{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000863 int rc;
864 HINSTANCE hPython;
865 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
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000880 hPython = LoadPythonDll(pythondll);
881 if (!hPython) {
882 char reason[128];
883 wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
884 set_failure_reason(reason);
885 return -1;
886 }
887 rc = do_run_simple_script(hPython, script);
888 FreeLibrary(hPython);
889 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
890 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
891 /* 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);
899 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;
902 err_buf = malloc(prefix_len + err_buf_size + 1);
903 if (err_buf) {
904 DWORD n = 0;
905 strcpy(err_buf, prefix);
906 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
907 ReadFile(redirected, err_buf+prefix_len, err_buf_size, &n, NULL);
908 err_buf[prefix_len+n] = '\0';
909 set_failure_reason(err_buf);
910 free(err_buf);
911 } else {
912 set_failure_reason("Out of memory!");
913 }
914 }
915 CloseHandle(redirected);
916 DeleteFile(tempname);
917 return rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000918}
919
920
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000921static BOOL SystemError(int error, char *msg)
922{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000923 char Buffer[1024];
924 int n;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000925
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000926 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000946}
947
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000948static BOOL notify (int code, char *fmt, ...)
949{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000950 char Buffer[1024];
951 va_list marker;
952 BOOL result = TRUE;
953 int a, b;
954 char *cp;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000955
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000956 va_start(marker, fmt);
957 _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000958
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000959 switch (code) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000960/* Questions */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000961 case CAN_OVERWRITE:
962 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000963
964/* Information notification */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000965 case DIR_CREATED:
966 if (logfile)
967 fprintf(logfile, "100 Made Dir: %s\n", fmt);
968 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000969
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000970 case FILE_CREATED:
971 if (logfile)
972 fprintf(logfile, "200 File Copy: %s\n", fmt);
973 goto add_to_filelist_label;
974 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000975
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000976 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000983
984/* Error Messages */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000985 case ZLIB_ERROR:
986 MessageBox(GetFocus(), Buffer, "Error",
987 MB_OK | MB_ICONWARNING);
988 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000989
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000990 case SYSTEM_ERROR:
991 SystemError(GetLastError(), Buffer);
992 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000993
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000994 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001003}
1004
1005static char *MapExistingFile(char *pathname, DWORD *psize)
1006{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001007 HANDLE hFile, hFileMapping;
1008 DWORD nSizeLow, nSizeHigh;
1009 char *data;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001010
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001011 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001021
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001022 if (hFileMapping == INVALID_HANDLE_VALUE)
1023 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001024
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001025 data = MapViewOfFile(hFileMapping,
1026 FILE_MAP_READ, 0, 0, 0);
1027
1028 CloseHandle(hFileMapping);
1029 *psize = nSizeLow;
1030 return data;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001031}
1032
1033
1034static void create_bitmap(HWND hwnd)
1035{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001036 BITMAPFILEHEADER *bfh;
1037 BITMAPINFO *bi;
1038 HDC hdc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001039
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001040 if (!bitmap_bytes)
1041 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001042
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001043 if (hBitmap)
1044 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001045
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001046 hdc = GetDC(hwnd);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001047
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001048 bfh = (BITMAPFILEHEADER *)bitmap_bytes;
1049 bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001050
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001051 hBitmap = CreateDIBitmap(hdc,
1052 &bi->bmiHeader,
1053 CBM_INIT,
1054 bitmap_bytes + bfh->bfOffBits,
1055 bi,
1056 DIB_RGB_COLORS);
1057 ReleaseDC(hwnd, hdc);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001058}
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,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001064 char **out_ini_file, char **out_preinstall_script)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001065{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001066 /* read the end of central directory record */
1067 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
1068 (struct eof_cdir)];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001069
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001070 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
1071 pe->ofsCDir;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001072
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001073 int ofs = arc_start - sizeof (struct meta_data_hdr);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001074
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001075 /* 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];
Thomas Hellera19cdad2004-02-20 14:43:21 +00001080
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001081 /* ensure that if we fail, we don't have garbage out pointers */
1082 *out_ini_file = *out_preinstall_script = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001083
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001084 if (pe->tag != 0x06054b50) {
1085 return FALSE;
1086 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001087
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001088 if (pmd->tag != 0x1234567B) {
1089 return SystemError(0,
1090 "Invalid cfgdata magic number (see bdist_wininst.py)");
1091 }
1092 if (ofs < 0) {
1093 return FALSE;
1094 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001095
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001096 if (pmd->bitmap_size) {
1097 /* Store pointer to bitmap bytes */
1098 bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
1099 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001100
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001101 *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)
1106 return FALSE;
1107 if (!GetTempPath(sizeof(tempdir), tempdir)
1108 || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
1109 SystemError(GetLastError(),
1110 "Could not create temporary file");
1111 return FALSE;
1112 }
1113
1114 dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
1115 0, 0, NULL/*notify*/);
1116 if (!dst)
1117 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;
1124 UnmapViewOfFile(dst);
1125 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001126}
1127
1128static void PumpMessages(void)
1129{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001130 MSG msg;
1131 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1132 TranslateMessage(&msg);
1133 DispatchMessage(&msg);
1134 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001135}
1136
1137LRESULT CALLBACK
1138WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1139{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001140 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001160}
1161
1162static HWND CreateBackground(char *title)
1163{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001164 WNDCLASS wc;
1165 HWND hwnd;
1166 char buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001167
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001168 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";
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001178
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001179 if (!RegisterClass(&wc))
1180 MessageBox(hwndMain,
1181 "Could not register window class",
1182 "Setup.exe", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001183
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001184 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001198}
1199
1200/*
1201 * Center a window on the screen
1202 */
1203static void CenterWindow(HWND hwnd)
1204{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001205 RECT rc;
1206 int w, h;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001207
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001208 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001215}
1216
1217#include <prsht.h>
1218
1219BOOL CALLBACK
1220IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1221{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001222 LPNMHDR lpnm;
1223 char Buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001224
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001225 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001241
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001242 case WM_NOTIFY:
1243 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001244
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001245 switch (lpnm->code) {
1246 case PSN_SETACTIVE:
1247 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
1248 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001249
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001250 case PSN_WIZNEXT:
1251 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001252
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001253 case PSN_RESET:
1254 break;
1255
1256 default:
1257 break;
1258 }
1259 }
1260 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001261}
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,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001272 PSTR ImageName,
1273 PSTR DllName,
1274 ULONG Va,
1275 ULONG Parameter)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001276{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001277 char fname[_MAX_PATH];
1278 int int_version;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001279
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001280 switch(reason) {
1281 case BindOutOfMemory:
1282 case BindRvaToVaFailed:
1283 case BindNoRoomInImage:
1284 case BindImportProcedureFailed:
1285 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001286
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001287 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001297
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001298 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001312}
1313
1314/*
1315 */
1316static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
1317{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001318 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 *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001328
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001329 {
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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001337
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001338 hPython = LoadLibrary(dll);
1339 if (!hPython)
1340 return NULL;
1341 Py_Initialize = (void (*)(void))GetProcAddress
1342 (hPython,"Py_Initialize");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001343
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001344 PySys_SetArgv = (void (*)(int, char **))GetProcAddress
1345 (hPython,"PySys_SetArgv");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001346
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001347 PyRun_SimpleString = (int (*)(char *))GetProcAddress
1348 (hPython,"PyRun_SimpleString");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001349
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001350 Py_SetProgramName = (void (*)(char *))GetProcAddress
1351 (hPython,"Py_SetProgramName");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001352
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001353 PySys_GetObject = (void* (*)(char *))GetProcAddress
1354 (hPython,"PySys_GetObject");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001355
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001356 Py_GetPrefix = (char * (*)(void))GetProcAddress
1357 (hPython,"Py_GetPrefix");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001358
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001359 Py_GetPath = (char * (*)(void))GetProcAddress
1360 (hPython,"Py_GetPath");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001361
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001362 Py_Finalize = (void (*)(void))GetProcAddress(hPython,
1363 "Py_Finalize");
1364 Py_SetProgramName(exe);
1365 Py_Initialize();
1366 PySys_SetArgv(1, &exe);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001367
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001368 MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
1369 MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001370
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001371 Py_Finalize();
1372 FreeLibrary(hPython);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001373
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001374 return prefix;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001375}
1376
1377static BOOL
1378CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
1379{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001380 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001399}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001407 char vers_name[_MAX_PATH + 80];
1408 DWORD itemindex;
1409 OPENFILENAME of;
1410 char pathname[_MAX_PATH];
1411 DWORD result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001412
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001413 strcpy(pathname, "python.exe");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001414
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001415 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";
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001431
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001432 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001449}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001465 DWORD index = 0;
1466 char core_version[80];
1467 HKEY hKey;
1468 BOOL result = TRUE;
1469 DWORD bufsize;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001470
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001471 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)) {
1479 char subkey_name[80], vers_name[80];
1480 int itemindex;
1481 DWORD value_size;
1482 HKEY hk;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001483
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001484 bufsize = sizeof(core_version);
1485 ++index;
1486 if (version && *version && strcmp(version, core_version))
1487 continue;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001488
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001489 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);
1494 if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
1495 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)) {
1501 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
1502 (LPARAM)(LPSTR)vers_name);
1503 ivi->hkey = hkRoot;
1504 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1505 (LPARAM)(LPSTR)ivi);
1506 }
1507 RegCloseKey(hk);
1508 }
1509 }
1510 RegCloseKey(hKey);
1511 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001512}
1513
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001514/* Determine if the current user can write to HKEY_LOCAL_MACHINE */
1515BOOL HasLocalMachinePrivs()
1516{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001517 HKEY hKey;
1518 DWORD result;
1519 static char KeyName[] =
1520 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001521
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001522 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;
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001530}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001542 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 }
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001556}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001561 if (major > 2)
1562 return new_scheme;
1563 else if((major == 2) && (minor >= 2))
1564 return new_scheme;
1565 return old_scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001566}
1567
1568BOOL CALLBACK
1569SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1570{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001571 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001572
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001573 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001590
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001591 /* 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001602
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001603 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)) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001622/*
1623 case IDC_OTHERPYTHON:
1624 if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1625 target_version))
1626 goto UpdateInstallDir;
1627 break;
1628*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001629 case IDC_VERSIONS_LIST:
1630 switch (HIWORD(wParam)) {
1631 int id;
1632 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;
1648 InstalledVersionInfo *ivi;
1649 PropSheet_SetWizButtons(GetParent(hwnd),
1650 PSWIZB_BACK | PSWIZB_NEXT);
1651 /* Get the python directory */
1652 ivi = (InstalledVersionInfo *)
1653 SendDlgItemMessage(hwnd,
1654 IDC_VERSIONS_LIST,
1655 LB_GETITEMDATA,
1656 id,
1657 0);
1658 hkey_root = ivi->hkey;
1659 strcpy(python_dir, ivi->prefix);
1660 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);
1672 if (result == 2) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001673#ifdef _DEBUG
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001674 wsprintf(pythondll, "python%d%d_d.dll",
1675 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001676#else
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001677 wsprintf(pythondll, "python%d%d.dll",
1678 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001679#endif
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001680 }
1681 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001699
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001700 case WM_NOTIFY:
1701 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001702
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001703 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001715
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001716 case PSN_WIZNEXT:
1717 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001718
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001719 case PSN_WIZFINISH:
1720 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001721
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001722 case PSN_RESET:
1723 break;
1724
1725 default:
1726 break;
1727 }
1728 }
1729 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001730}
1731
1732static BOOL OpenLogfile(char *dir)
1733{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001734 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";
1742 const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
1743 "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
1744 DWORD disposition;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001745
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +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,
1750 KeyName,
1751 0, /* reserved */
1752 NULL, /* class */
1753 0, /* options */
1754 KEY_CREATE_SUB_KEY, /* sam */
1755 NULL, /* security */
1756 &hKey, /* result key */
1757 NULL); /* disposition */
1758 if (result != ERROR_SUCCESS) {
1759 if (result == ERROR_ACCESS_DENIED) {
1760 /* 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 */
1764 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001774
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001775 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001784
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001785 /* Root key must be first entry processed by uninstaller. */
1786 fprintf(logfile, "999 Root Key: %s\n", root_name);
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001787
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001788 sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001789
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001790 result = RegCreateKeyEx(hKey, subkey_name,
1791 0, NULL, 0,
1792 KEY_WRITE,
1793 NULL,
1794 &hSubkey,
1795 &disposition);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001796
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001797 if (result != ERROR_SUCCESS)
1798 MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001799
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001800 RegCloseKey(hKey);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001801
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001802 if (disposition == REG_CREATED_NEW_KEY)
1803 fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001804
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001805 sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001806
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001807 result = RegSetValueEx(hSubkey, "DisplayName",
1808 0,
1809 REG_SZ,
1810 buffer,
1811 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001812
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001813 if (result != ERROR_SUCCESS)
1814 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001815
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001816 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1817 KeyName, subkey_name, "DisplayName", buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001818
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001819 {
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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001825
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001826 sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1827 dir, meta_name, dir, meta_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001828
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001829 result = RegSetValueEx(hSubkey, "UninstallString",
1830 0,
1831 REG_SZ,
1832 buffer,
1833 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001834
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001835 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001842}
1843
1844static void CloseLogfile(void)
1845{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001846 char buffer[_MAX_PATH+1];
1847 time_t ltime;
1848 struct tm *now;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001849
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001850 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001858}
1859
1860BOOL CALLBACK
1861InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1862{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001863 LPNMHDR lpnm;
1864 char Buffer[4096];
1865 SCHEME *scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001866
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001867 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);
1879 SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
1880 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001881
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001882 case WM_NUMFILES:
1883 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1884 PumpMessages();
1885 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001886
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001887 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001893
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001894 case WM_NOTIFY:
1895 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001896
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001897 switch (lpnm->code) {
1898 case PSN_SETACTIVE:
1899 PropSheet_SetWizButtons(GetParent(hwnd),
1900 PSWIZB_BACK | PSWIZB_NEXT);
1901 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001902
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001903 case PSN_WIZFINISH:
1904 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001905
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001906 case PSN_WIZNEXT:
1907 /* Handle a Next button click here */
1908 hDialog = hwnd;
1909 success = TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001910
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +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);
1919 /* 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';
1925
1926 CheckRootKey(hwnd);
1927
1928 if (!OpenLogfile(python_dir))
1929 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001930
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*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001950 scheme = GetScheme(py_major, py_minor);
1951 /* 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 }
1960 /* Extract all files from the archive */
1961 SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
1962 if (!unzip_archive (scheme,
1963 python_dir, arc_data,
1964 arc_size, notify))
1965 set_failure_reason("Failed to unzip installation files");
1966 /* Compile the py-files */
1967 if (success && pyc_compile) {
1968 int errors;
1969 HINSTANCE hPython;
1970 SetDlgItemText(hwnd, IDC_TITLE,
1971 "Compiling files to .pyc...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001972
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001973 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1974 hPython = LoadPythonDll(pythondll);
1975 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 }
1986 if (success && pyo_compile) {
1987 int errors;
1988 HINSTANCE hPython;
1989 SetDlgItemText(hwnd, IDC_TITLE,
1990 "Compiling files to .pyo...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001991
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001992 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1993 hPython = LoadPythonDll(pythondll);
1994 if (hPython) {
1995 errors = compile_filelist(hPython, TRUE);
1996 FreeLibrary(hPython);
1997 }
1998 /* Errors ignored: see above */
1999 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002000
2001
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002002 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002003
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002004 case PSN_RESET:
2005 break;
2006
2007 default:
2008 break;
2009 }
2010 }
2011 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002012}
2013
2014
2015BOOL CALLBACK
2016FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2017{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002018 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002019
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002020 switch (msg) {
2021 case WM_INITDIALOG:
2022 if (hBitmap)
2023 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2024 IMAGE_BITMAP, (LPARAM)hBitmap);
2025 if (!success)
2026 SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002027
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002028 /* 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002032
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002033 case WM_USER:
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002034
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002035 if (success && install_script && install_script[0]) {
2036 char fname[MAX_PATH];
2037 char *buffer;
2038 HCURSOR hCursor;
2039 int result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002040
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002041 char *argv[3] = {NULL, "-install", NULL};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002042
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002043 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002048
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002049 if (logfile)
2050 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002051
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002052 hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002053
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002054 argv[0] = fname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002055
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002056 result = run_installscript(fname, 2, argv, &buffer);
2057 if (0 != result) {
2058 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
2059 }
2060 if (buffer)
2061 SetDlgItemText(hwnd, IDC_INFO, buffer);
2062 SetDlgItemText(hwnd, IDC_TITLE,
2063 "Postinstall script finished.\n"
2064 "Click the Finish button to exit the Setup wizard.");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002065
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002066 free(buffer);
2067 SetCursor(hCursor);
2068 CloseLogfile();
2069 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002070
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002071 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002072
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002073 case WM_NOTIFY:
2074 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002075
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002076 switch (lpnm->code) {
2077 case PSN_SETACTIVE: /* Enable the Finish button */
2078 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2079 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002080
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002081 case PSN_WIZNEXT:
2082 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002083
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002084 case PSN_WIZFINISH:
2085 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002086
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002087 case PSN_RESET:
2088 break;
2089
2090 default:
2091 break;
2092 }
2093 }
2094 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002095}
2096
2097void RunWizard(HWND hwnd)
2098{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002099 PROPSHEETPAGE psp = {0};
2100 HPROPSHEETPAGE ahpsp[4] = {0};
2101 PROPSHEETHEADER psh = {0};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002102
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002103 /* 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002110
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002111 ahpsp[0] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002112
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002113 /* Select python version to use */
2114 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2115 psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON);
2116 psp.pfnDlgProc = SelectPythonDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002117
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002118 ahpsp[1] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002119
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002120 /* Install the files */
2121 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2122 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES);
2123 psp.pfnDlgProc = InstallFilesDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002124
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002125 ahpsp[2] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002126
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002127 /* Show success or failure */
2128 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2129 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED);
2130 psp.pfnDlgProc = FinishedDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002131
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002132 ahpsp[3] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002133
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002134 /* 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002144
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002145 PropertySheet(&psh);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002146}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002153 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)();
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002163}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002174 HKEY hk;
2175 char key_name[80];
2176 // 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;
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002189}
2190
Georg Brandl26adf522008-07-16 02:02:25 +00002191// Returns TRUE if the platform supports UAC.
2192BOOL PlatformSupportsUAC()
2193{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002194 // 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 =
Georg Brandl26adf522008-07-16 02:02:25 +00002203 ( (winverinfo.dwMajorVersion > 5) ||
2204 ( (winverinfo.dwMajorVersion == 5) && (winverinfo.dwMinorVersion >= 1) ));
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002205 return bIsWindowsXPorLater;
Georg Brandl26adf522008-07-16 02:02:25 +00002206}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002213 // 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 }
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002223}
2224
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002225int DoInstall(void)
2226{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002227 char ini_buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002228
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002229 /* Read installation information */
2230 GetPrivateProfileString("Setup", "title", "", ini_buffer,
2231 sizeof(ini_buffer), ini_file);
2232 unescape(title, ini_buffer, sizeof(title));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002233
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002234 GetPrivateProfileString("Setup", "info", "", ini_buffer,
2235 sizeof(ini_buffer), ini_file);
2236 unescape(info, ini_buffer, sizeof(info));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002237
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002238 GetPrivateProfileString("Setup", "build_info", "", build_info,
2239 sizeof(build_info), ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002240
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002241 pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2242 ini_file);
2243 pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2244 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002245
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002246 GetPrivateProfileString("Setup", "target_version", "",
2247 target_version, sizeof(target_version),
2248 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002249
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002250 GetPrivateProfileString("metadata", "name", "",
2251 meta_name, sizeof(meta_name),
2252 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002253
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002254 GetPrivateProfileString("Setup", "install_script", "",
2255 install_script, sizeof(install_script),
2256 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002257
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002258 GetPrivateProfileString("Setup", "user_access_control", "",
2259 user_access_control, sizeof(user_access_control), ini_file);
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002260
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002261 // See if we need to do the Vista UAC magic.
2262 if (strcmp(user_access_control, "force")==0) {
2263 if (PlatformSupportsUAC() && !MyIsUserAnAdmin()) {
2264 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.
2271 if (PlatformSupportsUAC() && !MyIsUserAnAdmin() && NeedAutoUAC()) {
2272 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
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002284 hwndMain = CreateBackground(title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002285
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002286 RunWizard(hwndMain);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002287
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002288 /* Clean up */
2289 UnmapViewOfFile(arc_data);
2290 if (ini_file)
2291 DeleteFile(ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002292
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002293 if (hBitmap)
2294 DeleteObject(hBitmap);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002295
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002296 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002297}
2298
2299/*********************** uninstall section ******************************/
2300
2301static int compare(const void *p1, const void *p2)
2302{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002303 return strcmp(*(char **)p2, *(char **)p1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002304}
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{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002317 char exename[_MAX_PATH];
2318 char batname[_MAX_PATH];
2319 FILE *fp;
2320 STARTUPINFO si;
2321 PROCESS_INFORMATION pi;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002322
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002323 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002353}
2354
2355void DeleteRegistryKey(char *string)
2356{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002357 char *keyname;
2358 char *subkeyname;
2359 char *delim;
2360 HKEY hKey;
2361 long result;
2362 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002363
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002364 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002365
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002366 keyname = strchr(line, '[');
2367 if (!keyname)
2368 return;
2369 ++keyname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002370
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002371 subkeyname = strchr(keyname, ']');
2372 if (!subkeyname)
2373 return;
2374 *subkeyname++='\0';
2375 delim = strchr(subkeyname, '\n');
2376 if (delim)
2377 *delim = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002378
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002379 result = RegOpenKeyEx(hkey_root,
2380 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);
2389 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2390 MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2391 RegCloseKey(hKey);
2392 }
2393 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002394}
2395
2396void DeleteRegistryValue(char *string)
2397{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002398 char *keyname;
2399 char *valuename;
2400 char *value;
2401 HKEY hKey;
2402 long result;
2403 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002404
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002405 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002406
2407/* Format is 'Reg DB Value: [key]name=value' */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002408 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002419
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002420 *value++ = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002421
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002422 result = RegOpenKeyEx(hkey_root,
2423 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);
2431 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2432 MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2433 RegCloseKey(hKey);
2434 }
2435 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002436}
2437
2438BOOL MyDeleteFile(char *line)
2439{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002440 char *pathname = strchr(line, ':');
2441 if (!pathname)
2442 return FALSE;
2443 ++pathname;
2444 while (isspace(*pathname))
2445 ++pathname;
2446 return DeleteFile(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002447}
2448
2449BOOL MyRemoveDirectory(char *line)
2450{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002451 char *pathname = strchr(line, ':');
2452 if (!pathname)
2453 return FALSE;
2454 ++pathname;
2455 while (isspace(*pathname))
2456 ++pathname;
2457 return RemoveDirectory(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002458}
2459
2460BOOL Run_RemoveScript(char *line)
2461{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002462 char *dllname;
2463 char *scriptname;
2464 static char lastscript[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002465
2466/* Format is 'Run Scripts: [pythondll]scriptname' */
2467/* XXX Currently, pythondll carries no path!!! */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002468 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)) {
2479 char *argv[3] = {NULL, "-remove", NULL};
2480 char *buffer = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002481
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002482 argv[0] = scriptname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002483
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +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
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002487 if (buffer && buffer[0])
2488 MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
2489 free(buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002490
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002491 strcpy(lastscript, scriptname);
2492 }
2493 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002494}
2495
2496int DoUninstall(int argc, char **argv)
2497{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002498 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002508
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002509 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002523
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002532
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002533 lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2534 if (!lines)
2535 return SystemError(0, "Out of memory");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002536
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002537 /* 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);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002554
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002555 /* Sort all the lines, so that highest 3-digit codes are first */
2556 qsort(&lines[0], nLines, sizeof(char *),
2557 compare);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002558
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002559 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
2566 hkey_root = HKEY_LOCAL_MACHINE;
2567 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 */
2574 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)) {
2589 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002643}
2644
2645int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002646 LPSTR lpszCmdLine, INT nCmdShow)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002647{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002648 extern int __argc;
2649 extern char **__argv;
2650 char *basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002651
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002652 GetModuleFileName(NULL, modulename, sizeof(modulename));
2653 GetModuleFileNameW(NULL, wmodulename, sizeof(wmodulename)/sizeof(wmodulename[0]));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002654
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002655 /* 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 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002661
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002662 /* 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 */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002672
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002673 /* Try to extract the configuration data into a temporary file */
2674 if (ExtractInstallData(arc_data, arc_size, &exe_size,
2675 &ini_file, &pre_install_script))
2676 return DoInstall();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002677
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002678 if (!ini_file && __argc > 1) {
2679 return DoUninstall(__argc, __argv);
2680 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002681
2682
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002683 basename = strrchr(modulename, '\\');
2684 if (basename)
2685 ++basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002686
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002687 /* 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;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002693}