blob: a0232d23c94e5b1d28ed1e70ab0a3f5a50463fdd [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
Victor Stinnerdaf45552013-08-28 00:53:59 +0200746 fh = open(pathname, _O_RDONLY | O_NOINHERIT);
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000747 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
Gregory P. Smithb803c6c2013-03-23 16:05:36 -07001022 if (hFileMapping == NULL)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001023 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");
Benjamin Peterson25c7d3f2014-11-27 20:39:02 -06001777 if (!logfile) {
1778 char error[1024];
1779
1780 sprintf(error, "Can't create \"%s\" (%s).\n\n"
1781 "Try to execute the installer as administrator.",
1782 buffer, strerror(errno));
1783 MessageBox(GetFocus(), error, NULL, MB_OK | MB_ICONSTOP);
1784 return FALSE;
1785 }
1786
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001787 time(&ltime);
1788 now = localtime(&ltime);
1789 strftime(buffer, sizeof(buffer),
1790 "*** Installation started %Y/%m/%d %H:%M ***\n",
1791 localtime(&ltime));
1792 fprintf(logfile, buffer);
1793 fprintf(logfile, "Source: %s\n", modulename);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001794
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001795 /* Root key must be first entry processed by uninstaller. */
1796 fprintf(logfile, "999 Root Key: %s\n", root_name);
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001797
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001798 sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001799
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001800 result = RegCreateKeyEx(hKey, subkey_name,
1801 0, NULL, 0,
1802 KEY_WRITE,
1803 NULL,
1804 &hSubkey,
1805 &disposition);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001806
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001807 if (result != ERROR_SUCCESS)
1808 MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001809
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001810 RegCloseKey(hKey);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001811
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001812 if (disposition == REG_CREATED_NEW_KEY)
1813 fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001814
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001815 sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001816
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001817 result = RegSetValueEx(hSubkey, "DisplayName",
1818 0,
1819 REG_SZ,
1820 buffer,
1821 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001822
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001823 if (result != ERROR_SUCCESS)
1824 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001825
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001826 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1827 KeyName, subkey_name, "DisplayName", buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001828
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001829 {
1830 FILE *fp;
1831 sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
1832 fp = fopen(buffer, "wb");
1833 fwrite(arc_data, exe_size, 1, fp);
1834 fclose(fp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001835
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001836 sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1837 dir, meta_name, dir, meta_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001838
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001839 result = RegSetValueEx(hSubkey, "UninstallString",
1840 0,
1841 REG_SZ,
1842 buffer,
1843 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001844
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001845 if (result != ERROR_SUCCESS)
1846 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1847
1848 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1849 KeyName, subkey_name, "UninstallString", buffer);
1850 }
1851 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001852}
1853
1854static void CloseLogfile(void)
1855{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001856 char buffer[_MAX_PATH+1];
1857 time_t ltime;
1858 struct tm *now;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001859
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001860 time(&ltime);
1861 now = localtime(&ltime);
1862 strftime(buffer, sizeof(buffer),
1863 "*** Installation finished %Y/%m/%d %H:%M ***\n",
1864 localtime(&ltime));
1865 fprintf(logfile, buffer);
1866 if (logfile)
1867 fclose(logfile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001868}
1869
1870BOOL CALLBACK
1871InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1872{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001873 LPNMHDR lpnm;
1874 char Buffer[4096];
1875 SCHEME *scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001876
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001877 switch (msg) {
1878 case WM_INITDIALOG:
1879 if (hBitmap)
1880 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1881 IMAGE_BITMAP, (LPARAM)hBitmap);
1882 wsprintf(Buffer,
1883 "Click Next to begin the installation of %s. "
1884 "If you want to review or change any of your "
1885 " installation settings, click Back. "
1886 "Click Cancel to exit the wizard.",
1887 meta_name);
1888 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1889 SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
1890 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001891
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001892 case WM_NUMFILES:
1893 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1894 PumpMessages();
1895 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001896
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001897 case WM_NEXTFILE:
1898 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
1899 0);
1900 SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
1901 PumpMessages();
1902 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001903
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001904 case WM_NOTIFY:
1905 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001906
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001907 switch (lpnm->code) {
1908 case PSN_SETACTIVE:
1909 PropSheet_SetWizButtons(GetParent(hwnd),
1910 PSWIZB_BACK | PSWIZB_NEXT);
1911 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001912
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001913 case PSN_WIZFINISH:
1914 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001915
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001916 case PSN_WIZNEXT:
1917 /* Handle a Next button click here */
1918 hDialog = hwnd;
1919 success = TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001920
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001921 /* Disable the buttons while we work. Sending CANCELTOCLOSE has
1922 the effect of disabling the cancel button, which is a) as we
1923 do everything synchronously we can't cancel, and b) the next
1924 step is 'finished', when it is too late to cancel anyway.
1925 The next step being 'Finished' means we also don't need to
1926 restore the button state back */
1927 PropSheet_SetWizButtons(GetParent(hwnd), 0);
1928 SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
1929 /* Make sure the installation directory name ends in a */
1930 /* backslash */
1931 if (python_dir[strlen(python_dir)-1] != '\\')
1932 strcat(python_dir, "\\");
1933 /* Strip the trailing backslash again */
1934 python_dir[strlen(python_dir)-1] = '\0';
1935
1936 CheckRootKey(hwnd);
1937
1938 if (!OpenLogfile(python_dir))
1939 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001940
1941/*
1942 * The scheme we have to use depends on the Python version...
1943 if sys.version < "2.2":
1944 WINDOWS_SCHEME = {
1945 'purelib': '$base',
1946 'platlib': '$base',
1947 'headers': '$base/Include/$dist_name',
1948 'scripts': '$base/Scripts',
1949 'data' : '$base',
1950 }
1951 else:
1952 WINDOWS_SCHEME = {
1953 'purelib': '$base/Lib/site-packages',
1954 'platlib': '$base/Lib/site-packages',
1955 'headers': '$base/Include/$dist_name',
1956 'scripts': '$base/Scripts',
1957 'data' : '$base',
1958 }
1959*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001960 scheme = GetScheme(py_major, py_minor);
1961 /* Run the pre-install script. */
1962 if (pre_install_script && *pre_install_script) {
1963 SetDlgItemText (hwnd, IDC_TITLE,
1964 "Running pre-installation script");
1965 run_simple_script(pre_install_script);
1966 }
1967 if (!success) {
1968 break;
1969 }
1970 /* Extract all files from the archive */
1971 SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
1972 if (!unzip_archive (scheme,
1973 python_dir, arc_data,
1974 arc_size, notify))
1975 set_failure_reason("Failed to unzip installation files");
1976 /* Compile the py-files */
1977 if (success && pyc_compile) {
1978 int errors;
1979 HINSTANCE hPython;
1980 SetDlgItemText(hwnd, IDC_TITLE,
1981 "Compiling files to .pyc...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001982
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001983 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1984 hPython = LoadPythonDll(pythondll);
1985 if (hPython) {
1986 errors = compile_filelist(hPython, FALSE);
1987 FreeLibrary(hPython);
1988 }
1989 /* Compilation errors are intentionally ignored:
1990 * Python2.0 contains a bug which will result
1991 * in sys.path containing garbage under certain
1992 * circumstances, and an error message will only
1993 * confuse the user.
1994 */
1995 }
1996 if (success && pyo_compile) {
1997 int errors;
1998 HINSTANCE hPython;
1999 SetDlgItemText(hwnd, IDC_TITLE,
2000 "Compiling files to .pyo...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002001
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002002 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
2003 hPython = LoadPythonDll(pythondll);
2004 if (hPython) {
2005 errors = compile_filelist(hPython, TRUE);
2006 FreeLibrary(hPython);
2007 }
2008 /* Errors ignored: see above */
2009 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002010
2011
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002012 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002013
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002014 case PSN_RESET:
2015 break;
2016
2017 default:
2018 break;
2019 }
2020 }
2021 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002022}
2023
2024
2025BOOL CALLBACK
2026FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2027{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002028 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002029
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002030 switch (msg) {
2031 case WM_INITDIALOG:
2032 if (hBitmap)
2033 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2034 IMAGE_BITMAP, (LPARAM)hBitmap);
2035 if (!success)
2036 SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002037
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002038 /* async delay: will show the dialog box completely before
2039 the install_script is started */
2040 PostMessage(hwnd, WM_USER, 0, 0L);
2041 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002042
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002043 case WM_USER:
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002044
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002045 if (success && install_script && install_script[0]) {
2046 char fname[MAX_PATH];
2047 char *buffer;
2048 HCURSOR hCursor;
2049 int result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002050
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002051 char *argv[3] = {NULL, "-install", NULL};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002052
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002053 SetDlgItemText(hwnd, IDC_TITLE,
2054 "Please wait while running postinstall script...");
2055 strcpy(fname, python_dir);
2056 strcat(fname, "\\Scripts\\");
2057 strcat(fname, install_script);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002058
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002059 if (logfile)
2060 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002061
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002062 hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002063
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002064 argv[0] = fname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002065
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002066 result = run_installscript(fname, 2, argv, &buffer);
2067 if (0 != result) {
2068 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
2069 }
2070 if (buffer)
2071 SetDlgItemText(hwnd, IDC_INFO, buffer);
2072 SetDlgItemText(hwnd, IDC_TITLE,
2073 "Postinstall script finished.\n"
2074 "Click the Finish button to exit the Setup wizard.");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002075
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002076 free(buffer);
2077 SetCursor(hCursor);
2078 CloseLogfile();
2079 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002080
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002081 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002082
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002083 case WM_NOTIFY:
2084 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002085
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002086 switch (lpnm->code) {
2087 case PSN_SETACTIVE: /* Enable the Finish button */
2088 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2089 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002090
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002091 case PSN_WIZNEXT:
2092 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002093
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002094 case PSN_WIZFINISH:
2095 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002096
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002097 case PSN_RESET:
2098 break;
2099
2100 default:
2101 break;
2102 }
2103 }
2104 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002105}
2106
2107void RunWizard(HWND hwnd)
2108{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002109 PROPSHEETPAGE psp = {0};
2110 HPROPSHEETPAGE ahpsp[4] = {0};
2111 PROPSHEETHEADER psh = {0};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002112
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002113 /* Display module information */
2114 psp.dwSize = sizeof(psp);
2115 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2116 psp.hInstance = GetModuleHandle (NULL);
2117 psp.lParam = 0;
2118 psp.pfnDlgProc = IntroDlgProc;
2119 psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002120
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002121 ahpsp[0] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002122
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002123 /* Select python version to use */
2124 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2125 psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON);
2126 psp.pfnDlgProc = SelectPythonDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002127
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002128 ahpsp[1] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002129
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002130 /* Install the files */
2131 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2132 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES);
2133 psp.pfnDlgProc = InstallFilesDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002134
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002135 ahpsp[2] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002136
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002137 /* Show success or failure */
2138 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2139 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED);
2140 psp.pfnDlgProc = FinishedDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002141
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002142 ahpsp[3] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002143
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002144 /* Create the property sheet */
2145 psh.dwSize = sizeof(psh);
2146 psh.hInstance = GetModuleHandle(NULL);
2147 psh.hwndParent = hwnd;
2148 psh.phpage = ahpsp;
2149 psh.dwFlags = PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
2150 psh.pszbmWatermark = NULL;
2151 psh.pszbmHeader = NULL;
2152 psh.nStartPage = 0;
2153 psh.nPages = 4;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002154
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002155 PropertySheet(&psh);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002156}
2157
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002158// subtly different from HasLocalMachinePrivs(), in that after executing
2159// an 'elevated' process, we expect this to return TRUE - but there is no
2160// such implication for HasLocalMachinePrivs
2161BOOL MyIsUserAnAdmin()
2162{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002163 typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
2164 static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
2165 HMODULE shell32;
2166 // This function isn't guaranteed to be available (and it can't hurt
2167 // to leave the library loaded)
2168 if (0 == (shell32=LoadLibrary("shell32.dll")))
2169 return FALSE;
2170 if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
2171 return FALSE;
2172 return (*pfnIsUserAnAdmin)();
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002173}
2174
2175// Some magic for Vista's UAC. If there is a target_version, and
2176// if that target version is installed in the registry under
2177// HKLM, and we are not current administrator, then
2178// re-execute ourselves requesting elevation.
2179// Split into 2 functions - "should we elevate" and "spawn elevated"
2180
2181// Returns TRUE if we should spawn an elevated child
2182BOOL NeedAutoUAC()
2183{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002184 HKEY hk;
2185 char key_name[80];
2186 // no Python version info == we can't know yet.
2187 if (target_version[0] == '\0')
2188 return FALSE;
2189 // see how python is current installed
2190 wsprintf(key_name,
2191 "Software\\Python\\PythonCore\\%s\\InstallPath",
2192 target_version);
2193 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2194 key_name, 0, KEY_READ, &hk))
2195 return FALSE;
2196 RegCloseKey(hk);
2197 // Python is installed in HKLM - we must elevate.
2198 return TRUE;
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002199}
2200
Georg Brandl26adf522008-07-16 02:02:25 +00002201// Returns TRUE if the platform supports UAC.
2202BOOL PlatformSupportsUAC()
2203{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002204 // Note that win2k does seem to support ShellExecute with 'runas',
2205 // but does *not* support IsUserAnAdmin - so we just pretend things
2206 // only work on XP and later.
2207 BOOL bIsWindowsXPorLater;
2208 OSVERSIONINFO winverinfo;
2209 winverinfo.dwOSVersionInfoSize = sizeof(winverinfo);
2210 if (!GetVersionEx(&winverinfo))
2211 return FALSE; // something bad has gone wrong
2212 bIsWindowsXPorLater =
Georg Brandl26adf522008-07-16 02:02:25 +00002213 ( (winverinfo.dwMajorVersion > 5) ||
2214 ( (winverinfo.dwMajorVersion == 5) && (winverinfo.dwMinorVersion >= 1) ));
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002215 return bIsWindowsXPorLater;
Georg Brandl26adf522008-07-16 02:02:25 +00002216}
2217
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002218// Spawn ourself as an elevated application. On failure, a message is
2219// displayed to the user - but this app will always terminate, even
2220// on error.
2221void SpawnUAC()
2222{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002223 // interesting failure scenario that has been seen: initial executable
2224 // runs from a network drive - but once elevated, that network share
2225 // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2226 int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
2227 SW_SHOWNORMAL);
2228 if (ret <= 32) {
2229 char msg[128];
2230 wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
2231 MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
2232 }
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002233}
2234
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002235int DoInstall(void)
2236{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002237 char ini_buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002238
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002239 /* Read installation information */
2240 GetPrivateProfileString("Setup", "title", "", ini_buffer,
2241 sizeof(ini_buffer), ini_file);
2242 unescape(title, ini_buffer, sizeof(title));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002243
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002244 GetPrivateProfileString("Setup", "info", "", ini_buffer,
2245 sizeof(ini_buffer), ini_file);
2246 unescape(info, ini_buffer, sizeof(info));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002247
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002248 GetPrivateProfileString("Setup", "build_info", "", build_info,
2249 sizeof(build_info), ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002250
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002251 pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2252 ini_file);
2253 pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2254 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002255
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002256 GetPrivateProfileString("Setup", "target_version", "",
2257 target_version, sizeof(target_version),
2258 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002259
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002260 GetPrivateProfileString("metadata", "name", "",
2261 meta_name, sizeof(meta_name),
2262 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002263
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002264 GetPrivateProfileString("Setup", "install_script", "",
2265 install_script, sizeof(install_script),
2266 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002267
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002268 GetPrivateProfileString("Setup", "user_access_control", "",
2269 user_access_control, sizeof(user_access_control), ini_file);
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002270
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002271 // See if we need to do the Vista UAC magic.
2272 if (strcmp(user_access_control, "force")==0) {
2273 if (PlatformSupportsUAC() && !MyIsUserAnAdmin()) {
2274 SpawnUAC();
2275 return 0;
2276 }
2277 // already admin - keep going
2278 } else if (strcmp(user_access_control, "auto")==0) {
2279 // Check if it looks like we need UAC control, based
2280 // on how Python itself was installed.
2281 if (PlatformSupportsUAC() && !MyIsUserAnAdmin() && NeedAutoUAC()) {
2282 SpawnUAC();
2283 return 0;
2284 }
2285 } else {
2286 // display a warning about unknown values - only the developer
2287 // of the extension will see it (until they fix it!)
2288 if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
2289 MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
2290 // nothing to do.
2291 }
2292 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002293
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002294 hwndMain = CreateBackground(title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002295
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002296 RunWizard(hwndMain);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002297
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002298 /* Clean up */
2299 UnmapViewOfFile(arc_data);
2300 if (ini_file)
2301 DeleteFile(ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002302
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002303 if (hBitmap)
2304 DeleteObject(hBitmap);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002305
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002306 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002307}
2308
2309/*********************** uninstall section ******************************/
2310
2311static int compare(const void *p1, const void *p2)
2312{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002313 return strcmp(*(char **)p2, *(char **)p1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002314}
2315
2316/*
2317 * Commit suicide (remove the uninstaller itself).
2318 *
2319 * Create a batch file to first remove the uninstaller
2320 * (will succeed after it has finished), then the batch file itself.
2321 *
2322 * This technique has been demonstrated by Jeff Richter,
2323 * MSJ 1/1996
2324 */
2325void remove_exe(void)
2326{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002327 char exename[_MAX_PATH];
2328 char batname[_MAX_PATH];
2329 FILE *fp;
2330 STARTUPINFO si;
2331 PROCESS_INFORMATION pi;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002332
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002333 GetModuleFileName(NULL, exename, sizeof(exename));
2334 sprintf(batname, "%s.bat", exename);
2335 fp = fopen(batname, "w");
2336 fprintf(fp, ":Repeat\n");
2337 fprintf(fp, "del \"%s\"\n", exename);
2338 fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
2339 fprintf(fp, "del \"%s\"\n", batname);
2340 fclose(fp);
2341
2342 ZeroMemory(&si, sizeof(si));
2343 si.cb = sizeof(si);
2344 si.dwFlags = STARTF_USESHOWWINDOW;
2345 si.wShowWindow = SW_HIDE;
2346 if (CreateProcess(NULL,
2347 batname,
2348 NULL,
2349 NULL,
2350 FALSE,
2351 CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
2352 NULL,
2353 "\\",
2354 &si,
2355 &pi)) {
2356 SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
2357 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2358 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
2359 CloseHandle(pi.hProcess);
2360 ResumeThread(pi.hThread);
2361 CloseHandle(pi.hThread);
2362 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002363}
2364
2365void DeleteRegistryKey(char *string)
2366{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002367 char *keyname;
2368 char *subkeyname;
2369 char *delim;
2370 HKEY hKey;
2371 long result;
2372 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002373
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002374 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002375
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002376 keyname = strchr(line, '[');
2377 if (!keyname)
2378 return;
2379 ++keyname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002380
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002381 subkeyname = strchr(keyname, ']');
2382 if (!subkeyname)
2383 return;
2384 *subkeyname++='\0';
2385 delim = strchr(subkeyname, '\n');
2386 if (delim)
2387 *delim = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002388
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002389 result = RegOpenKeyEx(hkey_root,
2390 keyname,
2391 0,
2392 KEY_WRITE,
2393 &hKey);
2394
2395 if (result != ERROR_SUCCESS)
2396 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2397 else {
2398 result = RegDeleteKey(hKey, subkeyname);
2399 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2400 MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2401 RegCloseKey(hKey);
2402 }
2403 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002404}
2405
2406void DeleteRegistryValue(char *string)
2407{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002408 char *keyname;
2409 char *valuename;
2410 char *value;
2411 HKEY hKey;
2412 long result;
2413 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002414
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002415 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002416
2417/* Format is 'Reg DB Value: [key]name=value' */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002418 keyname = strchr(line, '[');
2419 if (!keyname)
2420 return;
2421 ++keyname;
2422 valuename = strchr(keyname, ']');
2423 if (!valuename)
2424 return;
2425 *valuename++ = '\0';
2426 value = strchr(valuename, '=');
2427 if (!value)
2428 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002429
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002430 *value++ = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002431
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002432 result = RegOpenKeyEx(hkey_root,
2433 keyname,
2434 0,
2435 KEY_WRITE,
2436 &hKey);
2437 if (result != ERROR_SUCCESS)
2438 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2439 else {
2440 result = RegDeleteValue(hKey, valuename);
2441 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2442 MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2443 RegCloseKey(hKey);
2444 }
2445 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002446}
2447
2448BOOL MyDeleteFile(char *line)
2449{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002450 char *pathname = strchr(line, ':');
2451 if (!pathname)
2452 return FALSE;
2453 ++pathname;
2454 while (isspace(*pathname))
2455 ++pathname;
2456 return DeleteFile(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002457}
2458
2459BOOL MyRemoveDirectory(char *line)
2460{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002461 char *pathname = strchr(line, ':');
2462 if (!pathname)
2463 return FALSE;
2464 ++pathname;
2465 while (isspace(*pathname))
2466 ++pathname;
2467 return RemoveDirectory(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002468}
2469
2470BOOL Run_RemoveScript(char *line)
2471{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002472 char *dllname;
2473 char *scriptname;
2474 static char lastscript[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002475
2476/* Format is 'Run Scripts: [pythondll]scriptname' */
2477/* XXX Currently, pythondll carries no path!!! */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002478 dllname = strchr(line, '[');
2479 if (!dllname)
2480 return FALSE;
2481 ++dllname;
2482 scriptname = strchr(dllname, ']');
2483 if (!scriptname)
2484 return FALSE;
2485 *scriptname++ = '\0';
2486 /* this function may be called more than one time with the same
2487 script, only run it one time */
2488 if (strcmp(lastscript, scriptname)) {
2489 char *argv[3] = {NULL, "-remove", NULL};
2490 char *buffer = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002491
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002492 argv[0] = scriptname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002493
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002494 if (0 != run_installscript(scriptname, 2, argv, &buffer))
2495 fprintf(stderr, "*** Could not run installation script ***");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002496
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002497 if (buffer && buffer[0])
2498 MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
2499 free(buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002500
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002501 strcpy(lastscript, scriptname);
2502 }
2503 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002504}
2505
2506int DoUninstall(int argc, char **argv)
2507{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002508 FILE *logfile;
2509 char buffer[4096];
2510 int nLines = 0;
2511 int i;
2512 char *cp;
2513 int nFiles = 0;
2514 int nDirs = 0;
2515 int nErrors = 0;
2516 char **lines;
2517 int lines_buffer_size = 10;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002518
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002519 if (argc != 3) {
2520 MessageBox(NULL,
2521 "Wrong number of args",
2522 NULL,
2523 MB_OK);
2524 return 1; /* Error */
2525 }
2526 if (strcmp(argv[1], "-u")) {
2527 MessageBox(NULL,
2528 "2. arg is not -u",
2529 NULL,
2530 MB_OK);
2531 return 1; /* Error */
2532 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002533
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002534 logfile = fopen(argv[2], "r");
2535 if (!logfile) {
2536 MessageBox(NULL,
2537 "could not open logfile",
2538 NULL,
2539 MB_OK);
2540 return 1; /* Error */
2541 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002542
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002543 lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2544 if (!lines)
2545 return SystemError(0, "Out of memory");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002546
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002547 /* Read the whole logfile, realloacting the buffer */
2548 while (fgets(buffer, sizeof(buffer), logfile)) {
2549 int len = strlen(buffer);
2550 /* remove trailing white space */
2551 while (isspace(buffer[len-1]))
2552 len -= 1;
2553 buffer[len] = '\0';
2554 lines[nLines++] = strdup(buffer);
2555 if (nLines >= lines_buffer_size) {
2556 lines_buffer_size += 10;
2557 lines = (char **)realloc(lines,
2558 sizeof(char *) * lines_buffer_size);
2559 if (!lines)
2560 return SystemError(0, "Out of memory");
2561 }
2562 }
2563 fclose(logfile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002564
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002565 /* Sort all the lines, so that highest 3-digit codes are first */
2566 qsort(&lines[0], nLines, sizeof(char *),
2567 compare);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002568
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002569 if (IDYES != MessageBox(NULL,
2570 "Are you sure you want to remove\n"
2571 "this package from your computer?",
2572 "Please confirm",
2573 MB_YESNO | MB_ICONQUESTION))
2574 return 0;
2575
2576 hkey_root = HKEY_LOCAL_MACHINE;
2577 cp = "";
2578 for (i = 0; i < nLines; ++i) {
2579 /* Ignore duplicate lines */
2580 if (strcmp(cp, lines[i])) {
2581 int ign;
2582 cp = lines[i];
2583 /* Parse the lines */
2584 if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
2585 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
2586 hkey_root = HKEY_CURRENT_USER;
2587 else {
2588 // HKLM - check they have permissions.
2589 if (!HasLocalMachinePrivs()) {
2590 MessageBox(GetFocus(),
2591 "You do not seem to have sufficient access rights\n"
2592 "on this machine to uninstall this software",
2593 NULL,
2594 MB_OK | MB_ICONSTOP);
2595 return 1; /* Error */
2596 }
2597 }
2598 } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
2599 if (MyRemoveDirectory(cp))
2600 ++nDirs;
2601 else {
2602 int code = GetLastError();
2603 if (code != 2 && code != 3) { /* file or path not found */
2604 ++nErrors;
2605 }
2606 }
2607 } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
2608 if (MyDeleteFile(cp))
2609 ++nFiles;
2610 else {
2611 int code = GetLastError();
2612 if (code != 2 && code != 3) { /* file or path not found */
2613 ++nErrors;
2614 }
2615 }
2616 } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
2617 if (MyDeleteFile(cp))
2618 ++nFiles;
2619 else {
2620 int code = GetLastError();
2621 if (code != 2 && code != 3) { /* file or path not found */
2622 ++nErrors;
2623 }
2624 }
2625 } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
2626 DeleteRegistryKey(cp);
2627 } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
2628 DeleteRegistryValue(cp);
2629 } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
2630 Run_RemoveScript(cp);
2631 }
2632 }
2633 }
2634
2635 if (DeleteFile(argv[2])) {
2636 ++nFiles;
2637 } else {
2638 ++nErrors;
2639 SystemError(GetLastError(), argv[2]);
2640 }
2641 if (nErrors)
2642 wsprintf(buffer,
2643 "%d files and %d directories removed\n"
2644 "%d files or directories could not be removed",
2645 nFiles, nDirs, nErrors);
2646 else
2647 wsprintf(buffer, "%d files and %d directories removed",
2648 nFiles, nDirs);
2649 MessageBox(NULL, buffer, "Uninstall Finished!",
2650 MB_OK | MB_ICONINFORMATION);
2651 remove_exe();
2652 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002653}
2654
2655int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002656 LPSTR lpszCmdLine, INT nCmdShow)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002657{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002658 extern int __argc;
2659 extern char **__argv;
2660 char *basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002661
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002662 GetModuleFileName(NULL, modulename, sizeof(modulename));
2663 GetModuleFileNameW(NULL, wmodulename, sizeof(wmodulename)/sizeof(wmodulename[0]));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002664
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002665 /* Map the executable file to memory */
2666 arc_data = MapExistingFile(modulename, &arc_size);
2667 if (!arc_data) {
2668 SystemError(GetLastError(), "Could not open archive");
2669 return 1;
2670 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002671
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002672 /* OK. So this program can act as installer (self-extracting
2673 * zip-file, or as uninstaller when started with '-u logfile'
2674 * command line flags.
2675 *
2676 * The installer is usually started without command line flags,
2677 * and the uninstaller is usually started with the '-u logfile'
2678 * flag. What to do if some innocent user double-clicks the
2679 * exe-file?
2680 * The following implements a defensive strategy...
2681 */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002682
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002683 /* Try to extract the configuration data into a temporary file */
2684 if (ExtractInstallData(arc_data, arc_size, &exe_size,
2685 &ini_file, &pre_install_script))
2686 return DoInstall();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002687
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002688 if (!ini_file && __argc > 1) {
2689 return DoUninstall(__argc, __argv);
2690 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002691
2692
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002693 basename = strrchr(modulename, '\\');
2694 if (basename)
2695 ++basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002696
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002697 /* Last guess about the purpose of this program */
2698 if (basename && (0 == strncmp(basename, "Remove", 6)))
2699 SystemError(0, "This program is normally started by windows");
2700 else
2701 SystemError(0, "Setup program invalid or damaged");
2702 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002703}