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