blob: f39b3819dc088596c571d19aef46e65c14c17e5e [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
156
157#define WM_NUMFILES WM_USER+1
158/* wParam: 0, lParam: total number of files */
159#define WM_NEXTFILE WM_USER+2
160/* wParam: number of this file */
161/* lParam: points to pathname */
162
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000163static BOOL notify(int code, char *fmt, ...);
164
165/* Note: If scheme.prefix is nonempty, it must end with a '\'! */
166/* Note: purelib must be the FIRST entry! */
167SCHEME old_scheme[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000168 { "PURELIB", "" },
169 { "PLATLIB", "" },
170 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
171 { "SCRIPTS", "Scripts\\" },
172 { "DATA", "" },
173 { NULL, NULL },
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000174};
175
176SCHEME new_scheme[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000177 { "PURELIB", "Lib\\site-packages\\" },
178 { "PLATLIB", "Lib\\site-packages\\" },
179 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
180 { "SCRIPTS", "Scripts\\" },
181 { "DATA", "" },
182 { NULL, NULL },
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000183};
184
185static void unescape(char *dst, char *src, unsigned size)
186{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000187 char *eon;
188 char ch;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000189
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000190 while (src && *src && (size > 2)) {
191 if (*src == '\\') {
192 switch (*++src) {
193 case 'n':
194 ++src;
195 *dst++ = '\r';
196 *dst++ = '\n';
197 size -= 2;
198 break;
199 case 'r':
200 ++src;
201 *dst++ = '\r';
202 --size;
203 break;
204 case '0': case '1': case '2': case '3':
205 ch = (char)strtol(src, &eon, 8);
206 if (ch == '\n') {
207 *dst++ = '\r';
208 --size;
209 }
210 *dst++ = ch;
211 --size;
212 src = eon;
213 }
214 } else {
215 *dst++ = *src++;
216 --size;
217 }
218 }
219 *dst = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000220}
221
222static struct tagFile {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000223 char *path;
224 struct tagFile *next;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000225} *file_list = NULL;
226
Thomas Hellera19cdad2004-02-20 14:43:21 +0000227static void set_failure_reason(char *reason)
228{
229 if (failure_reason)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000230 free(failure_reason);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000231 failure_reason = strdup(reason);
232 success = FALSE;
233}
234static char *get_failure_reason()
235{
236 if (!failure_reason)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000237 return "Installation failed.";
Thomas Hellera19cdad2004-02-20 14:43:21 +0000238 return failure_reason;
239}
240
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000241static void add_to_filelist(char *path)
242{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000243 struct tagFile *p;
244 p = (struct tagFile *)malloc(sizeof(struct tagFile));
245 p->path = strdup(path);
246 p->next = file_list;
247 file_list = p;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000248}
249
250static int do_compile_files(int (__cdecl * PyRun_SimpleString)(char *),
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000251 int optimize)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000252{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000253 struct tagFile *p;
254 int total, n;
255 char Buffer[MAX_PATH + 64];
256 int errors = 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000257
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000258 total = 0;
259 p = file_list;
260 while (p) {
261 ++total;
262 p = p->next;
263 }
264 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETRANGE, 0,
265 MAKELPARAM(0, total));
266 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, 0, 0);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000267
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000268 n = 0;
269 p = file_list;
270 while (p) {
271 ++n;
272 wsprintf(Buffer,
273 "import py_compile; py_compile.compile (r'%s')",
274 p->path);
275 if (PyRun_SimpleString(Buffer)) {
276 ++errors;
277 }
278 /* We send the notification even if the files could not
279 * be created so that the uninstaller will remove them
280 * in case they are created later.
281 */
282 wsprintf(Buffer, "%s%c", p->path, optimize ? 'o' : 'c');
283 notify(FILE_CREATED, Buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000284
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000285 SendDlgItemMessage(hDialog, IDC_PROGRESS, PBM_SETPOS, n, 0);
286 SetDlgItemText(hDialog, IDC_INFO, p->path);
287 p = p->next;
288 }
289 return errors;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000290}
291
292#define DECLPROC(dll, result, name, args)\
293 typedef result (*__PROC__##name) args;\
294 result (*name)args = (__PROC__##name)GetProcAddress(dll, #name)
295
296
297#define DECLVAR(dll, type, name)\
298 type *name = (type*)GetProcAddress(dll, #name)
299
300typedef void PyObject;
301
Mark Hammond891f2632009-01-29 13:08:01 +0000302// Convert a "char *" string to "whcar_t *", or NULL on error.
303// Result string must be free'd
304wchar_t *widen_string(char *src)
305{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000306 wchar_t *result;
307 DWORD dest_cch;
308 int src_len = strlen(src) + 1; // include NULL term in all ops
309 /* use MultiByteToWideChar() to see how much we need. */
310 /* NOTE: this will include the null-term in the length */
311 dest_cch = MultiByteToWideChar(CP_ACP, 0, src, src_len, NULL, 0);
312 // alloc the buffer
313 result = (wchar_t *)malloc(dest_cch * sizeof(wchar_t));
314 if (result==NULL)
315 return NULL;
316 /* do the conversion */
317 if (0==MultiByteToWideChar(CP_ACP, 0, src, src_len, result, dest_cch)) {
318 free(result);
319 return NULL;
320 }
321 return result;
Mark Hammond891f2632009-01-29 13:08:01 +0000322}
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000323
324/*
325 * Returns number of files which failed to compile,
326 * -1 if python could not be loaded at all
327 */
328static int compile_filelist(HINSTANCE hPython, BOOL optimize_flag)
329{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000330 DECLPROC(hPython, void, Py_Initialize, (void));
331 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
332 DECLPROC(hPython, void, Py_Finalize, (void));
333 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
334 DECLPROC(hPython, PyObject *, PySys_GetObject, (char *));
335 DECLVAR(hPython, int, Py_OptimizeFlag);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000336
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000337 int errors = 0;
338 struct tagFile *p = file_list;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000339
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000340 if (!p)
341 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000342
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000343 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize)
344 return -1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000345
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000346 if (!PyRun_SimpleString || !PySys_GetObject || !Py_OptimizeFlag)
347 return -1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000348
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000349 *Py_OptimizeFlag = optimize_flag ? 1 : 0;
350 Py_SetProgramName(wmodulename);
351 Py_Initialize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000352
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000353 errors += do_compile_files(PyRun_SimpleString, optimize_flag);
354 Py_Finalize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000355
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000356 return errors;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000357}
358
359typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
360
361struct PyMethodDef {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000362 char *ml_name;
363 PyCFunction ml_meth;
364 int ml_flags;
365 char *ml_doc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000366};
367typedef struct PyMethodDef PyMethodDef;
368
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000369// XXX - all of these are potentially fragile! We load and unload
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000370// the Python DLL multiple times - so storing functions pointers
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000371// is dangerous (although things *look* OK at present)
372// Better might be to roll prepare_script_environment() into
373// LoadPythonDll(), and create a new UnloadPythonDLL() which also
374// clears the global pointers.
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000375void *(*g_Py_BuildValue)(char *, ...);
376int (*g_PyArg_ParseTuple)(PyObject *, char *, ...);
Christian Heimes81ee3ef2008-05-04 22:42:01 +0000377PyObject * (*g_PyLong_FromVoidPtr)(void *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000378
379PyObject *g_PyExc_ValueError;
380PyObject *g_PyExc_OSError;
381
382PyObject *(*g_PyErr_Format)(PyObject *, char *, ...);
383
384#define DEF_CSIDL(name) { name, #name }
385
386struct {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000387 int nFolder;
388 char *name;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000389} csidl_names[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000390 /* Startup menu for all users.
391 NT only */
392 DEF_CSIDL(CSIDL_COMMON_STARTMENU),
393 /* Startup menu. */
394 DEF_CSIDL(CSIDL_STARTMENU),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000395
396/* DEF_CSIDL(CSIDL_COMMON_APPDATA), */
397/* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000398 /* Repository for application-specific data.
399 Needs Internet Explorer 4.0 */
400 DEF_CSIDL(CSIDL_APPDATA),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000401
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000402 /* The desktop for all users.
403 NT only */
404 DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY),
405 /* The desktop. */
406 DEF_CSIDL(CSIDL_DESKTOPDIRECTORY),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000407
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000408 /* Startup folder for all users.
409 NT only */
410 DEF_CSIDL(CSIDL_COMMON_STARTUP),
411 /* Startup folder. */
412 DEF_CSIDL(CSIDL_STARTUP),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000413
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000414 /* Programs item in the start menu for all users.
415 NT only */
416 DEF_CSIDL(CSIDL_COMMON_PROGRAMS),
417 /* Program item in the user's start menu. */
418 DEF_CSIDL(CSIDL_PROGRAMS),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000419
420/* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
421/* DEF_CSIDL(CSIDL_PROGRAM_FILES), */
422
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000423 /* Virtual folder containing fonts. */
424 DEF_CSIDL(CSIDL_FONTS),
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000425};
426
427#define DIM(a) (sizeof(a) / sizeof((a)[0]))
428
429static PyObject *FileCreated(PyObject *self, PyObject *args)
430{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000431 char *path;
432 if (!g_PyArg_ParseTuple(args, "s", &path))
433 return NULL;
434 notify(FILE_CREATED, path);
435 return g_Py_BuildValue("");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000436}
437
438static PyObject *DirectoryCreated(PyObject *self, PyObject *args)
439{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000440 char *path;
441 if (!g_PyArg_ParseTuple(args, "s", &path))
442 return NULL;
443 notify(DIR_CREATED, path);
444 return g_Py_BuildValue("");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000445}
446
447static PyObject *GetSpecialFolderPath(PyObject *self, PyObject *args)
448{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000449 char *name;
450 char lpszPath[MAX_PATH];
451 int i;
452 static HRESULT (WINAPI *My_SHGetSpecialFolderPath)(HWND hwnd,
453 LPTSTR lpszPath,
454 int nFolder,
455 BOOL fCreate);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000456
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000457 if (!My_SHGetSpecialFolderPath) {
458 HINSTANCE hLib = LoadLibrary("shell32.dll");
459 if (!hLib) {
460 g_PyErr_Format(g_PyExc_OSError,
461 "function not available");
462 return NULL;
463 }
464 My_SHGetSpecialFolderPath = (BOOL (WINAPI *)(HWND, LPTSTR,
465 int, BOOL))
466 GetProcAddress(hLib,
467 "SHGetSpecialFolderPathA");
468 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000469
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000470 if (!g_PyArg_ParseTuple(args, "s", &name))
471 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000472
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000473 if (!My_SHGetSpecialFolderPath) {
474 g_PyErr_Format(g_PyExc_OSError, "function not available");
475 return NULL;
476 }
477
478 for (i = 0; i < DIM(csidl_names); ++i) {
479 if (0 == strcmpi(csidl_names[i].name, name)) {
480 int nFolder;
481 nFolder = csidl_names[i].nFolder;
482 if (My_SHGetSpecialFolderPath(NULL, lpszPath,
483 nFolder, 0))
484 return g_Py_BuildValue("s", lpszPath);
485 else {
486 g_PyErr_Format(g_PyExc_OSError,
487 "no such folder (%s)", name);
488 return NULL;
489 }
490
491 }
492 };
493 g_PyErr_Format(g_PyExc_ValueError, "unknown CSIDL (%s)", name);
494 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000495}
496
497static PyObject *CreateShortcut(PyObject *self, PyObject *args)
498{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000499 char *path; /* path and filename */
500 char *description;
501 char *filename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000502
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000503 char *arguments = NULL;
504 char *iconpath = NULL;
505 int iconindex = 0;
506 char *workdir = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000507
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000508 WCHAR wszFilename[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000509
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000510 IShellLink *ps1 = NULL;
511 IPersistFile *pPf = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000512
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000513 HRESULT hr;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000514
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000515 hr = CoInitialize(NULL);
516 if (FAILED(hr)) {
517 g_PyErr_Format(g_PyExc_OSError,
518 "CoInitialize failed, error 0x%x", hr);
519 goto error;
520 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000521
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000522 if (!g_PyArg_ParseTuple(args, "sss|sssi",
523 &path, &description, &filename,
524 &arguments, &workdir, &iconpath, &iconindex))
525 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000526
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000527 hr = CoCreateInstance(&CLSID_ShellLink,
528 NULL,
529 CLSCTX_INPROC_SERVER,
530 &IID_IShellLink,
531 &ps1);
532 if (FAILED(hr)) {
533 g_PyErr_Format(g_PyExc_OSError,
534 "CoCreateInstance failed, error 0x%x", hr);
535 goto error;
536 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000537
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000538 hr = ps1->lpVtbl->QueryInterface(ps1, &IID_IPersistFile,
539 (void **)&pPf);
540 if (FAILED(hr)) {
541 g_PyErr_Format(g_PyExc_OSError,
542 "QueryInterface(IPersistFile) error 0x%x", hr);
543 goto error;
544 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000545
546
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000547 hr = ps1->lpVtbl->SetPath(ps1, path);
548 if (FAILED(hr)) {
549 g_PyErr_Format(g_PyExc_OSError,
550 "SetPath() failed, error 0x%x", hr);
551 goto error;
552 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000553
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000554 hr = ps1->lpVtbl->SetDescription(ps1, description);
555 if (FAILED(hr)) {
556 g_PyErr_Format(g_PyExc_OSError,
557 "SetDescription() 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 if (arguments) {
562 hr = ps1->lpVtbl->SetArguments(ps1, arguments);
563 if (FAILED(hr)) {
564 g_PyErr_Format(g_PyExc_OSError,
565 "SetArguments() error 0x%x", hr);
566 goto error;
567 }
568 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000569
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000570 if (iconpath) {
571 hr = ps1->lpVtbl->SetIconLocation(ps1, iconpath, iconindex);
572 if (FAILED(hr)) {
573 g_PyErr_Format(g_PyExc_OSError,
574 "SetIconLocation() error 0x%x", hr);
575 goto error;
576 }
577 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000578
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000579 if (workdir) {
580 hr = ps1->lpVtbl->SetWorkingDirectory(ps1, workdir);
581 if (FAILED(hr)) {
582 g_PyErr_Format(g_PyExc_OSError,
583 "SetWorkingDirectory() error 0x%x", hr);
584 goto error;
585 }
586 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000587
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000588 MultiByteToWideChar(CP_ACP, 0,
589 filename, -1,
590 wszFilename, MAX_PATH);
591
592 hr = pPf->lpVtbl->Save(pPf, wszFilename, TRUE);
593 if (FAILED(hr)) {
594 g_PyErr_Format(g_PyExc_OSError,
595 "Failed to create shortcut '%s' - error 0x%x", filename, hr);
596 goto error;
597 }
598
599 pPf->lpVtbl->Release(pPf);
600 ps1->lpVtbl->Release(ps1);
601 CoUninitialize();
602 return g_Py_BuildValue("");
603
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000604 error:
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000605 if (pPf)
606 pPf->lpVtbl->Release(pPf);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000607
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000608 if (ps1)
609 ps1->lpVtbl->Release(ps1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000610
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000611 CoUninitialize();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000612
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000613 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000614}
615
Thomas Hellera19cdad2004-02-20 14:43:21 +0000616static PyObject *PyMessageBox(PyObject *self, PyObject *args)
617{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000618 int rc;
619 char *text, *caption;
620 int flags;
621 if (!g_PyArg_ParseTuple(args, "ssi", &text, &caption, &flags))
622 return NULL;
623 rc = MessageBox(GetFocus(), text, caption, flags);
624 return g_Py_BuildValue("i", rc);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000625}
626
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000627static PyObject *GetRootHKey(PyObject *self)
628{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000629 return g_PyLong_FromVoidPtr(hkey_root);
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000630}
631
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000632#define METH_VARARGS 0x0001
Mark Hammondf9bfdd82004-07-02 23:53:16 +0000633#define METH_NOARGS 0x0004
634typedef PyObject *(*PyCFunction)(PyObject *, PyObject *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000635
636PyMethodDef meth[] = {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000637 {"create_shortcut", CreateShortcut, METH_VARARGS, NULL},
638 {"get_special_folder_path", GetSpecialFolderPath, METH_VARARGS, NULL},
639 {"get_root_hkey", (PyCFunction)GetRootHKey, METH_NOARGS, NULL},
640 {"file_created", FileCreated, METH_VARARGS, NULL},
641 {"directory_created", DirectoryCreated, METH_VARARGS, NULL},
642 {"message_box", PyMessageBox, METH_VARARGS, NULL},
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000643};
644
Thomas Heller48340392004-06-18 17:03:38 +0000645static HINSTANCE LoadPythonDll(char *fname)
646{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000647 char fullpath[_MAX_PATH];
648 LONG size = sizeof(fullpath);
649 char subkey_name[80];
650 char buffer[260 + 12];
651 HINSTANCE h;
Thomas Heller8abe7bf2005-02-03 20:11:28 +0000652
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000653 /* make sure PYTHONHOME is set, to that sys.path is initialized correctly */
654 wsprintf(buffer, "PYTHONHOME=%s", python_dir);
655 _putenv(buffer);
656 h = LoadLibrary(fname);
657 if (h)
658 return h;
659 wsprintf(subkey_name,
660 "SOFTWARE\\Python\\PythonCore\\%d.%d\\InstallPath",
661 py_major, py_minor);
662 if (ERROR_SUCCESS != RegQueryValue(HKEY_CURRENT_USER, subkey_name,
663 fullpath, &size) &&
664 ERROR_SUCCESS != RegQueryValue(HKEY_LOCAL_MACHINE, subkey_name,
665 fullpath, &size))
666 return NULL;
667 strcat(fullpath, "\\");
668 strcat(fullpath, fname);
669 return LoadLibrary(fullpath);
Thomas Heller48340392004-06-18 17:03:38 +0000670}
671
Thomas Hellera19cdad2004-02-20 14:43:21 +0000672static int prepare_script_environment(HINSTANCE hPython)
673{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000674 PyObject *mod;
675 DECLPROC(hPython, PyObject *, PyImport_ImportModule, (char *));
676 DECLPROC(hPython, int, PyObject_SetAttrString, (PyObject *, char *, PyObject *));
677 DECLPROC(hPython, PyObject *, PyObject_GetAttrString, (PyObject *, char *));
678 DECLPROC(hPython, PyObject *, PyCFunction_New, (PyMethodDef *, PyObject *));
679 DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
680 DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
681 DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
682 DECLPROC(hPython, PyObject *, PyLong_FromVoidPtr, (void *));
683 if (!PyImport_ImportModule || !PyObject_GetAttrString ||
684 !PyObject_SetAttrString || !PyCFunction_New)
685 return 1;
686 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
687 return 1;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000688
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000689 mod = PyImport_ImportModule("builtins");
690 if (mod) {
691 int i;
692 g_PyExc_ValueError = PyObject_GetAttrString(mod, "ValueError");
693 g_PyExc_OSError = PyObject_GetAttrString(mod, "OSError");
694 for (i = 0; i < DIM(meth); ++i) {
695 PyObject_SetAttrString(mod, meth[i].ml_name,
696 PyCFunction_New(&meth[i], NULL));
697 }
698 }
699 g_Py_BuildValue = Py_BuildValue;
700 g_PyArg_ParseTuple = PyArg_ParseTuple;
701 g_PyErr_Format = PyErr_Format;
702 g_PyLong_FromVoidPtr = PyLong_FromVoidPtr;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000703
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000704 return 0;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000705}
706
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000707/*
708 * This function returns one of the following error codes:
709 * 1 if the Python-dll does not export the functions we need
710 * 2 if no install-script is specified in pathname
711 * 3 if the install-script file could not be opened
Thomas Heller9f2e3be2005-02-03 20:35:10 +0000712 * the return value of PyRun_SimpleString() otherwise,
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000713 * which is 0 if everything is ok, -1 if an exception had occurred
714 * in the install-script.
715 */
716
717static int
Mark Hammond6d0e9752009-01-29 12:36:50 +0000718do_run_installscript(HINSTANCE hPython, char *pathname, int argc, char **argv)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000719{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000720 int fh, result, i;
721 static wchar_t *wargv[256];
722 DECLPROC(hPython, void, Py_Initialize, (void));
723 DECLPROC(hPython, int, PySys_SetArgv, (int, wchar_t **));
724 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
725 DECLPROC(hPython, void, Py_Finalize, (void));
726 DECLPROC(hPython, PyObject *, Py_BuildValue, (char *, ...));
727 DECLPROC(hPython, PyObject *, PyCFunction_New,
728 (PyMethodDef *, PyObject *));
729 DECLPROC(hPython, int, PyArg_ParseTuple, (PyObject *, char *, ...));
730 DECLPROC(hPython, PyObject *, PyErr_Format, (PyObject *, char *));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000731
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000732 if (!Py_Initialize || !PySys_SetArgv
733 || !PyRun_SimpleString || !Py_Finalize)
734 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000735
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000736 if (!Py_BuildValue || !PyArg_ParseTuple || !PyErr_Format)
737 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000738
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000739 if (!PyCFunction_New || !PyArg_ParseTuple || !PyErr_Format)
740 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000741
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000742 if (pathname == NULL || pathname[0] == '\0')
743 return 2;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000744
Victor Stinnerdaf45552013-08-28 00:53:59 +0200745 fh = open(pathname, _O_RDONLY | O_NOINHERIT);
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000746 if (-1 == fh) {
747 fprintf(stderr, "Could not open postinstall-script %s\n",
748 pathname);
749 return 3;
750 }
Mark Hammond6d0e9752009-01-29 12:36:50 +0000751
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000752 SetDlgItemText(hDialog, IDC_INFO, "Running Script...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000753
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000754 Py_Initialize();
Mark Hammond891f2632009-01-29 13:08:01 +0000755
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000756 prepare_script_environment(hPython);
757 // widen the argv array for py3k.
758 memset(wargv, 0, sizeof(wargv));
759 for (i=0;i<argc;i++)
760 wargv[i] = argv[i] ? widen_string(argv[i]) : NULL;
761 PySys_SetArgv(argc, wargv);
762 // free the strings we just widened.
763 for (i=0;i<argc;i++)
764 if (wargv[i])
765 free(wargv[i]);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000766
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000767 result = 3;
768 {
769 struct _stat statbuf;
770 if(0 == _fstat(fh, &statbuf)) {
771 char *script = alloca(statbuf.st_size + 5);
772 int n = read(fh, script, statbuf.st_size);
773 if (n > 0) {
774 script[n] = '\n';
775 script[n+1] = 0;
776 result = PyRun_SimpleString(script);
777 }
778 }
779 }
780 Py_Finalize();
781
782 close(fh);
783 return result;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000784}
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000785
Mark Hammond6d0e9752009-01-29 12:36:50 +0000786static int
787run_installscript(char *pathname, int argc, char **argv, char **pOutput)
788{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000789 HINSTANCE hPython;
790 int result = 1;
791 int out_buf_size;
792 HANDLE redirected, old_stderr, old_stdout;
793 char *tempname;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000794
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000795 *pOutput = NULL;
Mark Hammond6d0e9752009-01-29 12:36:50 +0000796
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000797 tempname = tempnam(NULL, NULL);
798 // We use a static CRT while the Python version we load uses
Ezio Melotti13925002011-03-16 11:05:33 +0200799 // the CRT from one of various possible DLLs. As a result we
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000800 // need to redirect the standard handles using the API rather
801 // than the CRT.
802 redirected = CreateFile(
803 tempname,
804 GENERIC_WRITE | GENERIC_READ,
805 FILE_SHARE_READ,
806 NULL,
807 CREATE_ALWAYS,
808 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
809 NULL);
810 old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
811 old_stderr = GetStdHandle(STD_ERROR_HANDLE);
812 SetStdHandle(STD_OUTPUT_HANDLE, redirected);
813 SetStdHandle(STD_ERROR_HANDLE, redirected);
Mark Hammond6d0e9752009-01-29 12:36:50 +0000814
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000815 hPython = LoadPythonDll(pythondll);
816 if (hPython) {
817 result = do_run_installscript(hPython, pathname, argc, argv);
818 FreeLibrary(hPython);
819 } else {
820 fprintf(stderr, "*** Could not load Python ***");
821 }
822 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
823 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
824 out_buf_size = min(GetFileSize(redirected, NULL), 4096);
825 *pOutput = malloc(out_buf_size+1);
826 if (*pOutput) {
827 DWORD nread = 0;
828 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
829 ReadFile(redirected, *pOutput, out_buf_size, &nread, NULL);
830 (*pOutput)[nread] = '\0';
831 }
832 CloseHandle(redirected);
833 DeleteFile(tempname);
834 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000835}
836
Thomas Hellera19cdad2004-02-20 14:43:21 +0000837static int do_run_simple_script(HINSTANCE hPython, char *script)
838{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000839 int rc;
840 DECLPROC(hPython, void, Py_Initialize, (void));
841 DECLPROC(hPython, void, Py_SetProgramName, (wchar_t *));
842 DECLPROC(hPython, void, Py_Finalize, (void));
843 DECLPROC(hPython, int, PyRun_SimpleString, (char *));
844 DECLPROC(hPython, void, PyErr_Print, (void));
Thomas Hellera19cdad2004-02-20 14:43:21 +0000845
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000846 if (!Py_Initialize || !Py_SetProgramName || !Py_Finalize ||
847 !PyRun_SimpleString || !PyErr_Print)
848 return -1;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000849
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000850 Py_SetProgramName(wmodulename);
851 Py_Initialize();
852 prepare_script_environment(hPython);
853 rc = PyRun_SimpleString(script);
854 if (rc)
855 PyErr_Print();
856 Py_Finalize();
857 return rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000858}
859
860static int run_simple_script(char *script)
861{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000862 int rc;
863 HINSTANCE hPython;
864 char *tempname = tempnam(NULL, NULL);
865 // Redirect output using win32 API - see comments above...
866 HANDLE redirected = CreateFile(
867 tempname,
868 GENERIC_WRITE | GENERIC_READ,
869 FILE_SHARE_READ,
870 NULL,
871 CREATE_ALWAYS,
872 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH,
873 NULL);
874 HANDLE old_stdout = GetStdHandle(STD_OUTPUT_HANDLE);
875 HANDLE old_stderr = GetStdHandle(STD_ERROR_HANDLE);
876 SetStdHandle(STD_OUTPUT_HANDLE, redirected);
877 SetStdHandle(STD_ERROR_HANDLE, redirected);
Thomas Hellera19cdad2004-02-20 14:43:21 +0000878
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000879 hPython = LoadPythonDll(pythondll);
880 if (!hPython) {
881 char reason[128];
882 wsprintf(reason, "Can't load Python for pre-install script (%d)", GetLastError());
883 set_failure_reason(reason);
884 return -1;
885 }
886 rc = do_run_simple_script(hPython, script);
887 FreeLibrary(hPython);
888 SetStdHandle(STD_OUTPUT_HANDLE, old_stdout);
889 SetStdHandle(STD_ERROR_HANDLE, old_stderr);
890 /* We only care about the output when we fail. If the script works
891 OK, then we discard it
892 */
893 if (rc) {
894 int err_buf_size;
895 char *err_buf;
896 const char *prefix = "Running the pre-installation script failed\r\n";
897 int prefix_len = strlen(prefix);
898 err_buf_size = GetFileSize(redirected, NULL);
899 if (err_buf_size==INVALID_FILE_SIZE) // an error - let's try anyway...
900 err_buf_size = 4096;
901 err_buf = malloc(prefix_len + err_buf_size + 1);
902 if (err_buf) {
903 DWORD n = 0;
904 strcpy(err_buf, prefix);
905 SetFilePointer(redirected, 0, 0, FILE_BEGIN);
906 ReadFile(redirected, err_buf+prefix_len, err_buf_size, &n, NULL);
907 err_buf[prefix_len+n] = '\0';
908 set_failure_reason(err_buf);
909 free(err_buf);
910 } else {
911 set_failure_reason("Out of memory!");
912 }
913 }
914 CloseHandle(redirected);
915 DeleteFile(tempname);
916 return rc;
Thomas Hellera19cdad2004-02-20 14:43:21 +0000917}
918
919
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000920static BOOL SystemError(int error, char *msg)
921{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000922 char Buffer[1024];
923 int n;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000924
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000925 if (error) {
926 LPVOID lpMsgBuf;
927 FormatMessage(
928 FORMAT_MESSAGE_ALLOCATE_BUFFER |
929 FORMAT_MESSAGE_FROM_SYSTEM,
930 NULL,
931 error,
932 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
933 (LPSTR)&lpMsgBuf,
934 0,
935 NULL
936 );
937 strncpy(Buffer, lpMsgBuf, sizeof(Buffer));
938 LocalFree(lpMsgBuf);
939 } else
940 Buffer[0] = '\0';
941 n = lstrlen(Buffer);
942 _snprintf(Buffer+n, sizeof(Buffer)-n, msg);
943 MessageBox(hwndMain, Buffer, "Runtime Error", MB_OK | MB_ICONSTOP);
944 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000945}
946
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000947static BOOL notify (int code, char *fmt, ...)
948{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000949 char Buffer[1024];
950 va_list marker;
951 BOOL result = TRUE;
952 int a, b;
953 char *cp;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000954
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000955 va_start(marker, fmt);
956 _vsnprintf(Buffer, sizeof(Buffer), fmt, marker);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000957
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000958 switch (code) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000959/* Questions */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000960 case CAN_OVERWRITE:
961 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000962
963/* Information notification */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000964 case DIR_CREATED:
965 if (logfile)
966 fprintf(logfile, "100 Made Dir: %s\n", fmt);
967 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000968
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000969 case FILE_CREATED:
970 if (logfile)
971 fprintf(logfile, "200 File Copy: %s\n", fmt);
972 goto add_to_filelist_label;
973 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000974
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000975 case FILE_OVERWRITTEN:
976 if (logfile)
977 fprintf(logfile, "200 File Overwrite: %s\n", fmt);
978 add_to_filelist_label:
979 if ((cp = strrchr(fmt, '.')) && (0 == strcmp (cp, ".py")))
980 add_to_filelist(fmt);
981 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000982
983/* Error Messages */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000984 case ZLIB_ERROR:
985 MessageBox(GetFocus(), Buffer, "Error",
986 MB_OK | MB_ICONWARNING);
987 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000988
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000989 case SYSTEM_ERROR:
990 SystemError(GetLastError(), Buffer);
991 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +0000992
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +0000993 case NUM_FILES:
994 a = va_arg(marker, int);
995 b = va_arg(marker, int);
996 SendMessage(hDialog, WM_NUMFILES, 0, MAKELPARAM(0, a));
997 SendMessage(hDialog, WM_NEXTFILE, b,(LPARAM)fmt);
998 }
999 va_end(marker);
1000
1001 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001002}
1003
1004static char *MapExistingFile(char *pathname, DWORD *psize)
1005{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001006 HANDLE hFile, hFileMapping;
1007 DWORD nSizeLow, nSizeHigh;
1008 char *data;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001009
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001010 hFile = CreateFile(pathname,
1011 GENERIC_READ, FILE_SHARE_READ, NULL,
1012 OPEN_EXISTING,
1013 FILE_ATTRIBUTE_NORMAL, NULL);
1014 if (hFile == INVALID_HANDLE_VALUE)
1015 return NULL;
1016 nSizeLow = GetFileSize(hFile, &nSizeHigh);
1017 hFileMapping = CreateFileMapping(hFile,
1018 NULL, PAGE_READONLY, 0, 0, NULL);
1019 CloseHandle(hFile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001020
Gregory P. Smithb803c6c2013-03-23 16:05:36 -07001021 if (hFileMapping == NULL)
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001022 return NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001023
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001024 data = MapViewOfFile(hFileMapping,
1025 FILE_MAP_READ, 0, 0, 0);
1026
1027 CloseHandle(hFileMapping);
1028 *psize = nSizeLow;
1029 return data;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001030}
1031
1032
1033static void create_bitmap(HWND hwnd)
1034{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001035 BITMAPFILEHEADER *bfh;
1036 BITMAPINFO *bi;
1037 HDC hdc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001038
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001039 if (!bitmap_bytes)
1040 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001041
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001042 if (hBitmap)
1043 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001044
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001045 hdc = GetDC(hwnd);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001046
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001047 bfh = (BITMAPFILEHEADER *)bitmap_bytes;
1048 bi = (BITMAPINFO *)(bitmap_bytes + sizeof(BITMAPFILEHEADER));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001049
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001050 hBitmap = CreateDIBitmap(hdc,
1051 &bi->bmiHeader,
1052 CBM_INIT,
1053 bitmap_bytes + bfh->bfOffBits,
1054 bi,
1055 DIB_RGB_COLORS);
1056 ReleaseDC(hwnd, hdc);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001057}
1058
Thomas Hellera19cdad2004-02-20 14:43:21 +00001059/* Extract everything we need to begin the installation. Currently this
1060 is the INI filename with install data, and the raw pre-install script
1061*/
1062static BOOL ExtractInstallData(char *data, DWORD size, int *pexe_size,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001063 char **out_ini_file, char **out_preinstall_script)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001064{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001065 /* read the end of central directory record */
1066 struct eof_cdir *pe = (struct eof_cdir *)&data[size - sizeof
1067 (struct eof_cdir)];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001068
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001069 int arc_start = size - sizeof (struct eof_cdir) - pe->nBytesCDir -
1070 pe->ofsCDir;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001071
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001072 int ofs = arc_start - sizeof (struct meta_data_hdr);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001073
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001074 /* read meta_data info */
1075 struct meta_data_hdr *pmd = (struct meta_data_hdr *)&data[ofs];
1076 char *src, *dst;
1077 char *ini_file;
1078 char tempdir[MAX_PATH];
Thomas Hellera19cdad2004-02-20 14:43:21 +00001079
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001080 /* ensure that if we fail, we don't have garbage out pointers */
1081 *out_ini_file = *out_preinstall_script = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001082
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001083 if (pe->tag != 0x06054b50) {
1084 return FALSE;
1085 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001086
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001087 if (pmd->tag != 0x1234567B) {
1088 return SystemError(0,
1089 "Invalid cfgdata magic number (see bdist_wininst.py)");
1090 }
1091 if (ofs < 0) {
1092 return FALSE;
1093 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001094
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001095 if (pmd->bitmap_size) {
1096 /* Store pointer to bitmap bytes */
1097 bitmap_bytes = (char *)pmd - pmd->uncomp_size - pmd->bitmap_size;
1098 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001099
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001100 *pexe_size = ofs - pmd->uncomp_size - pmd->bitmap_size;
1101
1102 src = ((char *)pmd) - pmd->uncomp_size;
1103 ini_file = malloc(MAX_PATH); /* will be returned, so do not free it */
1104 if (!ini_file)
1105 return FALSE;
1106 if (!GetTempPath(sizeof(tempdir), tempdir)
1107 || !GetTempFileName(tempdir, "~du", 0, ini_file)) {
1108 SystemError(GetLastError(),
1109 "Could not create temporary file");
1110 return FALSE;
1111 }
1112
1113 dst = map_new_file(CREATE_ALWAYS, ini_file, NULL, pmd->uncomp_size,
1114 0, 0, NULL/*notify*/);
1115 if (!dst)
1116 return FALSE;
1117 /* Up to the first \0 is the INI file data. */
1118 strncpy(dst, src, pmd->uncomp_size);
1119 src += strlen(dst) + 1;
1120 /* Up to next \0 is the pre-install script */
1121 *out_preinstall_script = strdup(src);
1122 *out_ini_file = ini_file;
1123 UnmapViewOfFile(dst);
1124 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001125}
1126
1127static void PumpMessages(void)
1128{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001129 MSG msg;
1130 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
1131 TranslateMessage(&msg);
1132 DispatchMessage(&msg);
1133 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001134}
1135
1136LRESULT CALLBACK
1137WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1138{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001139 HDC hdc;
1140 HFONT hFont;
1141 int h;
1142 PAINTSTRUCT ps;
1143 switch (msg) {
1144 case WM_PAINT:
1145 hdc = BeginPaint(hwnd, &ps);
1146 h = GetSystemMetrics(SM_CYSCREEN) / 10;
1147 hFont = CreateFont(h, 0, 0, 0, 700, TRUE,
1148 0, 0, 0, 0, 0, 0, 0, "Times Roman");
1149 hFont = SelectObject(hdc, hFont);
1150 SetBkMode(hdc, TRANSPARENT);
1151 TextOut(hdc, 15, 15, title, strlen(title));
1152 SetTextColor(hdc, RGB(255, 255, 255));
1153 TextOut(hdc, 10, 10, title, strlen(title));
1154 DeleteObject(SelectObject(hdc, hFont));
1155 EndPaint(hwnd, &ps);
1156 return 0;
1157 }
1158 return DefWindowProc(hwnd, msg, wParam, lParam);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001159}
1160
1161static HWND CreateBackground(char *title)
1162{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001163 WNDCLASS wc;
1164 HWND hwnd;
1165 char buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001166
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001167 wc.style = CS_VREDRAW | CS_HREDRAW;
1168 wc.lpfnWndProc = WindowProc;
1169 wc.cbWndExtra = 0;
1170 wc.cbClsExtra = 0;
1171 wc.hInstance = GetModuleHandle(NULL);
1172 wc.hIcon = NULL;
1173 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
1174 wc.hbrBackground = CreateSolidBrush(RGB(0, 0, 128));
1175 wc.lpszMenuName = NULL;
1176 wc.lpszClassName = "SetupWindowClass";
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001177
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001178 if (!RegisterClass(&wc))
1179 MessageBox(hwndMain,
1180 "Could not register window class",
1181 "Setup.exe", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001182
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001183 wsprintf(buffer, "Setup %s", title);
1184 hwnd = CreateWindow("SetupWindowClass",
1185 buffer,
1186 0,
1187 0, 0,
1188 GetSystemMetrics(SM_CXFULLSCREEN),
1189 GetSystemMetrics(SM_CYFULLSCREEN),
1190 NULL,
1191 NULL,
1192 GetModuleHandle(NULL),
1193 NULL);
1194 ShowWindow(hwnd, SW_SHOWMAXIMIZED);
1195 UpdateWindow(hwnd);
1196 return hwnd;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001197}
1198
1199/*
1200 * Center a window on the screen
1201 */
1202static void CenterWindow(HWND hwnd)
1203{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001204 RECT rc;
1205 int w, h;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001206
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001207 GetWindowRect(hwnd, &rc);
1208 w = GetSystemMetrics(SM_CXSCREEN);
1209 h = GetSystemMetrics(SM_CYSCREEN);
1210 MoveWindow(hwnd,
1211 (w - (rc.right-rc.left))/2,
1212 (h - (rc.bottom-rc.top))/2,
1213 rc.right-rc.left, rc.bottom-rc.top, FALSE);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001214}
1215
1216#include <prsht.h>
1217
Steve Dower65e4cb12014-11-22 12:54:57 -08001218INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001219IntroDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1220{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001221 LPNMHDR lpnm;
1222 char Buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001223
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001224 switch (msg) {
1225 case WM_INITDIALOG:
1226 create_bitmap(hwnd);
1227 if(hBitmap)
1228 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1229 IMAGE_BITMAP, (LPARAM)hBitmap);
1230 CenterWindow(GetParent(hwnd));
1231 wsprintf(Buffer,
1232 "This Wizard will install %s on your computer. "
1233 "Click Next to continue "
1234 "or Cancel to exit the Setup Wizard.",
1235 meta_name);
1236 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1237 SetDlgItemText(hwnd, IDC_INTRO_TEXT, info);
1238 SetDlgItemText(hwnd, IDC_BUILD_INFO, build_info);
1239 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001240
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001241 case WM_NOTIFY:
1242 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001243
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001244 switch (lpnm->code) {
1245 case PSN_SETACTIVE:
1246 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_NEXT);
1247 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001248
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001249 case PSN_WIZNEXT:
1250 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001251
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001252 case PSN_RESET:
1253 break;
1254
1255 default:
1256 break;
1257 }
1258 }
1259 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001260}
1261
1262#ifdef USE_OTHER_PYTHON_VERSIONS
1263/* These are really private variables used to communicate
1264 * between StatusRoutine and CheckPythonExe
1265 */
1266char bound_image_dll[_MAX_PATH];
1267int bound_image_major;
1268int bound_image_minor;
1269
1270static BOOL __stdcall StatusRoutine(IMAGEHLP_STATUS_REASON reason,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001271 PSTR ImageName,
1272 PSTR DllName,
1273 ULONG Va,
1274 ULONG Parameter)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001275{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001276 char fname[_MAX_PATH];
1277 int int_version;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001278
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001279 switch(reason) {
1280 case BindOutOfMemory:
1281 case BindRvaToVaFailed:
1282 case BindNoRoomInImage:
1283 case BindImportProcedureFailed:
1284 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001285
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001286 case BindImportProcedure:
1287 case BindForwarder:
1288 case BindForwarderNOT:
1289 case BindImageModified:
1290 case BindExpandFileHeaders:
1291 case BindImageComplete:
1292 case BindSymbolsNotUpdated:
1293 case BindMismatchedSymbols:
1294 case BindImportModuleFailed:
1295 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001296
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001297 case BindImportModule:
1298 if (1 == sscanf(DllName, "python%d", &int_version)) {
1299 SearchPath(NULL, DllName, NULL, sizeof(fname),
1300 fname, NULL);
1301 strcpy(bound_image_dll, fname);
1302 bound_image_major = int_version / 10;
1303 bound_image_minor = int_version % 10;
1304 OutputDebugString("BOUND ");
1305 OutputDebugString(fname);
1306 OutputDebugString("\n");
1307 }
1308 break;
1309 }
1310 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001311}
1312
1313/*
1314 */
1315static LPSTR get_sys_prefix(LPSTR exe, LPSTR dll)
1316{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001317 void (__cdecl * Py_Initialize)(void);
1318 void (__cdecl * Py_SetProgramName)(char *);
1319 void (__cdecl * Py_Finalize)(void);
1320 void* (__cdecl * PySys_GetObject)(char *);
1321 void (__cdecl * PySys_SetArgv)(int, char **);
1322 char* (__cdecl * Py_GetPrefix)(void);
1323 char* (__cdecl * Py_GetPath)(void);
1324 HINSTANCE hPython;
1325 LPSTR prefix = NULL;
1326 int (__cdecl * PyRun_SimpleString)(char *);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001327
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001328 {
1329 char Buffer[256];
1330 wsprintf(Buffer, "PYTHONHOME=%s", exe);
1331 *strrchr(Buffer, '\\') = '\0';
1332// MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
1333 _putenv(Buffer);
1334 _putenv("PYTHONPATH=");
1335 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001336
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001337 hPython = LoadLibrary(dll);
1338 if (!hPython)
1339 return NULL;
1340 Py_Initialize = (void (*)(void))GetProcAddress
1341 (hPython,"Py_Initialize");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001342
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001343 PySys_SetArgv = (void (*)(int, char **))GetProcAddress
1344 (hPython,"PySys_SetArgv");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001345
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001346 PyRun_SimpleString = (int (*)(char *))GetProcAddress
1347 (hPython,"PyRun_SimpleString");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001348
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001349 Py_SetProgramName = (void (*)(char *))GetProcAddress
1350 (hPython,"Py_SetProgramName");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001351
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001352 PySys_GetObject = (void* (*)(char *))GetProcAddress
1353 (hPython,"PySys_GetObject");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001354
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001355 Py_GetPrefix = (char * (*)(void))GetProcAddress
1356 (hPython,"Py_GetPrefix");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001357
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001358 Py_GetPath = (char * (*)(void))GetProcAddress
1359 (hPython,"Py_GetPath");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001360
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001361 Py_Finalize = (void (*)(void))GetProcAddress(hPython,
1362 "Py_Finalize");
1363 Py_SetProgramName(exe);
1364 Py_Initialize();
1365 PySys_SetArgv(1, &exe);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001366
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001367 MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK);
1368 MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001369
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001370 Py_Finalize();
1371 FreeLibrary(hPython);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001372
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001373 return prefix;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001374}
1375
1376static BOOL
1377CheckPythonExe(LPSTR pathname, LPSTR version, int *pmajor, int *pminor)
1378{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001379 bound_image_dll[0] = '\0';
1380 if (!BindImageEx(BIND_NO_BOUND_IMPORTS | BIND_NO_UPDATE | BIND_ALL_IMAGES,
1381 pathname,
1382 NULL,
1383 NULL,
1384 StatusRoutine))
1385 return SystemError(0, "Could not bind image");
1386 if (bound_image_dll[0] == '\0')
1387 return SystemError(0, "Does not seem to be a python executable");
1388 *pmajor = bound_image_major;
1389 *pminor = bound_image_minor;
1390 if (version && *version) {
1391 char core_version[12];
1392 wsprintf(core_version, "%d.%d", bound_image_major, bound_image_minor);
1393 if (strcmp(version, core_version))
1394 return SystemError(0, "Wrong Python version");
1395 }
1396 get_sys_prefix(pathname, bound_image_dll);
1397 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001398}
1399
1400/*
1401 * Browse for other python versions. Insert it into the listbox specified
1402 * by hwnd. version, if not NULL or empty, is the version required.
1403 */
1404static BOOL GetOtherPythonVersion(HWND hwnd, LPSTR version)
1405{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001406 char vers_name[_MAX_PATH + 80];
1407 DWORD itemindex;
1408 OPENFILENAME of;
1409 char pathname[_MAX_PATH];
1410 DWORD result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001411
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001412 strcpy(pathname, "python.exe");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001413
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001414 memset(&of, 0, sizeof(of));
1415 of.lStructSize = sizeof(OPENFILENAME);
1416 of.hwndOwner = GetParent(hwnd);
1417 of.hInstance = NULL;
1418 of.lpstrFilter = "python.exe\0python.exe\0";
1419 of.lpstrCustomFilter = NULL;
1420 of.nMaxCustFilter = 0;
1421 of.nFilterIndex = 1;
1422 of.lpstrFile = pathname;
1423 of.nMaxFile = sizeof(pathname);
1424 of.lpstrFileTitle = NULL;
1425 of.nMaxFileTitle = 0;
1426 of.lpstrInitialDir = NULL;
1427 of.lpstrTitle = "Python executable";
1428 of.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST;
1429 of.lpstrDefExt = "exe";
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001430
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001431 result = GetOpenFileName(&of);
1432 if (result) {
1433 int major, minor;
1434 if (!CheckPythonExe(pathname, version, &major, &minor)) {
1435 return FALSE;
1436 }
1437 *strrchr(pathname, '\\') = '\0';
1438 wsprintf(vers_name, "Python Version %d.%d in %s",
1439 major, minor, pathname);
1440 itemindex = SendMessage(hwnd, LB_INSERTSTRING, -1,
1441 (LPARAM)(LPSTR)vers_name);
1442 SendMessage(hwnd, LB_SETCURSEL, itemindex, 0);
1443 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1444 (LPARAM)(LPSTR)strdup(pathname));
1445 return TRUE;
1446 }
1447 return FALSE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001448}
1449#endif /* USE_OTHER_PYTHON_VERSIONS */
1450
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001451typedef struct _InstalledVersionInfo {
1452 char prefix[MAX_PATH+1]; // sys.prefix directory.
1453 HKEY hkey; // Is this Python in HKCU or HKLM?
1454} InstalledVersionInfo;
1455
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001456
1457/*
1458 * Fill the listbox specified by hwnd with all python versions found
1459 * in the registry. version, if not NULL or empty, is the version
1460 * required.
1461 */
1462static BOOL GetPythonVersions(HWND hwnd, HKEY hkRoot, LPSTR version)
1463{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001464 DWORD index = 0;
1465 char core_version[80];
1466 HKEY hKey;
1467 BOOL result = TRUE;
1468 DWORD bufsize;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001469
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001470 if (ERROR_SUCCESS != RegOpenKeyEx(hkRoot,
1471 "Software\\Python\\PythonCore",
1472 0, KEY_READ, &hKey))
1473 return FALSE;
1474 bufsize = sizeof(core_version);
1475 while (ERROR_SUCCESS == RegEnumKeyEx(hKey, index,
1476 core_version, &bufsize, NULL,
1477 NULL, NULL, NULL)) {
1478 char subkey_name[80], vers_name[80];
1479 int itemindex;
1480 DWORD value_size;
1481 HKEY hk;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001482
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001483 bufsize = sizeof(core_version);
1484 ++index;
1485 if (version && *version && strcmp(version, core_version))
1486 continue;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001487
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001488 wsprintf(vers_name, "Python Version %s (found in registry)",
1489 core_version);
1490 wsprintf(subkey_name,
1491 "Software\\Python\\PythonCore\\%s\\InstallPath",
1492 core_version);
1493 if (ERROR_SUCCESS == RegOpenKeyEx(hkRoot, subkey_name, 0, KEY_READ, &hk)) {
1494 InstalledVersionInfo *ivi =
1495 (InstalledVersionInfo *)malloc(sizeof(InstalledVersionInfo));
1496 value_size = sizeof(ivi->prefix);
1497 if (ivi &&
1498 ERROR_SUCCESS == RegQueryValueEx(hk, NULL, NULL, NULL,
1499 ivi->prefix, &value_size)) {
1500 itemindex = SendMessage(hwnd, LB_ADDSTRING, 0,
1501 (LPARAM)(LPSTR)vers_name);
1502 ivi->hkey = hkRoot;
1503 SendMessage(hwnd, LB_SETITEMDATA, itemindex,
1504 (LPARAM)(LPSTR)ivi);
1505 }
1506 RegCloseKey(hk);
1507 }
1508 }
1509 RegCloseKey(hKey);
1510 return result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001511}
1512
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001513/* Determine if the current user can write to HKEY_LOCAL_MACHINE */
1514BOOL HasLocalMachinePrivs()
1515{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001516 HKEY hKey;
1517 DWORD result;
1518 static char KeyName[] =
1519 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001520
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001521 result = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
1522 KeyName,
1523 0,
1524 KEY_CREATE_SUB_KEY,
1525 &hKey);
1526 if (result==0)
1527 RegCloseKey(hKey);
1528 return result==0;
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001529}
1530
1531// Check the root registry key to use - either HKLM or HKCU.
1532// If Python is installed in HKCU, then our extension also must be installed
1533// in HKCU - as Python won't be available for other users, we shouldn't either
1534// (and will fail if we are!)
1535// If Python is installed in HKLM, then we will also prefer to use HKLM, but
1536// this may not be possible - so we silently fall back to HKCU.
1537//
1538// We assume hkey_root is already set to where Python itself is installed.
1539void CheckRootKey(HWND hwnd)
1540{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001541 if (hkey_root==HKEY_CURRENT_USER) {
1542 ; // as above, always install ourself in HKCU too.
1543 } else if (hkey_root==HKEY_LOCAL_MACHINE) {
1544 // Python in HKLM, but we may or may not have permissions there.
1545 // Open the uninstall key with 'create' permissions - if this fails,
1546 // we don't have permission.
1547 if (!HasLocalMachinePrivs())
1548 hkey_root = HKEY_CURRENT_USER;
1549 } else {
1550 MessageBox(hwnd, "Don't know Python's installation type",
1551 "Strange", MB_OK | MB_ICONSTOP);
1552 /* Default to wherever they can, but preferring HKLM */
1553 hkey_root = HasLocalMachinePrivs() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
1554 }
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001555}
1556
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001557/* Return the installation scheme depending on Python version number */
1558SCHEME *GetScheme(int major, int minor)
1559{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001560 if (major > 2)
1561 return new_scheme;
1562 else if((major == 2) && (minor >= 2))
1563 return new_scheme;
1564 return old_scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001565}
1566
Steve Dower65e4cb12014-11-22 12:54:57 -08001567INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001568SelectPythonDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1569{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001570 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001571
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001572 switch (msg) {
1573 case WM_INITDIALOG:
1574 if (hBitmap)
1575 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1576 IMAGE_BITMAP, (LPARAM)hBitmap);
1577 GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1578 HKEY_LOCAL_MACHINE, target_version);
1579 GetPythonVersions(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1580 HKEY_CURRENT_USER, target_version);
1581 { /* select the last entry which is the highest python
1582 version found */
1583 int count;
1584 count = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1585 LB_GETCOUNT, 0, 0);
1586 if (count && count != LB_ERR)
1587 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST, LB_SETCURSEL,
1588 count-1, 0);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001589
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001590 /* If a specific Python version is required,
1591 * display a prominent notice showing this fact.
1592 */
1593 if (target_version && target_version[0]) {
1594 char buffer[4096];
1595 wsprintf(buffer,
1596 "Python %s is required for this package. "
1597 "Select installation to use:",
1598 target_version);
1599 SetDlgItemText(hwnd, IDC_TITLE, buffer);
1600 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001601
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001602 if (count == 0) {
1603 char Buffer[4096];
1604 char *msg;
1605 if (target_version && target_version[0]) {
1606 wsprintf(Buffer,
1607 "Python version %s required, which was not found"
1608 " in the registry.", target_version);
1609 msg = Buffer;
1610 } else
1611 msg = "No Python installation found in the registry.";
1612 MessageBox(hwnd, msg, "Cannot install",
1613 MB_OK | MB_ICONSTOP);
1614 }
1615 }
1616 goto UpdateInstallDir;
1617 break;
1618
1619 case WM_COMMAND:
1620 switch (LOWORD(wParam)) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001621/*
1622 case IDC_OTHERPYTHON:
1623 if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1624 target_version))
1625 goto UpdateInstallDir;
1626 break;
1627*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001628 case IDC_VERSIONS_LIST:
1629 switch (HIWORD(wParam)) {
1630 int id;
1631 case LBN_SELCHANGE:
1632 UpdateInstallDir:
1633 PropSheet_SetWizButtons(GetParent(hwnd),
1634 PSWIZB_BACK | PSWIZB_NEXT);
1635 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1636 LB_GETCURSEL, 0, 0);
1637 if (id == LB_ERR) {
1638 PropSheet_SetWizButtons(GetParent(hwnd),
1639 PSWIZB_BACK);
1640 SetDlgItemText(hwnd, IDC_PATH, "");
1641 SetDlgItemText(hwnd, IDC_INSTALL_PATH, "");
1642 strcpy(python_dir, "");
1643 strcpy(pythondll, "");
1644 } else {
1645 char *pbuf;
1646 int result;
1647 InstalledVersionInfo *ivi;
1648 PropSheet_SetWizButtons(GetParent(hwnd),
1649 PSWIZB_BACK | PSWIZB_NEXT);
1650 /* Get the python directory */
1651 ivi = (InstalledVersionInfo *)
1652 SendDlgItemMessage(hwnd,
1653 IDC_VERSIONS_LIST,
1654 LB_GETITEMDATA,
1655 id,
1656 0);
1657 hkey_root = ivi->hkey;
1658 strcpy(python_dir, ivi->prefix);
1659 SetDlgItemText(hwnd, IDC_PATH, python_dir);
1660 /* retrieve the python version and pythondll to use */
1661 result = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1662 LB_GETTEXTLEN, (WPARAM)id, 0);
1663 pbuf = (char *)malloc(result + 1);
1664 if (pbuf) {
1665 /* guess the name of the python-dll */
1666 SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1667 LB_GETTEXT, (WPARAM)id,
1668 (LPARAM)pbuf);
1669 result = sscanf(pbuf, "Python Version %d.%d",
1670 &py_major, &py_minor);
1671 if (result == 2) {
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001672#ifdef _DEBUG
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001673 wsprintf(pythondll, "python%d%d_d.dll",
1674 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001675#else
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001676 wsprintf(pythondll, "python%d%d.dll",
1677 py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001678#endif
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001679 }
1680 free(pbuf);
1681 } else
1682 strcpy(pythondll, "");
1683 /* retrieve the scheme for this version */
1684 {
1685 char install_path[_MAX_PATH];
1686 SCHEME *scheme = GetScheme(py_major, py_minor);
1687 strcpy(install_path, python_dir);
1688 if (install_path[strlen(install_path)-1] != '\\')
1689 strcat(install_path, "\\");
1690 strcat(install_path, scheme[0].prefix);
1691 SetDlgItemText(hwnd, IDC_INSTALL_PATH, install_path);
1692 }
1693 }
1694 }
1695 break;
1696 }
1697 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001698
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001699 case WM_NOTIFY:
1700 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001701
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001702 switch (lpnm->code) {
1703 int id;
1704 case PSN_SETACTIVE:
1705 id = SendDlgItemMessage(hwnd, IDC_VERSIONS_LIST,
1706 LB_GETCURSEL, 0, 0);
1707 if (id == LB_ERR)
1708 PropSheet_SetWizButtons(GetParent(hwnd),
1709 PSWIZB_BACK);
1710 else
1711 PropSheet_SetWizButtons(GetParent(hwnd),
1712 PSWIZB_BACK | PSWIZB_NEXT);
1713 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001714
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001715 case PSN_WIZNEXT:
1716 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001717
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001718 case PSN_WIZFINISH:
1719 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001720
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001721 case PSN_RESET:
1722 break;
1723
1724 default:
1725 break;
1726 }
1727 }
1728 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001729}
1730
1731static BOOL OpenLogfile(char *dir)
1732{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001733 char buffer[_MAX_PATH+1];
1734 time_t ltime;
1735 struct tm *now;
1736 long result;
1737 HKEY hKey, hSubkey;
1738 char subkey_name[256];
1739 static char KeyName[] =
1740 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1741 const char *root_name = (hkey_root==HKEY_LOCAL_MACHINE ?
1742 "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER");
1743 DWORD disposition;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001744
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001745 /* Use Create, as the Uninstall subkey may not exist under HKCU.
1746 Use CreateKeyEx, so we can specify a SAM specifying write access
1747 */
1748 result = RegCreateKeyEx(hkey_root,
1749 KeyName,
1750 0, /* reserved */
1751 NULL, /* class */
1752 0, /* options */
1753 KEY_CREATE_SUB_KEY, /* sam */
1754 NULL, /* security */
1755 &hKey, /* result key */
1756 NULL); /* disposition */
1757 if (result != ERROR_SUCCESS) {
1758 if (result == ERROR_ACCESS_DENIED) {
1759 /* This should no longer be able to happen - we have already
1760 checked if they have permissions in HKLM, and all users
1761 should have write access to HKCU.
1762 */
1763 MessageBox(GetFocus(),
1764 "You do not seem to have sufficient access rights\n"
1765 "on this machine to install this software",
1766 NULL,
1767 MB_OK | MB_ICONSTOP);
1768 return FALSE;
1769 } else {
1770 MessageBox(GetFocus(), KeyName, "Could not open key", MB_OK);
1771 }
1772 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001773
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001774 sprintf(buffer, "%s\\%s-wininst.log", dir, meta_name);
1775 logfile = fopen(buffer, "a");
Benjamin Peterson25c7d3f2014-11-27 20:39:02 -06001776 if (!logfile) {
1777 char error[1024];
1778
1779 sprintf(error, "Can't create \"%s\" (%s).\n\n"
1780 "Try to execute the installer as administrator.",
1781 buffer, strerror(errno));
1782 MessageBox(GetFocus(), error, NULL, MB_OK | MB_ICONSTOP);
1783 return FALSE;
1784 }
1785
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001786 time(&ltime);
1787 now = localtime(&ltime);
1788 strftime(buffer, sizeof(buffer),
1789 "*** Installation started %Y/%m/%d %H:%M ***\n",
1790 localtime(&ltime));
1791 fprintf(logfile, buffer);
1792 fprintf(logfile, "Source: %s\n", modulename);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001793
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001794 /* Root key must be first entry processed by uninstaller. */
1795 fprintf(logfile, "999 Root Key: %s\n", root_name);
Mark Hammondf9bfdd82004-07-02 23:53:16 +00001796
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001797 sprintf(subkey_name, "%s-py%d.%d", meta_name, py_major, py_minor);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001798
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001799 result = RegCreateKeyEx(hKey, subkey_name,
1800 0, NULL, 0,
1801 KEY_WRITE,
1802 NULL,
1803 &hSubkey,
1804 &disposition);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001805
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001806 if (result != ERROR_SUCCESS)
1807 MessageBox(GetFocus(), subkey_name, "Could not create key", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001808
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001809 RegCloseKey(hKey);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001810
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001811 if (disposition == REG_CREATED_NEW_KEY)
1812 fprintf(logfile, "020 Reg DB Key: [%s]%s\n", KeyName, subkey_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001813
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001814 sprintf(buffer, "Python %d.%d %s", py_major, py_minor, title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001815
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001816 result = RegSetValueEx(hSubkey, "DisplayName",
1817 0,
1818 REG_SZ,
1819 buffer,
1820 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001821
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001822 if (result != ERROR_SUCCESS)
1823 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001824
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001825 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1826 KeyName, subkey_name, "DisplayName", buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001827
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001828 {
1829 FILE *fp;
1830 sprintf(buffer, "%s\\Remove%s.exe", dir, meta_name);
1831 fp = fopen(buffer, "wb");
1832 fwrite(arc_data, exe_size, 1, fp);
1833 fclose(fp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001834
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001835 sprintf(buffer, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1836 dir, meta_name, dir, meta_name);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001837
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001838 result = RegSetValueEx(hSubkey, "UninstallString",
1839 0,
1840 REG_SZ,
1841 buffer,
1842 strlen(buffer)+1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001843
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001844 if (result != ERROR_SUCCESS)
1845 MessageBox(GetFocus(), buffer, "Could not set key value", MB_OK);
1846
1847 fprintf(logfile, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1848 KeyName, subkey_name, "UninstallString", buffer);
1849 }
1850 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001851}
1852
1853static void CloseLogfile(void)
1854{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001855 char buffer[_MAX_PATH+1];
1856 time_t ltime;
1857 struct tm *now;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001858
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001859 time(&ltime);
1860 now = localtime(&ltime);
1861 strftime(buffer, sizeof(buffer),
1862 "*** Installation finished %Y/%m/%d %H:%M ***\n",
1863 localtime(&ltime));
1864 fprintf(logfile, buffer);
1865 if (logfile)
1866 fclose(logfile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001867}
1868
Steve Dower65e4cb12014-11-22 12:54:57 -08001869INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001870InstallFilesDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
1871{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001872 LPNMHDR lpnm;
1873 char Buffer[4096];
1874 SCHEME *scheme;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001875
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001876 switch (msg) {
1877 case WM_INITDIALOG:
1878 if (hBitmap)
1879 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
1880 IMAGE_BITMAP, (LPARAM)hBitmap);
1881 wsprintf(Buffer,
1882 "Click Next to begin the installation of %s. "
1883 "If you want to review or change any of your "
1884 " installation settings, click Back. "
1885 "Click Cancel to exit the wizard.",
1886 meta_name);
1887 SetDlgItemText(hwnd, IDC_TITLE, Buffer);
1888 SetDlgItemText(hwnd, IDC_INFO, "Ready to install");
1889 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001890
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001891 case WM_NUMFILES:
1892 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETRANGE, 0, lParam);
1893 PumpMessages();
1894 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001895
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001896 case WM_NEXTFILE:
1897 SendDlgItemMessage(hwnd, IDC_PROGRESS, PBM_SETPOS, wParam,
1898 0);
1899 SetDlgItemText(hwnd, IDC_INFO, (LPSTR)lParam);
1900 PumpMessages();
1901 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001902
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001903 case WM_NOTIFY:
1904 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001905
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001906 switch (lpnm->code) {
1907 case PSN_SETACTIVE:
1908 PropSheet_SetWizButtons(GetParent(hwnd),
1909 PSWIZB_BACK | PSWIZB_NEXT);
1910 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001911
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001912 case PSN_WIZFINISH:
1913 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001914
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001915 case PSN_WIZNEXT:
1916 /* Handle a Next button click here */
1917 hDialog = hwnd;
1918 success = TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001919
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001920 /* Disable the buttons while we work. Sending CANCELTOCLOSE has
1921 the effect of disabling the cancel button, which is a) as we
1922 do everything synchronously we can't cancel, and b) the next
1923 step is 'finished', when it is too late to cancel anyway.
1924 The next step being 'Finished' means we also don't need to
1925 restore the button state back */
1926 PropSheet_SetWizButtons(GetParent(hwnd), 0);
1927 SendMessage(GetParent(hwnd), PSM_CANCELTOCLOSE, 0, 0);
1928 /* Make sure the installation directory name ends in a */
1929 /* backslash */
1930 if (python_dir[strlen(python_dir)-1] != '\\')
1931 strcat(python_dir, "\\");
1932 /* Strip the trailing backslash again */
1933 python_dir[strlen(python_dir)-1] = '\0';
1934
1935 CheckRootKey(hwnd);
1936
1937 if (!OpenLogfile(python_dir))
1938 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001939
1940/*
1941 * The scheme we have to use depends on the Python version...
1942 if sys.version < "2.2":
Serhiy Storchakad741a882015-06-11 00:06:39 +03001943 WINDOWS_SCHEME = {
1944 'purelib': '$base',
1945 'platlib': '$base',
1946 'headers': '$base/Include/$dist_name',
1947 'scripts': '$base/Scripts',
1948 'data' : '$base',
1949 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001950 else:
Serhiy Storchakad741a882015-06-11 00:06:39 +03001951 WINDOWS_SCHEME = {
1952 'purelib': '$base/Lib/site-packages',
1953 'platlib': '$base/Lib/site-packages',
1954 'headers': '$base/Include/$dist_name',
1955 'scripts': '$base/Scripts',
1956 'data' : '$base',
1957 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001958*/
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001959 scheme = GetScheme(py_major, py_minor);
1960 /* Run the pre-install script. */
1961 if (pre_install_script && *pre_install_script) {
1962 SetDlgItemText (hwnd, IDC_TITLE,
1963 "Running pre-installation script");
1964 run_simple_script(pre_install_script);
1965 }
1966 if (!success) {
1967 break;
1968 }
1969 /* Extract all files from the archive */
1970 SetDlgItemText(hwnd, IDC_TITLE, "Installing files...");
1971 if (!unzip_archive (scheme,
1972 python_dir, arc_data,
1973 arc_size, notify))
1974 set_failure_reason("Failed to unzip installation files");
1975 /* Compile the py-files */
1976 if (success && pyc_compile) {
1977 int errors;
1978 HINSTANCE hPython;
1979 SetDlgItemText(hwnd, IDC_TITLE,
1980 "Compiling files to .pyc...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00001981
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00001982 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
1983 hPython = LoadPythonDll(pythondll);
1984 if (hPython) {
1985 errors = compile_filelist(hPython, FALSE);
1986 FreeLibrary(hPython);
1987 }
1988 /* Compilation errors are intentionally ignored:
1989 * Python2.0 contains a bug which will result
1990 * in sys.path containing garbage under certain
1991 * circumstances, and an error message will only
1992 * confuse the user.
1993 */
1994 }
1995 if (success && pyo_compile) {
1996 int errors;
1997 HINSTANCE hPython;
1998 SetDlgItemText(hwnd, IDC_TITLE,
1999 "Compiling files to .pyo...");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002000
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002001 SetDlgItemText(hDialog, IDC_INFO, "Loading python...");
2002 hPython = LoadPythonDll(pythondll);
2003 if (hPython) {
2004 errors = compile_filelist(hPython, TRUE);
2005 FreeLibrary(hPython);
2006 }
2007 /* Errors ignored: see above */
2008 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002009
2010
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002011 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002012
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002013 case PSN_RESET:
2014 break;
2015
2016 default:
2017 break;
2018 }
2019 }
2020 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002021}
2022
2023
Steve Dower65e4cb12014-11-22 12:54:57 -08002024INT_PTR CALLBACK
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002025FinishedDlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
2026{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002027 LPNMHDR lpnm;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002028
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002029 switch (msg) {
2030 case WM_INITDIALOG:
2031 if (hBitmap)
2032 SendDlgItemMessage(hwnd, IDC_BITMAP, STM_SETIMAGE,
2033 IMAGE_BITMAP, (LPARAM)hBitmap);
2034 if (!success)
2035 SetDlgItemText(hwnd, IDC_INFO, get_failure_reason());
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002036
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002037 /* async delay: will show the dialog box completely before
2038 the install_script is started */
2039 PostMessage(hwnd, WM_USER, 0, 0L);
2040 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002041
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002042 case WM_USER:
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002043
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002044 if (success && install_script && install_script[0]) {
2045 char fname[MAX_PATH];
2046 char *buffer;
2047 HCURSOR hCursor;
2048 int result;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002049
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002050 char *argv[3] = {NULL, "-install", NULL};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002051
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002052 SetDlgItemText(hwnd, IDC_TITLE,
2053 "Please wait while running postinstall script...");
2054 strcpy(fname, python_dir);
2055 strcat(fname, "\\Scripts\\");
2056 strcat(fname, install_script);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002057
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002058 if (logfile)
2059 fprintf(logfile, "300 Run Script: [%s]%s\n", pythondll, fname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002060
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002061 hCursor = SetCursor(LoadCursor(NULL, IDC_WAIT));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002062
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002063 argv[0] = fname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002064
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002065 result = run_installscript(fname, 2, argv, &buffer);
2066 if (0 != result) {
2067 fprintf(stderr, "*** run_installscript: internal error 0x%X ***\n", result);
2068 }
2069 if (buffer)
2070 SetDlgItemText(hwnd, IDC_INFO, buffer);
2071 SetDlgItemText(hwnd, IDC_TITLE,
2072 "Postinstall script finished.\n"
2073 "Click the Finish button to exit the Setup wizard.");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002074
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002075 free(buffer);
2076 SetCursor(hCursor);
2077 CloseLogfile();
2078 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002079
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002080 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002081
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002082 case WM_NOTIFY:
2083 lpnm = (LPNMHDR) lParam;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002084
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002085 switch (lpnm->code) {
2086 case PSN_SETACTIVE: /* Enable the Finish button */
2087 PropSheet_SetWizButtons(GetParent(hwnd), PSWIZB_FINISH);
2088 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002089
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002090 case PSN_WIZNEXT:
2091 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002092
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002093 case PSN_WIZFINISH:
2094 break;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002095
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002096 case PSN_RESET:
2097 break;
2098
2099 default:
2100 break;
2101 }
2102 }
2103 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002104}
2105
2106void RunWizard(HWND hwnd)
2107{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002108 PROPSHEETPAGE psp = {0};
2109 HPROPSHEETPAGE ahpsp[4] = {0};
2110 PROPSHEETHEADER psh = {0};
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002111
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002112 /* Display module information */
2113 psp.dwSize = sizeof(psp);
2114 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2115 psp.hInstance = GetModuleHandle (NULL);
2116 psp.lParam = 0;
2117 psp.pfnDlgProc = IntroDlgProc;
2118 psp.pszTemplate = MAKEINTRESOURCE(IDD_INTRO);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002119
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002120 ahpsp[0] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002121
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002122 /* Select python version to use */
2123 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2124 psp.pszTemplate = MAKEINTRESOURCE(IDD_SELECTPYTHON);
2125 psp.pfnDlgProc = SelectPythonDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002126
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002127 ahpsp[1] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002128
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002129 /* Install the files */
2130 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2131 psp.pszTemplate = MAKEINTRESOURCE(IDD_INSTALLFILES);
2132 psp.pfnDlgProc = InstallFilesDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002133
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002134 ahpsp[2] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002135
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002136 /* Show success or failure */
2137 psp.dwFlags = PSP_DEFAULT|PSP_HIDEHEADER;
2138 psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHED);
2139 psp.pfnDlgProc = FinishedDlgProc;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002140
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002141 ahpsp[3] = CreatePropertySheetPage(&psp);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002142
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002143 /* Create the property sheet */
2144 psh.dwSize = sizeof(psh);
2145 psh.hInstance = GetModuleHandle(NULL);
2146 psh.hwndParent = hwnd;
2147 psh.phpage = ahpsp;
2148 psh.dwFlags = PSH_WIZARD/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
2149 psh.pszbmWatermark = NULL;
2150 psh.pszbmHeader = NULL;
2151 psh.nStartPage = 0;
2152 psh.nPages = 4;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002153
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002154 PropertySheet(&psh);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002155}
2156
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002157// subtly different from HasLocalMachinePrivs(), in that after executing
2158// an 'elevated' process, we expect this to return TRUE - but there is no
2159// such implication for HasLocalMachinePrivs
2160BOOL MyIsUserAnAdmin()
2161{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002162 typedef BOOL (WINAPI *PFNIsUserAnAdmin)();
2163 static PFNIsUserAnAdmin pfnIsUserAnAdmin = NULL;
2164 HMODULE shell32;
2165 // This function isn't guaranteed to be available (and it can't hurt
2166 // to leave the library loaded)
2167 if (0 == (shell32=LoadLibrary("shell32.dll")))
2168 return FALSE;
2169 if (0 == (pfnIsUserAnAdmin=(PFNIsUserAnAdmin)GetProcAddress(shell32, "IsUserAnAdmin")))
2170 return FALSE;
2171 return (*pfnIsUserAnAdmin)();
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002172}
2173
2174// Some magic for Vista's UAC. If there is a target_version, and
2175// if that target version is installed in the registry under
2176// HKLM, and we are not current administrator, then
2177// re-execute ourselves requesting elevation.
2178// Split into 2 functions - "should we elevate" and "spawn elevated"
2179
2180// Returns TRUE if we should spawn an elevated child
2181BOOL NeedAutoUAC()
2182{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002183 HKEY hk;
2184 char key_name[80];
2185 // no Python version info == we can't know yet.
2186 if (target_version[0] == '\0')
2187 return FALSE;
2188 // see how python is current installed
2189 wsprintf(key_name,
2190 "Software\\Python\\PythonCore\\%s\\InstallPath",
2191 target_version);
2192 if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
2193 key_name, 0, KEY_READ, &hk))
2194 return FALSE;
2195 RegCloseKey(hk);
2196 // Python is installed in HKLM - we must elevate.
2197 return TRUE;
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002198}
2199
2200// Spawn ourself as an elevated application. On failure, a message is
2201// displayed to the user - but this app will always terminate, even
2202// on error.
2203void SpawnUAC()
2204{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002205 // interesting failure scenario that has been seen: initial executable
2206 // runs from a network drive - but once elevated, that network share
2207 // isn't seen, and ShellExecute fails with SE_ERR_ACCESSDENIED.
2208 int ret = (int)ShellExecute(0, "runas", modulename, "", NULL,
2209 SW_SHOWNORMAL);
2210 if (ret <= 32) {
2211 char msg[128];
2212 wsprintf(msg, "Failed to start elevated process (ShellExecute returned %d)", ret);
2213 MessageBox(0, msg, "Setup", MB_OK | MB_ICONERROR);
2214 }
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002215}
2216
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002217int DoInstall(void)
2218{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002219 char ini_buffer[4096];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002220
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002221 /* Read installation information */
2222 GetPrivateProfileString("Setup", "title", "", ini_buffer,
2223 sizeof(ini_buffer), ini_file);
2224 unescape(title, ini_buffer, sizeof(title));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002225
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002226 GetPrivateProfileString("Setup", "info", "", ini_buffer,
2227 sizeof(ini_buffer), ini_file);
2228 unescape(info, ini_buffer, sizeof(info));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002229
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002230 GetPrivateProfileString("Setup", "build_info", "", build_info,
2231 sizeof(build_info), ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002232
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002233 pyc_compile = GetPrivateProfileInt("Setup", "target_compile", 1,
2234 ini_file);
2235 pyo_compile = GetPrivateProfileInt("Setup", "target_optimize", 1,
2236 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002237
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002238 GetPrivateProfileString("Setup", "target_version", "",
2239 target_version, sizeof(target_version),
2240 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002241
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002242 GetPrivateProfileString("metadata", "name", "",
2243 meta_name, sizeof(meta_name),
2244 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002245
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002246 GetPrivateProfileString("Setup", "install_script", "",
2247 install_script, sizeof(install_script),
2248 ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002249
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002250 GetPrivateProfileString("Setup", "user_access_control", "",
2251 user_access_control, sizeof(user_access_control), ini_file);
Christian Heimes81ee3ef2008-05-04 22:42:01 +00002252
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002253 // See if we need to do the Vista UAC magic.
2254 if (strcmp(user_access_control, "force")==0) {
Steve Dower65e4cb12014-11-22 12:54:57 -08002255 if (!MyIsUserAnAdmin()) {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002256 SpawnUAC();
2257 return 0;
2258 }
2259 // already admin - keep going
2260 } else if (strcmp(user_access_control, "auto")==0) {
2261 // Check if it looks like we need UAC control, based
2262 // on how Python itself was installed.
Steve Dower65e4cb12014-11-22 12:54:57 -08002263 if (!MyIsUserAnAdmin() && NeedAutoUAC()) {
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002264 SpawnUAC();
2265 return 0;
2266 }
2267 } else {
2268 // display a warning about unknown values - only the developer
2269 // of the extension will see it (until they fix it!)
2270 if (user_access_control[0] && strcmp(user_access_control, "none") != 0) {
2271 MessageBox(GetFocus(), "Bad user_access_control value", "oops", MB_OK);
2272 // nothing to do.
2273 }
2274 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002275
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002276 hwndMain = CreateBackground(title);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002277
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002278 RunWizard(hwndMain);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002279
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002280 /* Clean up */
2281 UnmapViewOfFile(arc_data);
2282 if (ini_file)
2283 DeleteFile(ini_file);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002284
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002285 if (hBitmap)
2286 DeleteObject(hBitmap);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002287
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002288 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002289}
2290
2291/*********************** uninstall section ******************************/
2292
2293static int compare(const void *p1, const void *p2)
2294{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002295 return strcmp(*(char **)p2, *(char **)p1);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002296}
2297
2298/*
2299 * Commit suicide (remove the uninstaller itself).
2300 *
2301 * Create a batch file to first remove the uninstaller
2302 * (will succeed after it has finished), then the batch file itself.
2303 *
2304 * This technique has been demonstrated by Jeff Richter,
2305 * MSJ 1/1996
2306 */
2307void remove_exe(void)
2308{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002309 char exename[_MAX_PATH];
2310 char batname[_MAX_PATH];
2311 FILE *fp;
2312 STARTUPINFO si;
2313 PROCESS_INFORMATION pi;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002314
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002315 GetModuleFileName(NULL, exename, sizeof(exename));
2316 sprintf(batname, "%s.bat", exename);
2317 fp = fopen(batname, "w");
2318 fprintf(fp, ":Repeat\n");
2319 fprintf(fp, "del \"%s\"\n", exename);
2320 fprintf(fp, "if exist \"%s\" goto Repeat\n", exename);
2321 fprintf(fp, "del \"%s\"\n", batname);
2322 fclose(fp);
2323
2324 ZeroMemory(&si, sizeof(si));
2325 si.cb = sizeof(si);
2326 si.dwFlags = STARTF_USESHOWWINDOW;
2327 si.wShowWindow = SW_HIDE;
2328 if (CreateProcess(NULL,
2329 batname,
2330 NULL,
2331 NULL,
2332 FALSE,
2333 CREATE_SUSPENDED | IDLE_PRIORITY_CLASS,
2334 NULL,
2335 "\\",
2336 &si,
2337 &pi)) {
2338 SetThreadPriority(pi.hThread, THREAD_PRIORITY_IDLE);
2339 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
2340 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
2341 CloseHandle(pi.hProcess);
2342 ResumeThread(pi.hThread);
2343 CloseHandle(pi.hThread);
2344 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002345}
2346
2347void DeleteRegistryKey(char *string)
2348{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002349 char *keyname;
2350 char *subkeyname;
2351 char *delim;
2352 HKEY hKey;
2353 long result;
2354 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002355
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002356 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002357
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002358 keyname = strchr(line, '[');
2359 if (!keyname)
2360 return;
2361 ++keyname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002362
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002363 subkeyname = strchr(keyname, ']');
2364 if (!subkeyname)
2365 return;
2366 *subkeyname++='\0';
2367 delim = strchr(subkeyname, '\n');
2368 if (delim)
2369 *delim = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002370
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002371 result = RegOpenKeyEx(hkey_root,
2372 keyname,
2373 0,
2374 KEY_WRITE,
2375 &hKey);
2376
2377 if (result != ERROR_SUCCESS)
2378 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2379 else {
2380 result = RegDeleteKey(hKey, subkeyname);
2381 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2382 MessageBox(GetFocus(), string, "Could not delete key", MB_OK);
2383 RegCloseKey(hKey);
2384 }
2385 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002386}
2387
2388void DeleteRegistryValue(char *string)
2389{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002390 char *keyname;
2391 char *valuename;
2392 char *value;
2393 HKEY hKey;
2394 long result;
2395 char *line;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002396
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002397 line = strdup(string); /* so we can change it */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002398
2399/* Format is 'Reg DB Value: [key]name=value' */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002400 keyname = strchr(line, '[');
2401 if (!keyname)
2402 return;
2403 ++keyname;
2404 valuename = strchr(keyname, ']');
2405 if (!valuename)
2406 return;
2407 *valuename++ = '\0';
2408 value = strchr(valuename, '=');
2409 if (!value)
2410 return;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002411
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002412 *value++ = '\0';
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002413
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002414 result = RegOpenKeyEx(hkey_root,
2415 keyname,
2416 0,
2417 KEY_WRITE,
2418 &hKey);
2419 if (result != ERROR_SUCCESS)
2420 MessageBox(GetFocus(), string, "Could not open key", MB_OK);
2421 else {
2422 result = RegDeleteValue(hKey, valuename);
2423 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND)
2424 MessageBox(GetFocus(), string, "Could not delete value", MB_OK);
2425 RegCloseKey(hKey);
2426 }
2427 free(line);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002428}
2429
2430BOOL MyDeleteFile(char *line)
2431{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002432 char *pathname = strchr(line, ':');
2433 if (!pathname)
2434 return FALSE;
2435 ++pathname;
2436 while (isspace(*pathname))
2437 ++pathname;
2438 return DeleteFile(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002439}
2440
2441BOOL MyRemoveDirectory(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 RemoveDirectory(pathname);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002450}
2451
2452BOOL Run_RemoveScript(char *line)
2453{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002454 char *dllname;
2455 char *scriptname;
2456 static char lastscript[MAX_PATH];
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002457
2458/* Format is 'Run Scripts: [pythondll]scriptname' */
2459/* XXX Currently, pythondll carries no path!!! */
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002460 dllname = strchr(line, '[');
2461 if (!dllname)
2462 return FALSE;
2463 ++dllname;
2464 scriptname = strchr(dllname, ']');
2465 if (!scriptname)
2466 return FALSE;
2467 *scriptname++ = '\0';
2468 /* this function may be called more than one time with the same
2469 script, only run it one time */
2470 if (strcmp(lastscript, scriptname)) {
2471 char *argv[3] = {NULL, "-remove", NULL};
2472 char *buffer = NULL;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002473
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002474 argv[0] = scriptname;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002475
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002476 if (0 != run_installscript(scriptname, 2, argv, &buffer))
2477 fprintf(stderr, "*** Could not run installation script ***");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002478
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002479 if (buffer && buffer[0])
2480 MessageBox(GetFocus(), buffer, "uninstall-script", MB_OK);
2481 free(buffer);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002482
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002483 strcpy(lastscript, scriptname);
2484 }
2485 return TRUE;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002486}
2487
2488int DoUninstall(int argc, char **argv)
2489{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002490 FILE *logfile;
2491 char buffer[4096];
2492 int nLines = 0;
2493 int i;
2494 char *cp;
2495 int nFiles = 0;
2496 int nDirs = 0;
2497 int nErrors = 0;
2498 char **lines;
2499 int lines_buffer_size = 10;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002500
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002501 if (argc != 3) {
2502 MessageBox(NULL,
2503 "Wrong number of args",
2504 NULL,
2505 MB_OK);
2506 return 1; /* Error */
2507 }
2508 if (strcmp(argv[1], "-u")) {
2509 MessageBox(NULL,
2510 "2. arg is not -u",
2511 NULL,
2512 MB_OK);
2513 return 1; /* Error */
2514 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002515
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002516 logfile = fopen(argv[2], "r");
2517 if (!logfile) {
2518 MessageBox(NULL,
2519 "could not open logfile",
2520 NULL,
2521 MB_OK);
2522 return 1; /* Error */
2523 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002524
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002525 lines = (char **)malloc(sizeof(char *) * lines_buffer_size);
2526 if (!lines)
2527 return SystemError(0, "Out of memory");
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002528
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002529 /* Read the whole logfile, realloacting the buffer */
2530 while (fgets(buffer, sizeof(buffer), logfile)) {
2531 int len = strlen(buffer);
2532 /* remove trailing white space */
2533 while (isspace(buffer[len-1]))
2534 len -= 1;
2535 buffer[len] = '\0';
2536 lines[nLines++] = strdup(buffer);
2537 if (nLines >= lines_buffer_size) {
2538 lines_buffer_size += 10;
2539 lines = (char **)realloc(lines,
2540 sizeof(char *) * lines_buffer_size);
2541 if (!lines)
2542 return SystemError(0, "Out of memory");
2543 }
2544 }
2545 fclose(logfile);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002546
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002547 /* Sort all the lines, so that highest 3-digit codes are first */
2548 qsort(&lines[0], nLines, sizeof(char *),
2549 compare);
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002550
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002551 if (IDYES != MessageBox(NULL,
2552 "Are you sure you want to remove\n"
2553 "this package from your computer?",
2554 "Please confirm",
2555 MB_YESNO | MB_ICONQUESTION))
2556 return 0;
2557
2558 hkey_root = HKEY_LOCAL_MACHINE;
2559 cp = "";
2560 for (i = 0; i < nLines; ++i) {
2561 /* Ignore duplicate lines */
2562 if (strcmp(cp, lines[i])) {
2563 int ign;
2564 cp = lines[i];
2565 /* Parse the lines */
2566 if (2 == sscanf(cp, "%d Root Key: %s", &ign, &buffer)) {
2567 if (strcmp(buffer, "HKEY_CURRENT_USER")==0)
2568 hkey_root = HKEY_CURRENT_USER;
2569 else {
2570 // HKLM - check they have permissions.
2571 if (!HasLocalMachinePrivs()) {
2572 MessageBox(GetFocus(),
2573 "You do not seem to have sufficient access rights\n"
2574 "on this machine to uninstall this software",
2575 NULL,
2576 MB_OK | MB_ICONSTOP);
2577 return 1; /* Error */
2578 }
2579 }
2580 } else if (2 == sscanf(cp, "%d Made Dir: %s", &ign, &buffer)) {
2581 if (MyRemoveDirectory(cp))
2582 ++nDirs;
2583 else {
2584 int code = GetLastError();
2585 if (code != 2 && code != 3) { /* file or path not found */
2586 ++nErrors;
2587 }
2588 }
2589 } else if (2 == sscanf(cp, "%d File Copy: %s", &ign, &buffer)) {
2590 if (MyDeleteFile(cp))
2591 ++nFiles;
2592 else {
2593 int code = GetLastError();
2594 if (code != 2 && code != 3) { /* file or path not found */
2595 ++nErrors;
2596 }
2597 }
2598 } else if (2 == sscanf(cp, "%d File Overwrite: %s", &ign, &buffer)) {
2599 if (MyDeleteFile(cp))
2600 ++nFiles;
2601 else {
2602 int code = GetLastError();
2603 if (code != 2 && code != 3) { /* file or path not found */
2604 ++nErrors;
2605 }
2606 }
2607 } else if (2 == sscanf(cp, "%d Reg DB Key: %s", &ign, &buffer)) {
2608 DeleteRegistryKey(cp);
2609 } else if (2 == sscanf(cp, "%d Reg DB Value: %s", &ign, &buffer)) {
2610 DeleteRegistryValue(cp);
2611 } else if (2 == sscanf(cp, "%d Run Script: %s", &ign, &buffer)) {
2612 Run_RemoveScript(cp);
2613 }
2614 }
2615 }
2616
2617 if (DeleteFile(argv[2])) {
2618 ++nFiles;
2619 } else {
2620 ++nErrors;
2621 SystemError(GetLastError(), argv[2]);
2622 }
2623 if (nErrors)
2624 wsprintf(buffer,
2625 "%d files and %d directories removed\n"
2626 "%d files or directories could not be removed",
2627 nFiles, nDirs, nErrors);
2628 else
2629 wsprintf(buffer, "%d files and %d directories removed",
2630 nFiles, nDirs);
2631 MessageBox(NULL, buffer, "Uninstall Finished!",
2632 MB_OK | MB_ICONINFORMATION);
2633 remove_exe();
2634 return 0;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002635}
2636
2637int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst,
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002638 LPSTR lpszCmdLine, INT nCmdShow)
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002639{
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002640 extern int __argc;
2641 extern char **__argv;
2642 char *basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002643
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002644 GetModuleFileName(NULL, modulename, sizeof(modulename));
2645 GetModuleFileNameW(NULL, wmodulename, sizeof(wmodulename)/sizeof(wmodulename[0]));
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002646
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002647 /* Map the executable file to memory */
2648 arc_data = MapExistingFile(modulename, &arc_size);
2649 if (!arc_data) {
2650 SystemError(GetLastError(), "Could not open archive");
2651 return 1;
2652 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002653
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002654 /* OK. So this program can act as installer (self-extracting
2655 * zip-file, or as uninstaller when started with '-u logfile'
2656 * command line flags.
2657 *
2658 * The installer is usually started without command line flags,
2659 * and the uninstaller is usually started with the '-u logfile'
2660 * flag. What to do if some innocent user double-clicks the
2661 * exe-file?
2662 * The following implements a defensive strategy...
2663 */
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002664
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002665 /* Try to extract the configuration data into a temporary file */
2666 if (ExtractInstallData(arc_data, arc_size, &exe_size,
2667 &ini_file, &pre_install_script))
2668 return DoInstall();
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002669
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002670 if (!ini_file && __argc > 1) {
2671 return DoUninstall(__argc, __argv);
2672 }
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002673
2674
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002675 basename = strrchr(modulename, '\\');
2676 if (basename)
2677 ++basename;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002678
Antoine Pitrou7f14f0d2010-05-09 16:14:21 +00002679 /* Last guess about the purpose of this program */
2680 if (basename && (0 == strncmp(basename, "Remove", 6)))
2681 SystemError(0, "This program is normally started by windows");
2682 else
2683 SystemError(0, "Setup program invalid or damaged");
2684 return 1;
Thomas Hellerbb4b7d22002-11-22 20:39:33 +00002685}