blob: cb037ff2c603b6714017d01be27e513f52e30804 [file] [log] [blame]
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001/*
Steve Dower65e4cb12014-11-22 12:54:57 -08002 IMPORTANT NOTE: IF THIS FILE IS CHANGED, PCBUILD\BDIST_WININST.VCXPROJ MUST
3 BE REBUILT AS WELL.
Thomas Hellerd1d92ea2004-07-14 15:17:04 +00004
Steve Dower65e4cb12014-11-22 12:54:57 -08005 IF CHANGES TO THIS FILE ARE CHECKED IN, THE RECOMPILED BINARIES MUST BE
6 CHECKED IN AS WELL!
Thomas Hellerd1d92ea2004-07-14 15:17:04 +00007*/
8
9/*
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000010 * Written by Thomas Heller, May 2000
11 *
12 * $Id$
13 */
14
15/*
16 * Windows Installer program for distutils.
17 *
18 * (a kind of self-extracting zip-file)
19 *
20 * At runtime, the exefile has appended:
21 * - compressed setup-data in ini-format, containing the following sections:
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000022 * [metadata]
23 * author=Greg Ward
24 * author_email=gward@python.net
25 * description=Python Distribution Utilities
26 * licence=Python
27 * name=Distutils
28 * url=http://www.python.org/sigs/distutils-sig/
29 * version=0.9pre
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000030 *
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +000031 * [Setup]
32 * info= text to be displayed in the edit-box
33 * title= to be displayed by this program
34 * target_version = if present, python version required
35 * pyc_compile = if 0, do not compile py to pyc
36 * pyo_compile = if 0, do not compile py to pyo
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000037 *
38 * - a struct meta_data_hdr, describing the above
39 * - a zip-file, containing the modules to be installed.
40 * for the format see http://www.pkware.com/appnote.html
41 *
42 * What does this program do?
43 * - the setup-data is uncompressed and written to a temporary file.
44 * - setup-data is queried with GetPrivateProfile... calls
45 * - [metadata] - info is displayed in the dialog box
46 * - The registry is searched for installations of python
47 * - The user can select the python version to use.
48 * - The python-installation directory (sys.prefix) is displayed
49 * - When the start-button is pressed, files from the zip-archive
50 * are extracted to the file system. All .py filenames are stored
51 * in a list.
52 */
53/*
54 * Includes now an uninstaller.
55 */
56
57/*
58 * To Do:
59 *
60 * display some explanation when no python version is found
61 * instead showing the user an empty listbox to select something from.
62 *
63 * Finish the code so that we can use other python installations
Ezio Melotti42da6632011-03-15 05:18:48 +020064 * additionally to those found in the registry,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000065 * and then #define USE_OTHER_PYTHON_VERSIONS
66 *
67 * - install a help-button, which will display something meaningful
68 * to the poor user.
69 * text to the user
70 * - should there be a possibility to display a README file
71 * before starting the installation (if one is present in the archive)
72 * - more comments about what the code does(?)
73 *
74 * - evolve this into a full blown installer (???)
75 */
76
77#include <windows.h>
78#include <commctrl.h>
79#include <imagehlp.h>
80#include <objbase.h>
81#include <shlobj.h>
82#include <objidl.h>
83#include "resource.h"
84
85#include <stdio.h>
86#include <stdlib.h>
87#include <stdarg.h>
88#include <string.h>
89#include <time.h>
Thomas Heller9f2e3be2005-02-03 20:35:10 +000090#include <sys/types.h>
91#include <sys/stat.h>
92#include <malloc.h>
93#include <io.h>
94#include <fcntl.h>
Thomas Hellerbb4b7d22002-11-22 20:39:33 +000095
96#include "archive.h"
97
98/* Only for debugging!
99 static int dprintf(char *fmt, ...)
100 {
101 char Buffer[4096];
102 va_list marker;
103 int result;
104
105 va_start(marker, fmt);
106 result = wvsprintf(Buffer, fmt, marker);
107 OutputDebugString(Buffer);
108 return result;
109 }
110*/
111
112/* Bah: global variables */
113FILE *logfile;
114
115char modulename[MAX_PATH];
Mark Hammond891f2632009-01-29 13:08:01 +0000116wchar_t wmodulename[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000117
118HWND hwndMain;
119HWND hDialog;
120
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000121char *ini_file; /* Full pathname of ini-file */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000122/* From ini-file */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000123char info[4096]; /* [Setup] info= */
124char title[80]; /* [Setup] title=, contains package name
125 including version: "Distutils-1.0.1" */
126char target_version[10]; /* [Setup] target_version=, required python
127 version or empty string */
128char build_info[80]; /* [Setup] build_info=, distutils version
129 and build date */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000130
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000131char meta_name[80]; /* package name without version like
132 'Distutils' */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000133char install_script[MAX_PATH];
Thomas Hellera19cdad2004-02-20 14:43:21 +0000134char *pre_install_script; /* run before we install a single file */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000135
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000136char user_access_control[10]; // one of 'auto', 'force', otherwise none.
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000137
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000138int py_major, py_minor; /* Python version selected for installation */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000139
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000140char *arc_data; /* memory mapped archive */
141DWORD arc_size; /* number of bytes in archive */
142int exe_size; /* number of bytes for exe-file portion */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000143char python_dir[MAX_PATH];
144char pythondll[MAX_PATH];
145BOOL pyc_compile, pyo_compile;
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000146/* Either HKLM or HKCU, depending on where Python itself is registered, and
147 the permissions of the current user. */
148HKEY hkey_root = (HKEY)-1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000149
Ezio Melotti13925002011-03-16 11:05:33 +0200150BOOL success; /* Installation successful? */
Thomas Hellera19cdad2004-02-20 14:43:21 +0000151char *failure_reason = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000152
153HANDLE hBitmap;
154char *bitmap_bytes;
155
Steve Dower332334f2016-01-16 13:54:53 -0800156static const char *REGISTRY_SUFFIX_6432 =
157#ifdef MS_WIN64
158 "";
159#else
160 "-32";
161#endif
162
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000163
164#define WM_NUMFILES WM_USER+1
165/* wParam: 0, lParam: total number of files */
166#define WM_NEXTFILE WM_USER+2
167/* wParam: number of this file */
168/* lParam: points to pathname */
169
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000170static BOOL notify(int code, char *fmt, ...);
171
172/* Note: If scheme.prefix is nonempty, it must end with a '\'! */
173/* Note: purelib must be the FIRST entry! */
174SCHEME old_scheme[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000175 { "PURELIB", "" },
176 { "PLATLIB", "" },
177 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
178 { "SCRIPTS", "Scripts\\" },
179 { "DATA", "" },
180 { NULL, NULL },
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000181};
182
183SCHEME new_scheme[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000184 { "PURELIB", "Lib\\site-packages\\" },
185 { "PLATLIB", "Lib\\site-packages\\" },
186 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
187 { "SCRIPTS", "Scripts\\" },
188 { "DATA", "" },
189 { NULL, NULL },
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000190};
191
192static void unescape(char *dst, char *src, unsigned size)
193{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000194 char *eon;
195 char ch;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000196
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000197 while (src && *src && (size > 2)) {
198 if (*src == '\\') {
199 switch (*++src) {
200 case 'n':
201 ++src;
202 *dst++ = '\r';
203 *dst++ = '\n';
204 size -= 2;
205 break;
206 case 'r':
207 ++src;
208 *dst++ = '\r';
209 --size;
210 break;
211 case '0': case '1': case '2': case '3':
212 ch = (char)strtol(src, &eon, 8);
213 if (ch == '\n') {
214 *dst++ = '\r';
215 --size;
216 }
217 *dst++ = ch;
218 --size;
219 src = eon;
220 }
221 } else {
222 *dst++ = *src++;
223 --size;
224 }
225 }
226 *dst = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000227}
228
229static struct tagFile {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000230 char *path;
231 struct tagFile *next;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000232} *file_list = NULL;
233
Thomas Hellera19cdad2004-02-20 14:43:21 +0000234static void set_failure_reason(char *reason)
235{
236 if (failure_reason)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000237 free(failure_reason);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000238 failure_reason = strdup(reason);
239 success = FALSE;
240}
241static char *get_failure_reason()
242{
243 if (!failure_reason)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000244 return "Installation failed.";
Thomas Hellera19cdad2004-02-20 14:43:21 +0000245 return failure_reason;
246}
247
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000248static void add_to_filelist(char *path)
249{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000250 struct tagFile *p;
251 p = (struct tagFile *)malloc(sizeof(struct tagFile));
252 p->path = strdup(path);
253 p->next = file_list;
254 file_list = p;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000255}
256
257static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000258 int optimize)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000259{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000260 struct tagFile *p;
261 int total, n;
262 char Buffer[MAX_PATH + 64];
263 int errors = 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000264
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000265 total = 0;
266 p = file_list;
267 while (p) {
268 ++total;
269 p = p->next;
270 }
271 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0,
272 MAKELPARAM(0, total));
273 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000274
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000275 n = 0;
276 p = file_list;
277 while (p) {
278 ++n;
279 wsprintf(Buffer,
280 "import py_compile; py_compile.compile (r'%s')",
281 p->path);
282 if (PyRun_SimpleString(Buffer)) {
283 ++errors;
284 }
285 /* We send the notification even if the files could not
286 * be created so that the uninstaller will remove them
287 * in case they are created later.
288 */
289 wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c');
290 notify(FILE_CREATED, Buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000291
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000292 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
293 SetDlgItemText(hDialog, IDC_INFO, p->path);
294 p = p->next;
295 }
296 return errors;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000297}
298
299#define DECLPROC(dll, result, name, args)\
300 typedef result (*__PROC__##name) args;\
301 result (*name)args = (__PROC__##name)GetProcAddress(dll, #name)
302
303
304#define DECLVAR(dll, type, name)\
305 type *name = (type*)GetProcAddress(dll, #name)
306
307typedef void PyObject;
308
Mark Hammond891f2632009-01-29 13:08:01 +0000309// Convert a "char *" string to "whcar_t *", or NULL on error.
310// Result string must be free'd
311wchar_t *widen_string(char *src)
312{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000313 wchar_t *result;
314 DWORD dest_cch;
315 int src_len = strlen(src) + 1; // include NULL term in all ops
316 /* use MultiByteToWideChar() to see how much we need. */
317 /* NOTE: this will include the null-term in the length */
318 dest_cch = MultiByteToWideChar(CP_ACP, 0, src, src_len, NULL, 0);
319 // alloc the buffer
320 result = (wchar_t *)malloc(dest_cch * sizeof(wchar_t));
321 if (result==NULL)
322 return NULL;
323 /* do the conversion */
324 if (0==MultiByteToWideChar(CP_ACP, 0, src, src_len, result, dest_cch)) {
325 free(result);
326 return NULL;
327 }
328 return result;
Mark Hammond891f2632009-01-29 13:08:01 +0000329}
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000330
331/*
332 * Returns number of files which failed to compile,
333 * -1 if python could not be loaded at all
334 */
335static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag)
336{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000337 DECLPROC(hPython, void, Py_Initialize, (void));
338 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
339 DECLPROC(hPython, void, Py_Finalize, (void));
340 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
341 DECLPROC(hPython, PyObject *, PySys_GetObject, (char *));
342 DECLVAR(hPython, int, Py_OptimizeFlag);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000343
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000344 int errors = 0;
345 struct tagFile *p = file_list;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000346
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000347 if (!p)
348 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000349
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000350 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize)
351 return -1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000352
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000353 if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag)
354 return -1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000355
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000356 *Py_OptimizeFlag = optimize_flag ? 1 : 0;
357 Py_SetProgramName(wmodulename);
358 Py_Initialize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000359
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000360 errors += do_compile_files(PyRun_SimpleString, optimize_flag);
361 Py_Finalize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000362
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000363 return errors;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000364}
365
366typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
367
368struct PyMethodDef {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000369 char *ml_name;
370 PyCFunction ml_meth;
371 int ml_flags;
372 char *ml_doc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000373};
374typedef struct PyMethodDef PyMethodDef;
375
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000376// XXX - all of these are potentially fragile! We load and unload
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000377// the Python DLL multiple times - so storing functions pointers
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000378// is dangerous (although things *look* OK at present)
379// Better might be to roll prepare_script_environment() into
380// LoadPythonDll(), and create a new UnloadPythonDLL() which also
381// clears the global pointers.
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000382void *(*g_Py_BuildValue)(char *, ...);
383int (*g_PyArg_ParseTuple)(PyObject *, char *, ...);
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000384PyObject * (*g_PyLong_FromVoidPtr)(void *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000385
386PyObject *g_PyExc_ValueError;
387PyObject *g_PyExc_OSError;
388
389PyObject *(*g_PyErr_Format)(PyObject *, char *, ...);
390
391#define DEF_CSIDL(name) { name, #name }
392
393struct {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000394 int nFolder;
395 char *name;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000396} csidl_names[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000397 /* Startup menu for all users.
398 NT only */
399 DEF_CSIDL(CSIDL_COMMON_STARTMENU),
400 /* Startup menu. */
401 DEF_CSIDL(CSIDL_STARTMENU),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000402
403/* DEF_CSIDL(CSIDL_COMMON_APPDATA), */
404/* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000405 /* Repository for application-specific data.
406 Needs Internet Explorer 4.0 */
407 DEF_CSIDL(CSIDL_APPDATA),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000408
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000409 /* The desktop for all users.
410 NT only */
411 DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY),
412 /* The desktop. */
413 DEF_CSIDL(CSIDL_DESKTOPDIRECTORY),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000414
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000415 /* Startup folder for all users.
416 NT only */
417 DEF_CSIDL(CSIDL_COMMON_STARTUP),
418 /* Startup folder. */
419 DEF_CSIDL(CSIDL_STARTUP),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000420
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000421 /* Programs item in the start menu for all users.
422 NT only */
423 DEF_CSIDL(CSIDL_COMMON_PROGRAMS),
424 /* Program item in the user's start menu. */
425 DEF_CSIDL(CSIDL_PROGRAMS),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000426
427/* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
428/* DEF_CSIDL(CSIDL_PROGRAM_FILES), */
429
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000430 /* Virtual folder containing fonts. */
431 DEF_CSIDL(CSIDL_FONTS),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000432};
433
434#define DIM(a) (sizeof(a) / sizeof((a)[0]))
435
436static PyObject *FileCreated(PyObject *self, PyObject *args)
437{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000438 char *path;
439 if (!g_PyArg_ParseTuple(args, "s", &path))
440 return NULL;
441 notify(FILE_CREATED, path);
442 return g_Py_BuildValue("");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000443}
444
445static PyObject *DirectoryCreated(PyObject *self, PyObject *args)
446{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000447 char *path;
448 if (!g_PyArg_ParseTuple(args, "s", &path))
449 return NULL;
450 notify(DIR_CREATED, path);
451 return g_Py_BuildValue("");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000452}
453
454static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args)
455{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000456 char *name;
457 char lpszPath[MAX_PATH];
458 int i;
459 static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd,
460 LPTSTR lpszPath,
461 int nFolder,
462 BOOL fCreate);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000463
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000464 if (!My_SHGetSpecialFolderPath) {
465 HINSTANCE hLib = LoadLibrary("shell32.dll");
466 if (!hLib) {
467 g_PyErr_Format(g_PyExc_OSError,
468 "function not available");
469 return NULL;
470 }
471 My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR,
472 int, BOOL))
473 GetProcAddress(hLib,
474 "SHGetSpecialFolderPathA");
475 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000476
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000477 if (!g_PyArg_ParseTuple(args, "s", &name))
478 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000479
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000480 if (!My_SHGetSpecialFolderPath) {
481 g_PyErr_Format(g_PyExc_OSError, "function not available");
482 return NULL;
483 }
484
485 for (i = 0; i < DIM(csidl_names); ++i) {
486 if (0 == strcmpi(csidl_names[i].name, name)) {
487 int nFolder;
488 nFolder = csidl_names[i].nFolder;
489 if (My_SHGetSpecialFolderPath(NULL, lpszPath,
490 nFolder, 0))
491 return g_Py_BuildValue("s", lpszPath);
492 else {
493 g_PyErr_Format(g_PyExc_OSError,
494 "no such folder (%s)", name);
495 return NULL;
496 }
497
498 }
499 };
500 g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name);
501 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000502}
503
504static PyObject *CreateShortcut(PyObject *self, PyObject *args)
505{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000506 char *path; /* path and filename */
507 char *description;
508 char *filename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000509
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000510 char *arguments = NULL;
511 char *iconpath = NULL;
512 int iconindex = 0;
513 char *workdir = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000514
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000515 WCHAR wszFilename[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000516
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000517 IShellLink *ps1 = NULL;
518 IPersistFile *pPf = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000519
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000520 HRESULT hr;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000521
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000522 hr = CoInitialize(NULL);
523 if (FAILED(hr)) {
524 g_PyErr_Format(g_PyExc_OSError,
525 "CoInitialize failed, error 0x%x", hr);
526 goto error;
527 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000528
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000529 if (!g_PyArg_ParseTuple(args, "sss|sssi",
530 &path, &description, &filename,
531 &arguments, &workdir, &iconpath, &iconindex))
532 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000533
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000534 hr = CoCreateInstance(&CLSID_ShellLink,
535 NULL,
536 CLSCTX_INPROC_SERVER,
537 &IID_IShellLink,
538 &ps1);
539 if (FAILED(hr)) {
540 g_PyErr_Format(g_PyExc_OSError,
541 "CoCreateInstance failed, error 0x%x", hr);
542 goto error;
543 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000544
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000545 hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile,
546 (void **)&pPf);
547 if (FAILED(hr)) {
548 g_PyErr_Format(g_PyExc_OSError,
549 "QueryInterface(IPersistFile) error 0x%x", hr);
550 goto error;
551 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000552
553
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000554 hr = ps1->lpVtbl->SetPath(ps1, path);
555 if (FAILED(hr)) {
556 g_PyErr_Format(g_PyExc_OSError,
557 "SetPath() failed, error 0x%x", hr);
558 goto error;
559 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000560
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000561 hr = ps1->lpVtbl->SetDescription(ps1, description);
562 if (FAILED(hr)) {
563 g_PyErr_Format(g_PyExc_OSError,
564 "SetDescription() failed, error 0x%x", hr);
565 goto error;
566 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000567
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000568 if (arguments) {
569 hr = ps1->lpVtbl->SetArguments(ps1, arguments);
570 if (FAILED(hr)) {
571 g_PyErr_Format(g_PyExc_OSError,
572 "SetArguments() error 0x%x", hr);
573 goto error;
574 }
575 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000576
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000577 if (iconpath) {
578 hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex);
579 if (FAILED(hr)) {
580 g_PyErr_Format(g_PyExc_OSError,
581 "SetIconLocation() error 0x%x", hr);
582 goto error;
583 }
584 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000585
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000586 if (workdir) {
587 hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir);
588 if (FAILED(hr)) {
589 g_PyErr_Format(g_PyExc_OSError,
590 "SetWorkingDirectory() error 0x%x", hr);
591 goto error;
592 }
593 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000594
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000595 MultiByteToWideChar(CP_ACP, 0,
596 filename, -1,
597 wszFilename, MAX_PATH);
598
599 hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
600 if (FAILED(hr)) {
601 g_PyErr_Format(g_PyExc_OSError,
602 "Failed to create shortcut '%s' - error 0x%x", filename, hr);
603 goto error;
604 }
605
606 pPf->lpVtbl->Release(pPf);
607 ps1->lpVtbl->Release(ps1);
608 CoUninitialize();
609 return g_Py_BuildValue("");
610
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000611 error:
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000612 if (pPf)
613 pPf->lpVtbl->Release(pPf);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000614
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000615 if (ps1)
616 ps1->lpVtbl->Release(ps1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000617
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000618 CoUninitialize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000619
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000620 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000621}
622
Thomas Hellera19cdad2004-02-20 14:43:21 +0000623static PyObject *PyMessageBox(PyObject *self, PyObject *args)
624{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000625 int rc;
626 char *text, *caption;
627 int flags;
628 if (!g_PyArg_ParseTuple(args, "ssi", &text, &caption, &flags))
629 return NULL;
630 rc = MessageBox(GetFocus(), text, caption, flags);
631 return g_Py_BuildValue("i", rc);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000632}
633
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000634static PyObject *GetRootHKey(PyObject *self)
635{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000636 return g_PyLong_FromVoidPtr(hkey_root);
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000637}
638
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000639#define METH_VARARGS 0x0001
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000640#define METH_NOARGS 0x0004
641typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000642
643PyMethodDef meth[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000644 {"create_shortcut", CreateShortcut, METH_VARARGS, NULL},
645 {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
646 {"get_root_hkey", (PyCFunction)GetRootHKey, METH_NOARGS, NULL},
647 {"file_created", FileCreated, METH_VARARGS, NULL},
648 {"directory_created", DirectoryCreated, METH_VARARGS, NULL},
649 {"message_box", PyMessageBox, METH_VARARGS, NULL},
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000650};
651
Thomas Heller48340392004-06-18 17:03:38 +0000652static HINSTANCE LoadPythonDll(char *fname)
653{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000654 char fullpath[_MAX_PATH];
655 LONG size = sizeof(fullpath);
656 char subkey_name[80];
657 char buffer[260 + 12];
658 HINSTANCE h;
Thomas Heller8abe7bf2005-02-03 20:11:28 +0000659
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000660 /* make sure PYTHONHOME is set, to that sys.path is initialized correctly */
661 wsprintf(buffer, "PYTHONHOME=%s", python_dir);
662 _putenv(buffer);
663 h = LoadLibrary(fname);
664 if (h)
665 return h;
666 wsprintf(subkey_name,
Steve Dower332334f2016-01-16 13:54:53 -0800667 "SOFTWARE\\Python\\PythonCore\\%d.%d%s\\InstallPath",
668 py_major, py_minor, REGISTRY_SUFFIX_6432);
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000669 if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
670 fullpath, &size) &&
671 ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
672 fullpath, &size))
673 return NULL;
674 strcat(fullpath, "\\");
675 strcat(fullpath, fname);
Steve Dower332334f2016-01-16 13:54:53 -0800676 // We use LOAD_WITH_ALTERED_SEARCH_PATH to ensure any dependent DLLs
677 // next to the Python DLL (eg, the CRT DLL) are also loaded.
678 return LoadLibraryEx(fullpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
Thomas Heller48340392004-06-18 17:03:38 +0000679}
680
Thomas Hellera19cdad2004-02-20 14:43:21 +0000681static int prepare_script_environment(HINSTANCE hPython)
682{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000683 PyObject *mod;
684 DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
685 DECLPROC(hPython, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
686 DECLPROC(hPython, PyObject *, PyObject_GetAttrString, (PyObject *, char *));
687 DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *));
688 DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
689 DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
690 DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
691 DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
692 if (!PyImport_ImportModule || !PyObject_GetAttrString ||
693 !PyObject_SetAttrString || !PyCFunction_New)
694 return 1;
695 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
696 return 1;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000697
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000698 mod = PyImport_ImportModule("builtins");
699 if (mod) {
700 int i;
701 g_PyExc_ValueError = PyObject_GetAttrString(mod, "ValueError");
702 g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
703 for (i = 0; i < DIM(meth); ++i) {
704 PyObject_SetAttrString(mod, meth[i].ml_name,
705 PyCFunction_New(&meth[i], NULL));
706 }
707 }
708 g_Py_BuildValue = Py_BuildValue;
709 g_PyArg_ParseTuple = PyArg_ParseTuple;
710 g_PyErr_Format = PyErr_Format;
711 g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000712
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000713 return 0;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000714}
715
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000716/*
717 * This function returns one of the following error codes:
718 * 1 if the Python-dll does not export the functions we need
719 * 2 if no install-script is specified in pathname
720 * 3 if the install-script file could not be opened
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000721 * the return value of PyRun_SimpleString() otherwise,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000722 * which is 0 if everything is ok, -1 if an exception had occurred
723 * in the install-script.
724 */
725
726static int
Mark Hammond6d0e9752009-01-29 12:36:50 +0000727do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000728{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000729 int fh, result, i;
730 static wchar_t *wargv[256];
731 DECLPROC(hPython, void, Py_Initialize, (void));
732 DECLPROC(hPython, int, PySys_SetArgv, (int, wchar_t **));
733 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
734 DECLPROC(hPython, void, Py_Finalize, (void));
735 DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
736 DECLPROC(hPython, PyObject *, PyCFunction_New,
737 (PyMethodDef *, PyObject *));
738 DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
739 DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000740
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000741 if (!Py_Initialize || !PySys_SetArgv
742 || !PyRun_SimpleString || !Py_Finalize)
743 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000744
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000745 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
746 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000747
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000748 if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
749 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000750
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000751 if (pathname == NULL || pathname[0] == '\0')
752 return 2;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000753
Victor Stinnerdaf45552013-08-28 00:53:59 +0200754 fh = open(pathname, _O_RDONLY | O_NOINHERIT);
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000755 if (-1 == fh) {
756 fprintf(stderr, "Could not open postinstall-script %s\n",
757 pathname);
758 return 3;
759 }
Mark Hammond6d0e9752009-01-29 12:36:50 +0000760
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000761 SetDlgItemText(hDialog, IDC_INFO, "Running Script...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000762
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000763 Py_Initialize();
Mark Hammond891f2632009-01-29 13:08:01 +0000764
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000765 prepare_script_environment(hPython);
766 // widen the argv array for py3k.
767 memset(wargv, 0, sizeof(wargv));
768 for (i=0;i<argc;i++)
769 wargv[i] = argv[i] ? widen_string(argv[i]) : NULL;
770 PySys_SetArgv(argc, wargv);
771 // free the strings we just widened.
772 for (i=0;i<argc;i++)
773 if (wargv[i])
774 free(wargv[i]);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000775
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000776 result = 3;
777 {
778 struct _stat statbuf;
779 if(0 == _fstat(fh, &statbuf)) {
780 char *script = alloca(statbuf.st_size + 5);
781 int n = read(fh, script, statbuf.st_size);
782 if (n > 0) {
783 script[n] = '\n';
784 script[n+1] = 0;
785 result = PyRun_SimpleString(script);
786 }
787 }
788 }
789 Py_Finalize();
790
791 close(fh);
792 return result;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000793}
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000794
Mark Hammond6d0e9752009-01-29 12:36:50 +0000795static int
796run_installscript(char *pathname, int argc, char **argv, char **pOutput)
797{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000798 HINSTANCE hPython;
799 int result = 1;
800 int out_buf_size;
801 HANDLE redirected, old_stderr, old_stdout;
802 char *tempname;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000803
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000804 *pOutput = NULL;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000805
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000806 tempname = tempnam(NULL, NULL);
807 // We use a static CRT while the Python version we load uses
Ezio Melotti13925002011-03-16 11:05:33 +0200808 // the CRT from one of various possible DLLs. As a result we
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000809 // need to redirect the standard handles using the API rather
810 // than the CRT.
811 redirected = CreateFile(
812 tempname,
813 GENERIC_WRITE | GENERIC_READ,
814 FILE_SHARE_READ,
815 NULL,
816 CREATE_ALWAYS,
817 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
818 NULL);
819 old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
820 old_stderr = GetStdHandle(STD_ERROR_HANDLE);
821 SetStdHandle(STD_OUTPUT_HANDLE, redirected);
822 SetStdHandle(STD_ERROR_HANDLE, redirected);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000823
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000824 hPython = LoadPythonDll(pythondll);
825 if (hPython) {
826 result = do_run_installscript(hPython, pathname, argc, argv);
827 FreeLibrary(hPython);
828 } else {
829 fprintf(stderr, "*** Could not load Python ***");
830 }
831 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
832 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
833 out_buf_size = min(GetFileSize(redirected, NULL), 4096);
834 *pOutput = malloc(out_buf_size+1);
835 if (*pOutput) {
836 DWORD nread = 0;
837 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
838 ReadFile(redirected, *pOutput, out_buf_size, &nread, NULL);
839 (*pOutput)[nread] = '\0';
840 }
841 CloseHandle(redirected);
842 DeleteFile(tempname);
843 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000844}
845
Thomas Hellera19cdad2004-02-20 14:43:21 +0000846static int do_run_simple_script(HINSTANCE hPython, char *script)
847{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000848 int rc;
849 DECLPROC(hPython, void, Py_Initialize, (void));
850 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
851 DECLPROC(hPython, void, Py_Finalize, (void));
852 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
853 DECLPROC(hPython, void, PyErr_Print, (void));
Thomas Hellera19cdad2004-02-20 14:43:21 +0000854
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000855 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
856 !PyRun_SimpleString || !PyErr_Print)
857 return -1;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000858
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000859 Py_SetProgramName(wmodulename);
860 Py_Initialize();
861 prepare_script_environment(hPython);
862 rc = PyRun_SimpleString(script);
863 if (rc)
864 PyErr_Print();
865 Py_Finalize();
866 return rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000867}
868
869static int run_simple_script(char *script)
870{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000871 int rc;
872 HINSTANCE hPython;
873 char *tempname = tempnam(NULL, NULL);
874 // Redirect output using win32 API - see comments above...
875 HANDLE redirected = CreateFile(
876 tempname,
877 GENERIC_WRITE | GENERIC_READ,
878 FILE_SHARE_READ,
879 NULL,
880 CREATE_ALWAYS,
881 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
882 NULL);
883 HANDLE old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
884 HANDLE old_stderr = GetStdHandle(STD_ERROR_HANDLE);
885 SetStdHandle(STD_OUTPUT_HANDLE, redirected);
886 SetStdHandle(STD_ERROR_HANDLE, redirected);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000887
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000888 hPython = LoadPythonDll(pythondll);
889 if (!hPython) {
890 char reason[128];
891 wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
892 set_failure_reason(reason);
893 return -1;
894 }
895 rc = do_run_simple_script(hPython, script);
896 FreeLibrary(hPython);
897 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
898 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
899 /* We only care about the output when we fail. If the script works
900 OK, then we discard it
901 */
902 if (rc) {
903 int err_buf_size;
904 char *err_buf;
905 const char *prefix = "Running the pre-installation script failed\r\n";
906 int prefix_len = strlen(prefix);
907 err_buf_size = GetFileSize(redirected, NULL);
908 if (err_buf_size==INVALID_FILE_SIZE) // an error - let's try anyway...
909 err_buf_size = 4096;
910 err_buf = malloc(prefix_len + err_buf_size + 1);
911 if (err_buf) {
912 DWORD n = 0;
913 strcpy(err_buf, prefix);
914 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
915 ReadFile(redirected, err_buf+prefix_len, err_buf_size, &n, NULL);
916 err_buf[prefix_len+n] = '\0';
917 set_failure_reason(err_buf);
918 free(err_buf);
919 } else {
920 set_failure_reason("Out of memory!");
921 }
922 }
923 CloseHandle(redirected);
924 DeleteFile(tempname);
925 return rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000926}
927
928
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000929static BOOL SystemError(int error, char *msg)
930{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000931 char Buffer[1024];
932 int n;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000933
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000934 if (error) {
935 LPVOID lpMsgBuf;
936 FormatMessage(
937 FORMAT_MESSAGE_ALLOCATE_BUFFER |
938 FORMAT_MESSAGE_FROM_SYSTEM,
939 NULL,
940 error,
941 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
942 (LPSTR)&lpMsgBuf,
943 0,
944 NULL
945 );
946 strncpy(Buffer, lpMsgBuf, sizeof(Buffer));
947 LocalFree(lpMsgBuf);
948 } else
949 Buffer[0] = '\0';
950 n = lstrlen(Buffer);
951 _snprintf(Buffer+n, sizeof(Buffer)-n, msg);
952 MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
953 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000954}
955
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000956static BOOL notify (int code, char *fmt, ...)
957{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000958 char Buffer[1024];
959 va_list marker;
960 BOOL result = TRUE;
961 int a, b;
962 char *cp;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000963
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000964 va_start(marker, fmt);
965 _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000966
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000967 switch (code) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000968/* Questions */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000969 case CAN_OVERWRITE:
970 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000971
972/* Information notification */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000973 case DIR_CREATED:
974 if (logfile)
975 fprintf(logfile, "100 Made Dir: %s\n", fmt);
976 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000977
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000978 case FILE_CREATED:
979 if (logfile)
980 fprintf(logfile, "200 File Copy: %s\n", fmt);
981 goto add_to_filelist_label;
982 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000983
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000984 case FILE_OVERWRITTEN:
985 if (logfile)
986 fprintf(logfile, "200 File Overwrite: %s\n", fmt);
987 add_to_filelist_label:
988 if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
989 add_to_filelist(fmt);
990 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000991
992/* Error Messages */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000993 case ZLIB_ERROR:
994 MessageBox(GetFocus(), Buffer, "Error",
995 MB_OK | MB_ICONWARNING);
996 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000997
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000998 case SYSTEM_ERROR:
999 SystemError(GetLastError(), Buffer);
1000 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001001
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001002 case NUM_FILES:
1003 a = va_arg(marker, int);
1004 b = va_arg(marker, int);
1005 SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a));
1006 SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt);
1007 }
1008 va_end(marker);
1009
1010 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001011}
1012
1013static char *MapExistingFile(char *pathname, DWORD *psize)
1014{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001015 HANDLE hFile, hFileMapping;
1016 DWORD nSizeLow, nSizeHigh;
1017 char *data;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001018
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001019 hFile = CreateFile(pathname,
1020 GENERIC_READ, FILE_SHARE_READ, NULL,
1021 OPEN_EXISTING,
1022 FILE_ATTRIBUTE_NORMAL, NULL);
1023 if (hFile == INVALID_HANDLE_VALUE)
1024 return NULL;
1025 nSizeLow = GetFileSize(hFile, &nSizeHigh);
1026 hFileMapping = CreateFileMapping(hFile,
1027 NULL, PAGE_READONLY, 0, 0, NULL);
1028 CloseHandle(hFile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001029
Gregory P. Smithb803c6c2013-03-23 16:05:36 -07001030 if (hFileMapping == NULL)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001031 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001032
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001033 data = MapViewOfFile(hFileMapping,
1034 FILE_MAP_READ, 0, 0, 0);
1035
1036 CloseHandle(hFileMapping);
1037 *psize = nSizeLow;
1038 return data;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001039}
1040
1041
1042static void create_bitmap(HWND hwnd)
1043{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001044 BITMAPFILEHEADER *bfh;
1045 BITMAPINFO *bi;
1046 HDC hdc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001047
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001048 if (!bitmap_bytes)
1049 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001050
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001051 if (hBitmap)
1052 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001053
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001054 hdc = GetDC(hwnd);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001055
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001056 bfh = (BITMAPFILEHEADER *)bitmap_bytes;
1057 bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001058
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001059 hBitmap = CreateDIBitmap(hdc,
1060 &bi->bmiHeader,
1061 CBM_INIT,
1062 bitmap_bytes + bfh->bfOffBits,
1063 bi,
1064 DIB_RGB_COLORS);
1065 ReleaseDC(hwnd, hdc);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001066}
1067
Thomas Hellera19cdad2004-02-20 14:43:21 +00001068/* Extract everything we need to begin the installation. Currently this
1069 is the INI filename with install data, and the raw pre-install script
1070*/
1071static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001072 char **out_ini_file, char **out_preinstall_script)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001073{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001074 /* read the end of central directory record */
1075 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
1076 (struct eof_cdir)];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001077
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001078 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
1079 pe->ofsCDir;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001080
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001081 int ofs = arc_start - sizeof (struct meta_data_hdr);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001082
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001083 /* read meta_data info */
1084 struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
1085 char *src, *dst;
1086 char *ini_file;
1087 char tempdir[MAX_PATH];
Thomas Hellera19cdad2004-02-20 14:43:21 +00001088
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001089 /* ensure that if we fail, we don't have garbage out pointers */
1090 *out_ini_file = *out_preinstall_script = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001091
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001092 if (pe->tag != 0x06054b50) {
1093 return FALSE;
1094 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001095
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001096 if (pmd->tag != 0x1234567B) {
1097 return SystemError(0,
1098 "Invalid cfgdata magic number (see bdist_wininst.py)");
1099 }
1100 if (ofs < 0) {
1101 return FALSE;
1102 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001103
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001104 if (pmd->bitmap_size) {
1105 /* Store pointer to bitmap bytes */
1106 bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
1107 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001108
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001109 *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size;
1110
1111 src = ((char *)pmd) - pmd->uncomp_size;
1112 ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
1113 if (!ini_file)
1114 return FALSE;
1115 if (!GetTempPath(sizeof(tempdir), tempdir)
1116 || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
1117 SystemError(GetLastError(),
1118 "Could not create temporary file");
1119 return FALSE;
1120 }
1121
1122 dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
1123 0, 0, NULL/*notify*/);
1124 if (!dst)
1125 return FALSE;
1126 /* Up to the first \0 is the INI file data. */
1127 strncpy(dst, src, pmd->uncomp_size);
1128 src += strlen(dst) + 1;
1129 /* Up to next \0 is the pre-install script */
1130 *out_preinstall_script = strdup(src);
1131 *out_ini_file = ini_file;
1132 UnmapViewOfFile(dst);
1133 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001134}
1135
1136static void PumpMessages(void)
1137{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001138 MSG msg;
1139 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1140 TranslateMessage(&msg);
1141 DispatchMessage(&msg);
1142 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001143}
1144
1145LRESULT CALLBACK
1146WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1147{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001148 HDC hdc;
1149 HFONT hFont;
1150 int h;
1151 PAINTSTRUCT ps;
1152 switch (msg) {
1153 case WM_PAINT:
1154 hdc = BeginPaint(hwnd, &ps);
1155 h = GetSystemMetrics(SM_CYSCREEN) / 10;
1156 hFont = CreateFont(h, 0, 0, 0, 700, TRUE,
1157 0, 0, 0, 0, 0, 0, 0, "Times Roman");
1158 hFont = SelectObject(hdc, hFont);
1159 SetBkMode(hdc, TRANSPARENT);
1160 TextOut(hdc, 15, 15, title, strlen(title));
1161 SetTextColor(hdc, RGB(255, 255, 255));
1162 TextOut(hdc, 10, 10, title, strlen(title));
1163 DeleteObject(SelectObject(hdc, hFont));
1164 EndPaint(hwnd, &ps);
1165 return 0;
1166 }
1167 return DefWindowProc(hwnd, msg, wParam, lParam);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001168}
1169
1170static HWND CreateBackground(char *title)
1171{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001172 WNDCLASS wc;
1173 HWND hwnd;
1174 char buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001175
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001176 wc.style = CS_VREDRAW | CS_HREDRAW;
1177 wc.lpfnWndProc = WindowProc;
1178 wc.cbWndExtra = 0;
1179 wc.cbClsExtra = 0;
1180 wc.hInstance = GetModuleHandle(NULL);
1181 wc.hIcon = NULL;
1182 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1183 wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128));
1184 wc.lpszMenuName = NULL;
1185 wc.lpszClassName = "SetupWindowClass";
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001186
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001187 if (!RegisterClass(&wc))
1188 MessageBox(hwndMain,
1189 "Could not register window class",
1190 "Setup.exe", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001191
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001192 wsprintf(buffer, "Setup %s", title);
1193 hwnd = CreateWindow("SetupWindowClass",
1194 buffer,
1195 0,
1196 0, 0,
1197 GetSystemMetrics(SM_CXFULLSCREEN),
1198 GetSystemMetrics(SM_CYFULLSCREEN),
1199 NULL,
1200 NULL,
1201 GetModuleHandle(NULL),
1202 NULL);
1203 ShowWindow(hwnd, SW_SHOWMAXIMIZED);
1204 UpdateWindow(hwnd);
1205 return hwnd;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001206}
1207
1208/*
1209 * Center a window on the screen
1210 */
1211static void CenterWindow(HWND hwnd)
1212{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001213 RECT rc;
1214 int w, h;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001215
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001216 GetWindowRect(hwnd, &rc);
1217 w = GetSystemMetrics(SM_CXSCREEN);
1218 h = GetSystemMetrics(SM_CYSCREEN);
1219 MoveWindow(hwnd,
1220 (w - (rc.right-rc.left))/2,
1221 (h - (rc.bottom-rc.top))/2,
1222 rc.right-rc.left, rc.bottom-rc.top, FALSE);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001223}
1224
1225#include <prsht.h>
1226
Steve Dower65e4cb12014-11-22 12:54:57 -08001227INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001228IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1229{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001230 LPNMHDR lpnm;
1231 char Buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001232
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001233 switch (msg) {
1234 case WM_INITDIALOG:
1235 create_bitmap(hwnd);
1236 if(hBitmap)
1237 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1238 IMAGE_BITMAP, (LPARAM)hBitmap);
1239 CenterWindow(GetParent(hwnd));
1240 wsprintf(Buffer,
1241 "This Wizard will install %s on your computer. "
1242 "Click Next to continue "
1243 "or Cancel to exit the Setup Wizard.",
1244 meta_name);
1245 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1246 SetDlgItemText(hwnd, IDC_INTRO_TEXT, info);
1247 SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info);
1248 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001249
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001250 case WM_NOTIFY:
1251 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001252
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001253 switch (lpnm->code) {
1254 case PSN_SETACTIVE:
1255 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
1256 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001257
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001258 case PSN_WIZNEXT:
1259 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001260
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001261 case PSN_RESET:
1262 break;
1263
1264 default:
1265 break;
1266 }
1267 }
1268 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001269}
1270
1271#ifdef USE_OTHER_PYTHON_VERSIONS
1272/* These are really private variables used to communicate
1273 * between StatusRoutine and CheckPythonExe
1274 */
1275char bound_image_dll[_MAX_PATH];
1276int bound_image_major;
1277int bound_image_minor;
1278
1279static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001280 PSTR ImageName,
1281 PSTR DllName,
1282 ULONG Va,
1283 ULONG Parameter)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001284{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001285 char fname[_MAX_PATH];
1286 int int_version;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001287
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001288 switch(reason) {
1289 case BindOutOfMemory:
1290 case BindRvaToVaFailed:
1291 case BindNoRoomInImage:
1292 case BindImportProcedureFailed:
1293 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001294
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001295 case BindImportProcedure:
1296 case BindForwarder:
1297 case BindForwarderNOT:
1298 case BindImageModified:
1299 case BindExpandFileHeaders:
1300 case BindImageComplete:
1301 case BindSymbolsNotUpdated:
1302 case BindMismatchedSymbols:
1303 case BindImportModuleFailed:
1304 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001305
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001306 case BindImportModule:
1307 if (1 == sscanf(DllName, "python%d", &int_version)) {
1308 SearchPath(NULL, DllName, NULL, sizeof(fname),
1309 fname, NULL);
1310 strcpy(bound_image_dll, fname);
1311 bound_image_major = int_version / 10;
1312 bound_image_minor = int_version % 10;
1313 OutputDebugString("BOUND ");
1314 OutputDebugString(fname);
1315 OutputDebugString("\n");
1316 }
1317 break;
1318 }
1319 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001320}
1321
1322/*
1323 */
1324static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
1325{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001326 void (__cdecl * Py_Initialize)(void);
1327 void (__cdecl * Py_SetProgramName)(char *);
1328 void (__cdecl * Py_Finalize)(void);
1329 void* (__cdecl * PySys_GetObject)(char *);
1330 void (__cdecl * PySys_SetArgv)(int, char **);
1331 char* (__cdecl * Py_GetPrefix)(void);
1332 char* (__cdecl * Py_GetPath)(void);
1333 HINSTANCE hPython;
1334 LPSTR prefix = NULL;
1335 int (__cdecl * PyRun_SimpleString)(char *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001336
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001337 {
1338 char Buffer[256];
1339 wsprintf(Buffer, "PYTHONHOME=%s", exe);
1340 *strrchr(Buffer, '\\') = '\0';
1341// MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
1342 _putenv(Buffer);
1343 _putenv("PYTHONPATH=");
1344 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001345
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001346 hPython = LoadLibrary(dll);
1347 if (!hPython)
1348 return NULL;
1349 Py_Initialize = (void (*)(void))GetProcAddress
1350 (hPython,"Py_Initialize");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001351
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001352 PySys_SetArgv = (void (*)(int, char **))GetProcAddress
1353 (hPython,"PySys_SetArgv");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001354
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001355 PyRun_SimpleString = (int (*)(char *))GetProcAddress
1356 (hPython,"PyRun_SimpleString");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001357
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001358 Py_SetProgramName = (void (*)(char *))GetProcAddress
1359 (hPython,"Py_SetProgramName");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001360
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001361 PySys_GetObject = (void* (*)(char *))GetProcAddress
1362 (hPython,"PySys_GetObject");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001363
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001364 Py_GetPrefix = (char * (*)(void))GetProcAddress
1365 (hPython,"Py_GetPrefix");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001366
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001367 Py_GetPath = (char * (*)(void))GetProcAddress
1368 (hPython,"Py_GetPath");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001369
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001370 Py_Finalize = (void (*)(void))GetProcAddress(hPython,
1371 "Py_Finalize");
1372 Py_SetProgramName(exe);
1373 Py_Initialize();
1374 PySys_SetArgv(1, &exe);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001375
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001376 MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
1377 MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001378
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001379 Py_Finalize();
1380 FreeLibrary(hPython);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001381
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001382 return prefix;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001383}
1384
1385static BOOL
1386CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
1387{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001388 bound_image_dll[0] = '\0';
1389 if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
1390 pathname,
1391 NULL,
1392 NULL,
1393 StatusRoutine))
1394 return SystemError(0, "Could not bind image");
1395 if (bound_image_dll[0] == '\0')
1396 return SystemError(0, "Does not seem to be a python executable");
1397 *pmajor = bound_image_major;
1398 *pminor = bound_image_minor;
1399 if (version && *version) {
1400 char core_version[12];
1401 wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor);
1402 if (strcmp(version, core_version))
1403 return SystemError(0, "Wrong Python version");
1404 }
1405 get_sys_prefix(pathname, bound_image_dll);
1406 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001407}
1408
1409/*
1410 * Browse for other python versions. Insert it into the listbox specified
1411 * by hwnd. version, if not NULL or empty, is the version required.
1412 */
1413static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version)
1414{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001415 char vers_name[_MAX_PATH + 80];
1416 DWORD itemindex;
1417 OPENFILENAME of;
1418 char pathname[_MAX_PATH];
1419 DWORD result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001420
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001421 strcpy(pathname, "python.exe");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001422
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001423 memset(&of, 0, sizeof(of));
1424 of.lStructSize = sizeof(OPENFILENAME);
1425 of.hwndOwner = GetParent(hwnd);
1426 of.hInstance = NULL;
1427 of.lpstrFilter = "python.exe\0python.exe\0";
1428 of.lpstrCustomFilter = NULL;
1429 of.nMaxCustFilter = 0;
1430 of.nFilterIndex = 1;
1431 of.lpstrFile = pathname;
1432 of.nMaxFile = sizeof(pathname);
1433 of.lpstrFileTitle = NULL;
1434 of.nMaxFileTitle = 0;
1435 of.lpstrInitialDir = NULL;
1436 of.lpstrTitle = "Python executable";
1437 of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
1438 of.lpstrDefExt = "exe";
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001439
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001440 result = GetOpenFileName(&of);
1441 if (result) {
1442 int major, minor;
1443 if (!CheckPythonExe(pathname, version, &major, &minor)) {
1444 return FALSE;
1445 }
1446 *strrchr(pathname, '\\') = '\0';
1447 wsprintf(vers_name, "Python Version %d.%d in %s",
1448 major, minor, pathname);
1449 itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1,
1450 (LPARAM)(LPSTR)vers_name);
1451 SendMessage(hwnd, LB_SETCURSEL, itemindex, 0);
1452 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1453 (LPARAM)(LPSTR)strdup(pathname));
1454 return TRUE;
1455 }
1456 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001457}
1458#endif /* USE_OTHER_PYTHON_VERSIONS */
1459
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001460typedef struct _InstalledVersionInfo {
1461 char prefix[MAX_PATH+1]; // sys.prefix directory.
1462 HKEY hkey; // Is this Python in HKCU or HKLM?
1463} InstalledVersionInfo;
1464
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001465
1466/*
1467 * Fill the listbox specified by hwnd with all python versions found
1468 * in the registry. version, if not NULL or empty, is the version
1469 * required.
1470 */
1471static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version)
1472{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001473 DWORD index = 0;
1474 char core_version[80];
1475 HKEY hKey;
1476 BOOL result = TRUE;
1477 DWORD bufsize;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001478
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001479 if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot,
1480 "Software\\Python\\PythonCore",
1481 0, KEY_READ, &hKey))
1482 return FALSE;
1483 bufsize = sizeof(core_version);
1484 while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index,
1485 core_version, &bufsize, NULL,
1486 NULL, NULL, NULL)) {
1487 char subkey_name[80], vers_name[80];
1488 int itemindex;
1489 DWORD value_size;
1490 HKEY hk;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001491
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001492 bufsize = sizeof(core_version);
1493 ++index;
1494 if (version && *version && strcmp(version, core_version))
1495 continue;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001496
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001497 wsprintf(vers_name, "Python Version %s (found in registry)",
1498 core_version);
1499 wsprintf(subkey_name,
1500 "Software\\Python\\PythonCore\\%s\\InstallPath",
1501 core_version);
1502 if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
1503 InstalledVersionInfo *ivi =
1504 (InstalledVersionInfo *)malloc(sizeof(InstalledVersionInfo));
1505 value_size = sizeof(ivi->prefix);
1506 if (ivi &&
1507 ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL,
1508 ivi->prefix, &value_size)) {
1509 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
1510 (LPARAM)(LPSTR)vers_name);
1511 ivi->hkey = hkRoot;
1512 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1513 (LPARAM)(LPSTR)ivi);
1514 }
1515 RegCloseKey(hk);
1516 }
1517 }
1518 RegCloseKey(hKey);
1519 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001520}
1521
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001522/* Determine if the current user can write to HKEY_LOCAL_MACHINE */
1523BOOL HasLocalMachinePrivs()
1524{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001525 HKEY hKey;
1526 DWORD result;
1527 static char KeyName[] =
1528 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001529
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001530 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1531 KeyName,
1532 0,
1533 KEY_CREATE_SUB_KEY,
1534 &hKey);
1535 if (result==0)
1536 RegCloseKey(hKey);
1537 return result==0;
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001538}
1539
1540// Check the root registry key to use - either HKLM or HKCU.
1541// If Python is installed in HKCU, then our extension also must be installed
1542// in HKCU - as Python won't be available for other users, we shouldn't either
1543// (and will fail if we are!)
1544// If Python is installed in HKLM, then we will also prefer to use HKLM, but
1545// this may not be possible - so we silently fall back to HKCU.
1546//
1547// We assume hkey_root is already set to where Python itself is installed.
1548void CheckRootKey(HWND hwnd)
1549{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001550 if (hkey_root==HKEY_CURRENT_USER) {
1551 ; // as above, always install ourself in HKCU too.
1552 } else if (hkey_root==HKEY_LOCAL_MACHINE) {
1553 // Python in HKLM, but we may or may not have permissions there.
1554 // Open the uninstall key with 'create' permissions - if this fails,
1555 // we don't have permission.
1556 if (!HasLocalMachinePrivs())
1557 hkey_root = HKEY_CURRENT_USER;
1558 } else {
1559 MessageBox(hwnd, "Don't know Python's installation type",
1560 "Strange", MB_OK | MB_ICONSTOP);
1561 /* Default to wherever they can, but preferring HKLM */
1562 hkey_root = HasLocalMachinePrivs() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1563 }
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001564}
1565
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001566/* Return the installation scheme depending on Python version number */
1567SCHEME *GetScheme(int major, int minor)
1568{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001569 if (major > 2)
1570 return new_scheme;
1571 else if((major == 2) && (minor >= 2))
1572 return new_scheme;
1573 return old_scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001574}
1575
Steve Dower65e4cb12014-11-22 12:54:57 -08001576INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001577SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1578{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001579 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001580
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001581 switch (msg) {
1582 case WM_INITDIALOG:
1583 if (hBitmap)
1584 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1585 IMAGE_BITMAP, (LPARAM)hBitmap);
1586 GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1587 HKEY_LOCAL_MACHINE, target_version);
1588 GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1589 HKEY_CURRENT_USER, target_version);
1590 { /* select the last entry which is the highest python
1591 version found */
1592 int count;
1593 count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1594 LB_GETCOUNT, 0, 0);
1595 if (count && count != LB_ERR)
1596 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
1597 count-1, 0);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001598
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001599 /* If a specific Python version is required,
1600 * display a prominent notice showing this fact.
1601 */
1602 if (target_version && target_version[0]) {
1603 char buffer[4096];
1604 wsprintf(buffer,
1605 "Python %s is required for this package. "
1606 "Select installation to use:",
1607 target_version);
1608 SetDlgItemText(hwnd, IDC_TITLE, buffer);
1609 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001610
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001611 if (count == 0) {
1612 char Buffer[4096];
1613 char *msg;
1614 if (target_version && target_version[0]) {
1615 wsprintf(Buffer,
1616 "Python version %s required, which was not found"
1617 " in the registry.", target_version);
1618 msg = Buffer;
1619 } else
1620 msg = "No Python installation found in the registry.";
1621 MessageBox(hwnd, msg, "Cannot install",
1622 MB_OK | MB_ICONSTOP);
1623 }
1624 }
1625 goto UpdateInstallDir;
1626 break;
1627
1628 case WM_COMMAND:
1629 switch (LOWORD(wParam)) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001630/*
1631 case IDC_OTHERPYTHON:
1632 if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1633 target_version))
1634 goto UpdateInstallDir;
1635 break;
1636*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001637 case IDC_VERSIONS_LIST:
1638 switch (HIWORD(wParam)) {
1639 int id;
1640 case LBN_SELCHANGE:
1641 UpdateInstallDir:
1642 PropSheet_SetWizButtons(GetParent(hwnd),
1643 PSWIZB_BACK | PSWIZB_NEXT);
1644 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1645 LB_GETCURSEL, 0, 0);
1646 if (id == LB_ERR) {
1647 PropSheet_SetWizButtons(GetParent(hwnd),
1648 PSWIZB_BACK);
1649 SetDlgItemText(hwnd, IDC_PATH, "");
1650 SetDlgItemText(hwnd, IDC_INSTALL_PATH, "");
1651 strcpy(python_dir, "");
1652 strcpy(pythondll, "");
1653 } else {
1654 char *pbuf;
1655 int result;
1656 InstalledVersionInfo *ivi;
1657 PropSheet_SetWizButtons(GetParent(hwnd),
1658 PSWIZB_BACK | PSWIZB_NEXT);
1659 /* Get the python directory */
1660 ivi = (InstalledVersionInfo *)
1661 SendDlgItemMessage(hwnd,
1662 IDC_VERSIONS_LIST,
1663 LB_GETITEMDATA,
1664 id,
1665 0);
1666 hkey_root = ivi->hkey;
1667 strcpy(python_dir, ivi->prefix);
1668 SetDlgItemText(hwnd, IDC_PATH, python_dir);
1669 /* retrieve the python version and pythondll to use */
1670 result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1671 LB_GETTEXTLEN, (WPARAM)id, 0);
1672 pbuf = (char *)malloc(result + 1);
1673 if (pbuf) {
1674 /* guess the name of the python-dll */
1675 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1676 LB_GETTEXT, (WPARAM)id,
1677 (LPARAM)pbuf);
1678 result = sscanf(pbuf, "Python Version %d.%d",
1679 &py_major, &py_minor);
1680 if (result == 2) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001681#ifdef _DEBUG
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001682 wsprintf(pythondll, "python%d%d_d.dll",
1683 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001684#else
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001685 wsprintf(pythondll, "python%d%d.dll",
1686 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001687#endif
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001688 }
1689 free(pbuf);
1690 } else
1691 strcpy(pythondll, "");
1692 /* retrieve the scheme for this version */
1693 {
1694 char install_path[_MAX_PATH];
1695 SCHEME *scheme = GetScheme(py_major, py_minor);
1696 strcpy(install_path, python_dir);
1697 if (install_path[strlen(install_path)-1] != '\\')
1698 strcat(install_path, "\\");
1699 strcat(install_path, scheme[0].prefix);
1700 SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
1701 }
1702 }
1703 }
1704 break;
1705 }
1706 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001707
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001708 case WM_NOTIFY:
1709 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001710
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001711 switch (lpnm->code) {
1712 int id;
1713 case PSN_SETACTIVE:
1714 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1715 LB_GETCURSEL, 0, 0);
1716 if (id == LB_ERR)
1717 PropSheet_SetWizButtons(GetParent(hwnd),
1718 PSWIZB_BACK);
1719 else
1720 PropSheet_SetWizButtons(GetParent(hwnd),
1721 PSWIZB_BACK | PSWIZB_NEXT);
1722 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001723
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001724 case PSN_WIZNEXT:
1725 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001726
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001727 case PSN_WIZFINISH:
1728 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001729
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001730 case PSN_RESET:
1731 break;
1732
1733 default:
1734 break;
1735 }
1736 }
1737 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001738}
1739
1740static BOOL OpenLogfile(char *dir)
1741{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001742 char buffer[_MAX_PATH+1];
1743 time_t ltime;
1744 struct tm *now;
1745 long result;
1746 HKEY hKey, hSubkey;
1747 char subkey_name[256];
1748 static char KeyName[] =
1749 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1750 const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
1751 "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
1752 DWORD disposition;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001753
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001754 /* Use Create, as the Uninstall subkey may not exist under HKCU.
1755 Use CreateKeyEx, so we can specify a SAM specifying write access
1756 */
1757 result = RegCreateKeyEx(hkey_root,
1758 KeyName,
1759 0, /* reserved */
1760 NULL, /* class */
1761 0, /* options */
1762 KEY_CREATE_SUB_KEY, /* sam */
1763 NULL, /* security */
1764 &hKey, /* result key */
1765 NULL); /* disposition */
1766 if (result != ERROR_SUCCESS) {
1767 if (result == ERROR_ACCESS_DENIED) {
1768 /* This should no longer be able to happen - we have already
1769 checked if they have permissions in HKLM, and all users
1770 should have write access to HKCU.
1771 */
1772 MessageBox(GetFocus(),
1773 "You do not seem to have sufficient access rights\n"
1774 "on this machine to install this software",
1775 NULL,
1776 MB_OK | MB_ICONSTOP);
1777 return FALSE;
1778 } else {
1779 MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK);
1780 }
1781 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001782
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001783 sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name);
1784 logfile = fopen(buffer, "a");
Benjamin Peterson25c7d3f2014-11-27 20:39:02 -06001785 if (!logfile) {
1786 char error[1024];
1787
1788 sprintf(error, "Can't create \"%s\" (%s).\n\n"
1789 "Try to execute the installer as administrator.",
1790 buffer, strerror(errno));
1791 MessageBox(GetFocus(), error, NULL, MB_OK | MB_ICONSTOP);
1792 return FALSE;
1793 }
1794
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001795 time(&ltime);
1796 now = localtime(&ltime);
1797 strftime(buffer, sizeof(buffer),
1798 "*** Installation started %Y/%m/%d %H:%M ***\n",
1799 localtime(&ltime));
1800 fprintf(logfile, buffer);
1801 fprintf(logfile, "Source: %s\n", modulename);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001802
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001803 /* Root key must be first entry processed by uninstaller. */
1804 fprintf(logfile, "999 Root Key: %s\n", root_name);
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001805
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001806 sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001807
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001808 result = RegCreateKeyEx(hKey, subkey_name,
1809 0, NULL, 0,
1810 KEY_WRITE,
1811 NULL,
1812 &hSubkey,
1813 &disposition);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001814
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001815 if (result != ERROR_SUCCESS)
1816 MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001817
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001818 RegCloseKey(hKey);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001819
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001820 if (disposition == REG_CREATED_NEW_KEY)
1821 fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001822
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001823 sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001824
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001825 result = RegSetValueEx(hSubkey, "DisplayName",
1826 0,
1827 REG_SZ,
1828 buffer,
1829 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001830
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001831 if (result != ERROR_SUCCESS)
1832 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001833
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001834 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1835 KeyName, subkey_name, "DisplayName", buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001836
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001837 {
1838 FILE *fp;
1839 sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
1840 fp = fopen(buffer, "wb");
1841 fwrite(arc_data, exe_size, 1, fp);
1842 fclose(fp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001843
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001844 sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1845 dir, meta_name, dir, meta_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001846
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001847 result = RegSetValueEx(hSubkey, "UninstallString",
1848 0,
1849 REG_SZ,
1850 buffer,
1851 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001852
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001853 if (result != ERROR_SUCCESS)
1854 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1855
1856 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1857 KeyName, subkey_name, "UninstallString", buffer);
1858 }
1859 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001860}
1861
1862static void CloseLogfile(void)
1863{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001864 char buffer[_MAX_PATH+1];
1865 time_t ltime;
1866 struct tm *now;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001867
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001868 time(&ltime);
1869 now = localtime(&ltime);
1870 strftime(buffer, sizeof(buffer),
1871 "*** Installation finished %Y/%m/%d %H:%M ***\n",
1872 localtime(&ltime));
1873 fprintf(logfile, buffer);
1874 if (logfile)
1875 fclose(logfile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001876}
1877
Steve Dower65e4cb12014-11-22 12:54:57 -08001878INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001879InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1880{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001881 LPNMHDR lpnm;
1882 char Buffer[4096];
1883 SCHEME *scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001884
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001885 switch (msg) {
1886 case WM_INITDIALOG:
1887 if (hBitmap)
1888 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1889 IMAGE_BITMAP, (LPARAM)hBitmap);
1890 wsprintf(Buffer,
1891 "Click Next to begin the installation of %s. "
1892 "If you want to review or change any of your "
1893 " installation settings, click Back. "
1894 "Click Cancel to exit the wizard.",
1895 meta_name);
1896 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1897 SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
1898 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001899
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001900 case WM_NUMFILES:
1901 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1902 PumpMessages();
1903 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001904
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001905 case WM_NEXTFILE:
1906 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
1907 0);
1908 SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
1909 PumpMessages();
1910 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001911
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001912 case WM_NOTIFY:
1913 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001914
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001915 switch (lpnm->code) {
1916 case PSN_SETACTIVE:
1917 PropSheet_SetWizButtons(GetParent(hwnd),
1918 PSWIZB_BACK | PSWIZB_NEXT);
1919 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001920
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001921 case PSN_WIZFINISH:
1922 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001923
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001924 case PSN_WIZNEXT:
1925 /* Handle a Next button click here */
1926 hDialog = hwnd;
1927 success = TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001928
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001929 /* Disable the buttons while we work. Sending CANCELTOCLOSE has
1930 the effect of disabling the cancel button, which is a) as we
1931 do everything synchronously we can't cancel, and b) the next
1932 step is 'finished', when it is too late to cancel anyway.
1933 The next step being 'Finished' means we also don't need to
1934 restore the button state back */
1935 PropSheet_SetWizButtons(GetParent(hwnd), 0);
1936 SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
1937 /* Make sure the installation directory name ends in a */
1938 /* backslash */
1939 if (python_dir[strlen(python_dir)-1] != '\\')
1940 strcat(python_dir, "\\");
1941 /* Strip the trailing backslash again */
1942 python_dir[strlen(python_dir)-1] = '\0';
1943
1944 CheckRootKey(hwnd);
1945
1946 if (!OpenLogfile(python_dir))
1947 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001948
1949/*
1950 * The scheme we have to use depends on the Python version...
1951 if sys.version < "2.2":
Serhiy Storchakad741a882015-06-11 00:06:39 +03001952 WINDOWS_SCHEME = {
1953 'purelib': '$base',
1954 'platlib': '$base',
1955 'headers': '$base/Include/$dist_name',
1956 'scripts': '$base/Scripts',
1957 'data' : '$base',
1958 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001959 else:
Serhiy Storchakad741a882015-06-11 00:06:39 +03001960 WINDOWS_SCHEME = {
1961 'purelib': '$base/Lib/site-packages',
1962 'platlib': '$base/Lib/site-packages',
1963 'headers': '$base/Include/$dist_name',
1964 'scripts': '$base/Scripts',
1965 'data' : '$base',
1966 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001967*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001968 scheme = GetScheme(py_major, py_minor);
1969 /* Run the pre-install script. */
1970 if (pre_install_script && *pre_install_script) {
1971 SetDlgItemText (hwnd, IDC_TITLE,
1972 "Running pre-installation script");
1973 run_simple_script(pre_install_script);
1974 }
1975 if (!success) {
1976 break;
1977 }
1978 /* Extract all files from the archive */
1979 SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
1980 if (!unzip_archive (scheme,
1981 python_dir, arc_data,
1982 arc_size, notify))
1983 set_failure_reason("Failed to unzip installation files");
1984 /* Compile the py-files */
1985 if (success && pyc_compile) {
1986 int errors;
1987 HINSTANCE hPython;
1988 SetDlgItemText(hwnd, IDC_TITLE,
1989 "Compiling files to .pyc...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001990
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001991 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1992 hPython = LoadPythonDll(pythondll);
1993 if (hPython) {
1994 errors = compile_filelist(hPython, FALSE);
1995 FreeLibrary(hPython);
1996 }
1997 /* Compilation errors are intentionally ignored:
1998 * Python2.0 contains a bug which will result
1999 * in sys.path containing garbage under certain
2000 * circumstances, and an error message will only
2001 * confuse the user.
2002 */
2003 }
2004 if (success && pyo_compile) {
2005 int errors;
2006 HINSTANCE hPython;
2007 SetDlgItemText(hwnd, IDC_TITLE,
2008 "Compiling files to .pyo...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002009
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002010 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
2011 hPython = LoadPythonDll(pythondll);
2012 if (hPython) {
2013 errors = compile_filelist(hPython, TRUE);
2014 FreeLibrary(hPython);
2015 }
2016 /* Errors ignored: see above */
2017 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002018
2019
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002020 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002021
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002022 case PSN_RESET:
2023 break;
2024
2025 default:
2026 break;
2027 }
2028 }
2029 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002030}
2031
2032
Steve Dower65e4cb12014-11-22 12:54:57 -08002033INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002034FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2035{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002036 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002037
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002038 switch (msg) {
2039 case WM_INITDIALOG:
2040 if (hBitmap)
2041 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2042 IMAGE_BITMAP, (LPARAM)hBitmap);
2043 if (!success)
2044 SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002045
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002046 /* async delay: will show the dialog box completely before
2047 the install_script is started */
2048 PostMessage(hwnd, WM_USER, 0, 0L);
2049 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002050
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002051 case WM_USER:
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002052
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002053 if (success && install_script && install_script[0]) {
2054 char fname[MAX_PATH];
2055 char *buffer;
2056 HCURSOR hCursor;
2057 int result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002058
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002059 char *argv[3] = {NULL, "-install", NULL};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002060
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002061 SetDlgItemText(hwnd, IDC_TITLE,
2062 "Please wait while running postinstall script...");
2063 strcpy(fname, python_dir);
2064 strcat(fname, "\\Scripts\\");
2065 strcat(fname, install_script);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002066
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002067 if (logfile)
2068 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002069
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002070 hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002071
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002072 argv[0] = fname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002073
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002074 result = run_installscript(fname, 2, argv, &buffer);
2075 if (0 != result) {
2076 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
2077 }
2078 if (buffer)
2079 SetDlgItemText(hwnd, IDC_INFO, buffer);
2080 SetDlgItemText(hwnd, IDC_TITLE,
2081 "Postinstall script finished.\n"
2082 "Click the Finish button to exit the Setup wizard.");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002083
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002084 free(buffer);
2085 SetCursor(hCursor);
2086 CloseLogfile();
2087 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002088
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002089 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002090
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002091 case WM_NOTIFY:
2092 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002093
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002094 switch (lpnm->code) {
2095 case PSN_SETACTIVE: /* Enable the Finish button */
2096 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2097 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002098
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002099 case PSN_WIZNEXT:
2100 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002101
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002102 case PSN_WIZFINISH:
2103 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002104
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002105 case PSN_RESET:
2106 break;
2107
2108 default:
2109 break;
2110 }
2111 }
2112 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002113}
2114
2115void RunWizard(HWND hwnd)
2116{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002117 PROPSHEETPAGE psp = {0};
2118 HPROPSHEETPAGE ahpsp[4] = {0};
2119 PROPSHEETHEADER psh = {0};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002120
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002121 /* Display module information */
2122 psp.dwSize = sizeof(psp);
2123 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2124 psp.hInstance = GetModuleHandle (NULL);
2125 psp.lParam = 0;
2126 psp.pfnDlgProc = IntroDlgProc;
2127 psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002128
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002129 ahpsp[0] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002130
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002131 /* Select python version to use */
2132 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2133 psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON);
2134 psp.pfnDlgProc = SelectPythonDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002135
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002136 ahpsp[1] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002137
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002138 /* Install the files */
2139 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2140 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES);
2141 psp.pfnDlgProc = InstallFilesDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002142
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002143 ahpsp[2] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002144
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002145 /* Show success or failure */
2146 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2147 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED);
2148 psp.pfnDlgProc = FinishedDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002149
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002150 ahpsp[3] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002151
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002152 /* Create the property sheet */
2153 psh.dwSize = sizeof(psh);
2154 psh.hInstance = GetModuleHandle(NULL);
2155 psh.hwndParent = hwnd;
2156 psh.phpage = ahpsp;
2157 psh.dwFlags = PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
2158 psh.pszbmWatermark = NULL;
2159 psh.pszbmHeader = NULL;
2160 psh.nStartPage = 0;
2161 psh.nPages = 4;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002162
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002163 PropertySheet(&psh);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002164}
2165
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002166// subtly different from HasLocalMachinePrivs(), in that after executing
2167// an 'elevated' process, we expect this to return TRUE - but there is no
2168// such implication for HasLocalMachinePrivs
2169BOOL MyIsUserAnAdmin()
2170{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002171 typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
2172 static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
2173 HMODULE shell32;
2174 // This function isn't guaranteed to be available (and it can't hurt
2175 // to leave the library loaded)
2176 if (0 == (shell32=LoadLibrary("shell32.dll")))
2177 return FALSE;
2178 if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
2179 return FALSE;
2180 return (*pfnIsUserAnAdmin)();
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002181}
2182
2183// Some magic for Vista's UAC. If there is a target_version, and
2184// if that target version is installed in the registry under
2185// HKLM, and we are not current administrator, then
2186// re-execute ourselves requesting elevation.
2187// Split into 2 functions - "should we elevate" and "spawn elevated"
2188
2189// Returns TRUE if we should spawn an elevated child
2190BOOL NeedAutoUAC()
2191{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002192 HKEY hk;
2193 char key_name[80];
2194 // no Python version info == we can't know yet.
2195 if (target_version[0] == '\0')
2196 return FALSE;
2197 // see how python is current installed
2198 wsprintf(key_name,
2199 "Software\\Python\\PythonCore\\%s\\InstallPath",
2200 target_version);
2201 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2202 key_name, 0, KEY_READ, &hk))
2203 return FALSE;
2204 RegCloseKey(hk);
2205 // Python is installed in HKLM - we must elevate.
2206 return TRUE;
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002207}
2208
2209// Spawn ourself as an elevated application. On failure, a message is
2210// displayed to the user - but this app will always terminate, even
2211// on error.
2212void SpawnUAC()
2213{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002214 // interesting failure scenario that has been seen: initial executable
2215 // runs from a network drive - but once elevated, that network share
2216 // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2217 int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
2218 SW_SHOWNORMAL);
2219 if (ret <= 32) {
2220 char msg[128];
2221 wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
2222 MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
2223 }
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002224}
2225
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002226int DoInstall(void)
2227{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002228 char ini_buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002229
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002230 /* Read installation information */
2231 GetPrivateProfileString("Setup", "title", "", ini_buffer,
2232 sizeof(ini_buffer), ini_file);
2233 unescape(title, ini_buffer, sizeof(title));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002234
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002235 GetPrivateProfileString("Setup", "info", "", ini_buffer,
2236 sizeof(ini_buffer), ini_file);
2237 unescape(info, ini_buffer, sizeof(info));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002238
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002239 GetPrivateProfileString("Setup", "build_info", "", build_info,
2240 sizeof(build_info), ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002241
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002242 pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2243 ini_file);
2244 pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2245 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002246
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002247 GetPrivateProfileString("Setup", "target_version", "",
2248 target_version, sizeof(target_version),
2249 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002250
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002251 GetPrivateProfileString("metadata", "name", "",
2252 meta_name, sizeof(meta_name),
2253 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002254
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002255 GetPrivateProfileString("Setup", "install_script", "",
2256 install_script, sizeof(install_script),
2257 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002258
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002259 GetPrivateProfileString("Setup", "user_access_control", "",
2260 user_access_control, sizeof(user_access_control), ini_file);
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002261
Steve Dower332334f2016-01-16 13:54:53 -08002262 strcat(target_version, REGISTRY_SUFFIX_6432);
2263
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002264 // See if we need to do the Vista UAC magic.
2265 if (strcmp(user_access_control, "force")==0) {
Steve Dower65e4cb12014-11-22 12:54:57 -08002266 if (!MyIsUserAnAdmin()) {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002267 SpawnUAC();
2268 return 0;
2269 }
2270 // already admin - keep going
2271 } else if (strcmp(user_access_control, "auto")==0) {
2272 // Check if it looks like we need UAC control, based
2273 // on how Python itself was installed.
Steve Dower65e4cb12014-11-22 12:54:57 -08002274 if (!MyIsUserAnAdmin() && NeedAutoUAC()) {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002275 SpawnUAC();
2276 return 0;
2277 }
2278 } else {
2279 // display a warning about unknown values - only the developer
2280 // of the extension will see it (until they fix it!)
2281 if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
2282 MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
2283 // nothing to do.
2284 }
2285 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002286
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002287 hwndMain = CreateBackground(title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002288
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002289 RunWizard(hwndMain);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002290
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002291 /* Clean up */
2292 UnmapViewOfFile(arc_data);
2293 if (ini_file)
2294 DeleteFile(ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002295
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002296 if (hBitmap)
2297 DeleteObject(hBitmap);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002298
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002299 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002300}
2301
2302/*********************** uninstall section ******************************/
2303
2304static int compare(const void *p1, const void *p2)
2305{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002306 return strcmp(*(char **)p2, *(char **)p1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002307}
2308
2309/*
2310 * Commit suicide (remove the uninstaller itself).
2311 *
2312 * Create a batch file to first remove the uninstaller
2313 * (will succeed after it has finished), then the batch file itself.
2314 *
2315 * This technique has been demonstrated by Jeff Richter,
2316 * MSJ 1/1996
2317 */
2318void remove_exe(void)
2319{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002320 char exename[_MAX_PATH];
2321 char batname[_MAX_PATH];
2322 FILE *fp;
2323 STARTUPINFO si;
2324 PROCESS_INFORMATION pi;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002325
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002326 GetModuleFileName(NULL, exename, sizeof(exename));
2327 sprintf(batname, "%s.bat", exename);
2328 fp = fopen(batname, "w");
2329 fprintf(fp, ":Repeat\n");
2330 fprintf(fp, "del \"%s\"\n", exename);
2331 fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
2332 fprintf(fp, "del \"%s\"\n", batname);
2333 fclose(fp);
2334
2335 ZeroMemory(&si, sizeof(si));
2336 si.cb = sizeof(si);
2337 si.dwFlags = STARTF_USESHOWWINDOW;
2338 si.wShowWindow = SW_HIDE;
2339 if (CreateProcess(NULL,
2340 batname,
2341 NULL,
2342 NULL,
2343 FALSE,
2344 CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
2345 NULL,
2346 "\\",
2347 &si,
2348 &pi)) {
2349 SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
2350 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2351 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
2352 CloseHandle(pi.hProcess);
2353 ResumeThread(pi.hThread);
2354 CloseHandle(pi.hThread);
2355 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002356}
2357
2358void DeleteRegistryKey(char *string)
2359{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002360 char *keyname;
2361 char *subkeyname;
2362 char *delim;
2363 HKEY hKey;
2364 long result;
2365 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002366
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002367 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002368
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002369 keyname = strchr(line, '[');
2370 if (!keyname)
2371 return;
2372 ++keyname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002373
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002374 subkeyname = strchr(keyname, ']');
2375 if (!subkeyname)
2376 return;
2377 *subkeyname++='\0';
2378 delim = strchr(subkeyname, '\n');
2379 if (delim)
2380 *delim = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002381
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002382 result = RegOpenKeyEx(hkey_root,
2383 keyname,
2384 0,
2385 KEY_WRITE,
2386 &hKey);
2387
2388 if (result != ERROR_SUCCESS)
2389 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2390 else {
2391 result = RegDeleteKey(hKey, subkeyname);
2392 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2393 MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2394 RegCloseKey(hKey);
2395 }
2396 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002397}
2398
2399void DeleteRegistryValue(char *string)
2400{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002401 char *keyname;
2402 char *valuename;
2403 char *value;
2404 HKEY hKey;
2405 long result;
2406 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002407
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002408 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002409
2410/* Format is 'Reg DB Value: [key]name=value' */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002411 keyname = strchr(line, '[');
2412 if (!keyname)
2413 return;
2414 ++keyname;
2415 valuename = strchr(keyname, ']');
2416 if (!valuename)
2417 return;
2418 *valuename++ = '\0';
2419 value = strchr(valuename, '=');
2420 if (!value)
2421 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002422
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002423 *value++ = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002424
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002425 result = RegOpenKeyEx(hkey_root,
2426 keyname,
2427 0,
2428 KEY_WRITE,
2429 &hKey);
2430 if (result != ERROR_SUCCESS)
2431 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2432 else {
2433 result = RegDeleteValue(hKey, valuename);
2434 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2435 MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2436 RegCloseKey(hKey);
2437 }
2438 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002439}
2440
2441BOOL MyDeleteFile(char *line)
2442{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002443 char *pathname = strchr(line, ':');
2444 if (!pathname)
2445 return FALSE;
2446 ++pathname;
2447 while (isspace(*pathname))
2448 ++pathname;
2449 return DeleteFile(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002450}
2451
2452BOOL MyRemoveDirectory(char *line)
2453{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002454 char *pathname = strchr(line, ':');
2455 if (!pathname)
2456 return FALSE;
2457 ++pathname;
2458 while (isspace(*pathname))
2459 ++pathname;
2460 return RemoveDirectory(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002461}
2462
2463BOOL Run_RemoveScript(char *line)
2464{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002465 char *dllname;
2466 char *scriptname;
2467 static char lastscript[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002468
2469/* Format is 'Run Scripts: [pythondll]scriptname' */
2470/* XXX Currently, pythondll carries no path!!! */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002471 dllname = strchr(line, '[');
2472 if (!dllname)
2473 return FALSE;
2474 ++dllname;
2475 scriptname = strchr(dllname, ']');
2476 if (!scriptname)
2477 return FALSE;
2478 *scriptname++ = '\0';
2479 /* this function may be called more than one time with the same
2480 script, only run it one time */
2481 if (strcmp(lastscript, scriptname)) {
2482 char *argv[3] = {NULL, "-remove", NULL};
2483 char *buffer = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002484
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002485 argv[0] = scriptname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002486
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002487 if (0 != run_installscript(scriptname, 2, argv, &buffer))
2488 fprintf(stderr, "*** Could not run installation script ***");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002489
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002490 if (buffer && buffer[0])
2491 MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
2492 free(buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002493
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002494 strcpy(lastscript, scriptname);
2495 }
2496 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002497}
2498
2499int DoUninstall(int argc, char **argv)
2500{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002501 FILE *logfile;
2502 char buffer[4096];
2503 int nLines = 0;
2504 int i;
2505 char *cp;
2506 int nFiles = 0;
2507 int nDirs = 0;
2508 int nErrors = 0;
2509 char **lines;
2510 int lines_buffer_size = 10;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002511
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002512 if (argc != 3) {
2513 MessageBox(NULL,
2514 "Wrong number of args",
2515 NULL,
2516 MB_OK);
2517 return 1; /* Error */
2518 }
2519 if (strcmp(argv[1], "-u")) {
2520 MessageBox(NULL,
2521 "2. arg is not -u",
2522 NULL,
2523 MB_OK);
2524 return 1; /* Error */
2525 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002526
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002527 logfile = fopen(argv[2], "r");
2528 if (!logfile) {
2529 MessageBox(NULL,
2530 "could not open logfile",
2531 NULL,
2532 MB_OK);
2533 return 1; /* Error */
2534 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002535
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002536 lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2537 if (!lines)
2538 return SystemError(0, "Out of memory");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002539
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002540 /* Read the whole logfile, realloacting the buffer */
2541 while (fgets(buffer, sizeof(buffer), logfile)) {
2542 int len = strlen(buffer);
2543 /* remove trailing white space */
2544 while (isspace(buffer[len-1]))
2545 len -= 1;
2546 buffer[len] = '\0';
2547 lines[nLines++] = strdup(buffer);
2548 if (nLines >= lines_buffer_size) {
2549 lines_buffer_size += 10;
2550 lines = (char **)realloc(lines,
2551 sizeof(char *) * lines_buffer_size);
2552 if (!lines)
2553 return SystemError(0, "Out of memory");
2554 }
2555 }
2556 fclose(logfile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002557
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002558 /* Sort all the lines, so that highest 3-digit codes are first */
2559 qsort(&lines[0], nLines, sizeof(char *),
2560 compare);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002561
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002562 if (IDYES != MessageBox(NULL,
2563 "Are you sure you want to remove\n"
2564 "this package from your computer?",
2565 "Please confirm",
2566 MB_YESNO | MB_ICONQUESTION))
2567 return 0;
2568
2569 hkey_root = HKEY_LOCAL_MACHINE;
2570 cp = "";
2571 for (i = 0; i < nLines; ++i) {
2572 /* Ignore duplicate lines */
2573 if (strcmp(cp, lines[i])) {
2574 int ign;
2575 cp = lines[i];
2576 /* Parse the lines */
2577 if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
2578 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
2579 hkey_root = HKEY_CURRENT_USER;
2580 else {
2581 // HKLM - check they have permissions.
2582 if (!HasLocalMachinePrivs()) {
2583 MessageBox(GetFocus(),
2584 "You do not seem to have sufficient access rights\n"
2585 "on this machine to uninstall this software",
2586 NULL,
2587 MB_OK | MB_ICONSTOP);
2588 return 1; /* Error */
2589 }
2590 }
2591 } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
2592 if (MyRemoveDirectory(cp))
2593 ++nDirs;
2594 else {
2595 int code = GetLastError();
2596 if (code != 2 && code != 3) { /* file or path not found */
2597 ++nErrors;
2598 }
2599 }
2600 } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
2601 if (MyDeleteFile(cp))
2602 ++nFiles;
2603 else {
2604 int code = GetLastError();
2605 if (code != 2 && code != 3) { /* file or path not found */
2606 ++nErrors;
2607 }
2608 }
2609 } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
2610 if (MyDeleteFile(cp))
2611 ++nFiles;
2612 else {
2613 int code = GetLastError();
2614 if (code != 2 && code != 3) { /* file or path not found */
2615 ++nErrors;
2616 }
2617 }
2618 } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
2619 DeleteRegistryKey(cp);
2620 } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
2621 DeleteRegistryValue(cp);
2622 } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
2623 Run_RemoveScript(cp);
2624 }
2625 }
2626 }
2627
2628 if (DeleteFile(argv[2])) {
2629 ++nFiles;
2630 } else {
2631 ++nErrors;
2632 SystemError(GetLastError(), argv[2]);
2633 }
2634 if (nErrors)
2635 wsprintf(buffer,
2636 "%d files and %d directories removed\n"
2637 "%d files or directories could not be removed",
2638 nFiles, nDirs, nErrors);
2639 else
2640 wsprintf(buffer, "%d files and %d directories removed",
2641 nFiles, nDirs);
2642 MessageBox(NULL, buffer, "Uninstall Finished!",
2643 MB_OK | MB_ICONINFORMATION);
2644 remove_exe();
2645 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002646}
2647
2648int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002649 LPSTR lpszCmdLine, INT nCmdShow)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002650{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002651 extern int __argc;
2652 extern char **__argv;
2653 char *basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002654
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002655 GetModuleFileName(NULL, modulename, sizeof(modulename));
2656 GetModuleFileNameW(NULL, wmodulename, sizeof(wmodulename)/sizeof(wmodulename[0]));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002657
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002658 /* Map the executable file to memory */
2659 arc_data = MapExistingFile(modulename, &arc_size);
2660 if (!arc_data) {
2661 SystemError(GetLastError(), "Could not open archive");
2662 return 1;
2663 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002664
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002665 /* OK. So this program can act as installer (self-extracting
2666 * zip-file, or as uninstaller when started with '-u logfile'
2667 * command line flags.
2668 *
2669 * The installer is usually started without command line flags,
2670 * and the uninstaller is usually started with the '-u logfile'
2671 * flag. What to do if some innocent user double-clicks the
2672 * exe-file?
2673 * The following implements a defensive strategy...
2674 */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002675
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002676 /* Try to extract the configuration data into a temporary file */
2677 if (ExtractInstallData(arc_data, arc_size, &exe_size,
2678 &ini_file, &pre_install_script))
2679 return DoInstall();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002680
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002681 if (!ini_file && __argc > 1) {
2682 return DoUninstall(__argc, __argv);
2683 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002684
2685
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002686 basename = strrchr(modulename, '\\');
2687 if (basename)
2688 ++basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002689
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002690 /* Last guess about the purpose of this program */
2691 if (basename && (0 == strncmp(basename, "Remove", 6)))
2692 SystemError(0, "This program is normally started by windows");
2693 else
2694 SystemError(0, "Setup program invalid or damaged");
2695 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002696}