2 * Written by Thomas Heller, May 2000
8 * Windows Installer program for distutils.
10 * (a kind of self-extracting zip-file)
12 * At runtime, the exefile has appended:
13 * - compressed setup-data in ini-format, containing the following sections:
16 * author_email=gward@python.net
17 * description=Python Distribution Utilities
20 * url=http://www.python.org/sigs/distutils-sig/
24 * info= text to be displayed in the edit-box
25 * title= to be displayed by this program
26 * target_version = if present, python version required
27 * pyc_compile = if 0, do not compile py to pyc
28 * pyo_compile = if 0, do not compile py to pyo
30 * - a struct meta_data_hdr, describing the above
31 * - a zip-file, containing the modules to be installed.
32 * for the format see http://www.pkware.com/appnote.html
34 * What does this program do?
35 * - the setup-data is uncompressed and written to a temporary file.
36 * - setup-data is queried with GetPrivateProfile... calls
37 * - [metadata] - info is displayed in the dialog box
38 * - The registry is searched for installations of python
39 * - The user can select the python version to use.
40 * - The python-installation directory (sys.prefix) is displayed
41 * - When the start-button is pressed, files from the zip-archive
42 * are extracted to the file system. All .py filenames are stored
46 * Includes now an uninstaller.
52 * display some explanation when no python version is found
53 * instead showing the user an empty listbox to select something from.
55 * Finish the code so that we can use other python installations
56 * additionaly to those found in the registry,
57 * and then #define USE_OTHER_PYTHON_VERSIONS
59 * - install a help-button, which will display something meaningful
62 * - should there be a possibility to display a README file
63 * before starting the installation (if one is present in the archive)
64 * - more comments about what the code does(?)
66 * - evolve this into a full blown installer (???)
85 /* Only for debugging!
86 static int dprintf(char *fmt, ...)
92 va_start(marker, fmt);
93 result = wvsprintf(Buffer, fmt, marker);
94 OutputDebugString(Buffer);
99 /* Bah: global variables */
102 char modulename
[MAX_PATH
];
107 char *ini_file
; /* Full pathname of ini-file */
109 char info
[4096]; /* [Setup] info= */
110 char title
[80]; /* [Setup] title=, contains package name
111 including version: "Distutils-1.0.1" */
112 char target_version
[10]; /* [Setup] target_version=, required python
113 version or empty string */
114 char build_info
[80]; /* [Setup] build_info=, distutils version
117 char meta_name
[80]; /* package name without version like
119 char install_script
[MAX_PATH
];
122 int py_major
, py_minor
; /* Python version selected for installation */
124 char *arc_data
; /* memory mapped archive */
125 DWORD arc_size
; /* number of bytes in archive */
126 int exe_size
; /* number of bytes for exe-file portion */
127 char python_dir
[MAX_PATH
];
128 char pythondll
[MAX_PATH
];
129 BOOL pyc_compile
, pyo_compile
;
131 BOOL success
; /* Installation successfull? */
137 #define WM_NUMFILES WM_USER+1
138 /* wParam: 0, lParam: total number of files */
139 #define WM_NEXTFILE WM_USER+2
140 /* wParam: number of this file */
141 /* lParam: points to pathname */
143 enum { UNSPECIFIED
, ALWAYS
, NEVER
} allow_overwrite
= UNSPECIFIED
;
145 static BOOL
notify(int code
, char *fmt
, ...);
147 /* Note: If scheme.prefix is nonempty, it must end with a '\'! */
148 /* Note: purelib must be the FIRST entry! */
149 SCHEME old_scheme
[] = {
152 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
153 { "SCRIPTS", "Scripts\\" },
158 SCHEME new_scheme
[] = {
159 { "PURELIB", "Lib\\site-packages\\" },
160 { "PLATLIB", "Lib\\site-packages\\" },
161 { "HEADERS", "" }, /* 'Include/dist_name' part already in archive */
162 { "SCRIPTS", "Scripts\\" },
167 static void unescape(char *dst
, char *src
, unsigned size
)
172 while (src
&& *src
&& (size
> 2)) {
186 case '0': case '1': case '2': case '3':
187 ch
= (char)strtol(src
, &eon
, 8);
204 static struct tagFile
{
206 struct tagFile
*next
;
209 static void add_to_filelist(char *path
)
212 p
= (struct tagFile
*)malloc(sizeof(struct tagFile
));
213 p
->path
= strdup(path
);
218 static int do_compile_files(int (__cdecl
* PyRun_SimpleString
)(char *),
223 char Buffer
[MAX_PATH
+ 64];
232 SendDlgItemMessage(hDialog
, IDC_PROGRESS
, PBM_SETRANGE
, 0,
233 MAKELPARAM(0, total
));
234 SendDlgItemMessage(hDialog
, IDC_PROGRESS
, PBM_SETPOS
, 0, 0);
241 "import py_compile; py_compile.compile (r'%s')",
243 if (PyRun_SimpleString(Buffer
)) {
246 /* We send the notification even if the files could not
247 * be created so that the uninstaller will remove them
248 * in case they are created later.
250 wsprintf(Buffer
, "%s%c", p
->path
, optimize
? 'o' : 'c');
251 notify(FILE_CREATED
, Buffer
);
253 SendDlgItemMessage(hDialog
, IDC_PROGRESS
, PBM_SETPOS
, n
, 0);
254 SetDlgItemText(hDialog
, IDC_INFO
, p
->path
);
260 #define DECLPROC(dll, result, name, args)\
261 typedef result (*__PROC__##name) args;\
262 result (*name)args = (__PROC__##name)GetProcAddress(dll, #name)
265 #define DECLVAR(dll, type, name)\
266 type *name = (type*)GetProcAddress(dll, #name)
268 typedef void PyObject
;
272 * Returns number of files which failed to compile,
273 * -1 if python could not be loaded at all
275 static int compile_filelist(HINSTANCE hPython
, BOOL optimize_flag
)
277 DECLPROC(hPython
, void, Py_Initialize
, (void));
278 DECLPROC(hPython
, void, Py_SetProgramName
, (char *));
279 DECLPROC(hPython
, void, Py_Finalize
, (void));
280 DECLPROC(hPython
, int, PyRun_SimpleString
, (char *));
281 DECLPROC(hPython
, PyObject
*, PySys_GetObject
, (char *));
282 DECLVAR(hPython
, int, Py_OptimizeFlag
);
285 struct tagFile
*p
= file_list
;
290 if (!Py_Initialize
|| !Py_SetProgramName
|| !Py_Finalize
)
293 if (!PyRun_SimpleString
|| !PySys_GetObject
|| !Py_OptimizeFlag
)
296 *Py_OptimizeFlag
= optimize_flag
? 1 : 0;
297 Py_SetProgramName(modulename
);
300 errors
+= do_compile_files(PyRun_SimpleString
, optimize_flag
);
306 typedef PyObject
*(*PyCFunction
)(PyObject
*, PyObject
*);
314 typedef struct PyMethodDef PyMethodDef
;
316 void *(*g_Py_BuildValue
)(char *, ...);
317 int (*g_PyArg_ParseTuple
)(PyObject
*, char *, ...);
319 PyObject
*g_PyExc_ValueError
;
320 PyObject
*g_PyExc_OSError
;
322 PyObject
*(*g_PyErr_Format
)(PyObject
*, char *, ...);
324 #define DEF_CSIDL(name) { name, #name }
330 /* Startup menu for all users.
332 DEF_CSIDL(CSIDL_COMMON_STARTMENU
),
334 DEF_CSIDL(CSIDL_STARTMENU
),
336 /* DEF_CSIDL(CSIDL_COMMON_APPDATA), */
337 /* DEF_CSIDL(CSIDL_LOCAL_APPDATA), */
338 /* Repository for application-specific data.
339 Needs Internet Explorer 4.0 */
340 DEF_CSIDL(CSIDL_APPDATA
),
342 /* The desktop for all users.
344 DEF_CSIDL(CSIDL_COMMON_DESKTOPDIRECTORY
),
346 DEF_CSIDL(CSIDL_DESKTOPDIRECTORY
),
348 /* Startup folder for all users.
350 DEF_CSIDL(CSIDL_COMMON_STARTUP
),
351 /* Startup folder. */
352 DEF_CSIDL(CSIDL_STARTUP
),
354 /* Programs item in the start menu for all users.
356 DEF_CSIDL(CSIDL_COMMON_PROGRAMS
),
357 /* Program item in the user's start menu. */
358 DEF_CSIDL(CSIDL_PROGRAMS
),
360 /* DEF_CSIDL(CSIDL_PROGRAM_FILES_COMMON), */
361 /* DEF_CSIDL(CSIDL_PROGRAM_FILES), */
363 /* Virtual folder containing fonts. */
364 DEF_CSIDL(CSIDL_FONTS
),
367 #define DIM(a) (sizeof(a) / sizeof((a)[0]))
369 static PyObject
*FileCreated(PyObject
*self
, PyObject
*args
)
372 if (!g_PyArg_ParseTuple(args
, "s", &path
))
374 notify(FILE_CREATED
, path
);
375 return g_Py_BuildValue("");
378 static PyObject
*DirectoryCreated(PyObject
*self
, PyObject
*args
)
381 if (!g_PyArg_ParseTuple(args
, "s", &path
))
383 notify(DIR_CREATED
, path
);
384 return g_Py_BuildValue("");
387 static PyObject
*GetSpecialFolderPath(PyObject
*self
, PyObject
*args
)
390 char lpszPath
[MAX_PATH
];
392 static HRESULT (WINAPI
*My_SHGetSpecialFolderPath
)(HWND hwnd
,
397 if (!My_SHGetSpecialFolderPath
) {
398 HINSTANCE hLib
= LoadLibrary("shell32.dll");
400 g_PyErr_Format(g_PyExc_OSError
,
401 "function not available");
404 My_SHGetSpecialFolderPath
= (BOOL (WINAPI
*)(HWND
, LPTSTR
,
407 "SHGetSpecialFolderPathA");
410 if (!g_PyArg_ParseTuple(args
, "s", &name
))
413 if (!My_SHGetSpecialFolderPath
) {
414 g_PyErr_Format(g_PyExc_OSError
, "function not available");
418 for (i
= 0; i
< DIM(csidl_names
); ++i
) {
419 if (0 == strcmpi(csidl_names
[i
].name
, name
)) {
421 nFolder
= csidl_names
[i
].nFolder
;
422 if (My_SHGetSpecialFolderPath(NULL
, lpszPath
,
424 return g_Py_BuildValue("s", lpszPath
);
426 g_PyErr_Format(g_PyExc_OSError
,
427 "no such folder (%s)", name
);
433 g_PyErr_Format(g_PyExc_ValueError
, "unknown CSIDL (%s)", name
);
437 static PyObject
*CreateShortcut(PyObject
*self
, PyObject
*args
)
439 char *path
; /* path and filename */
443 char *arguments
= NULL
;
444 char *iconpath
= NULL
;
446 char *workdir
= NULL
;
448 WCHAR wszFilename
[MAX_PATH
];
450 IShellLink
*ps1
= NULL
;
451 IPersistFile
*pPf
= NULL
;
455 hr
= CoInitialize(NULL
);
457 g_PyErr_Format(g_PyExc_OSError
,
458 "CoInitialize failed, error 0x%x", hr
);
462 if (!g_PyArg_ParseTuple(args
, "sss|sssi",
463 &path
, &description
, &filename
,
464 &arguments
, &workdir
, &iconpath
, &iconindex
))
467 hr
= CoCreateInstance(&CLSID_ShellLink
,
469 CLSCTX_INPROC_SERVER
,
473 g_PyErr_Format(g_PyExc_OSError
,
474 "CoCreateInstance failed, error 0x%x", hr
);
478 hr
= ps1
->lpVtbl
->QueryInterface(ps1
, &IID_IPersistFile
,
481 g_PyErr_Format(g_PyExc_OSError
,
482 "QueryInterface(IPersistFile) error 0x%x", hr
);
487 hr
= ps1
->lpVtbl
->SetPath(ps1
, path
);
489 g_PyErr_Format(g_PyExc_OSError
,
490 "SetPath() failed, error 0x%x", hr
);
494 hr
= ps1
->lpVtbl
->SetDescription(ps1
, description
);
496 g_PyErr_Format(g_PyExc_OSError
,
497 "SetDescription() failed, error 0x%x", hr
);
502 hr
= ps1
->lpVtbl
->SetArguments(ps1
, arguments
);
504 g_PyErr_Format(g_PyExc_OSError
,
505 "SetArguments() error 0x%x", hr
);
511 hr
= ps1
->lpVtbl
->SetIconLocation(ps1
, iconpath
, iconindex
);
513 g_PyErr_Format(g_PyExc_OSError
,
514 "SetIconLocation() error 0x%x", hr
);
520 hr
= ps1
->lpVtbl
->SetWorkingDirectory(ps1
, workdir
);
522 g_PyErr_Format(g_PyExc_OSError
,
523 "SetWorkingDirectory() error 0x%x", hr
);
528 MultiByteToWideChar(CP_ACP
, 0,
530 wszFilename
, MAX_PATH
);
532 hr
= pPf
->lpVtbl
->Save(pPf
, wszFilename
, TRUE
);
534 g_PyErr_Format(g_PyExc_OSError
,
535 "Save() failed, error 0x%x", hr
);
539 pPf
->lpVtbl
->Release(pPf
);
540 ps1
->lpVtbl
->Release(ps1
);
542 return g_Py_BuildValue("");
546 pPf
->lpVtbl
->Release(pPf
);
549 ps1
->lpVtbl
->Release(ps1
);
556 #define METH_VARARGS 0x0001
558 PyMethodDef meth
[] = {
559 {"create_shortcut", CreateShortcut
, METH_VARARGS
, NULL
},
560 {"get_special_folder_path", GetSpecialFolderPath
, METH_VARARGS
, NULL
},
561 {"file_created", FileCreated
, METH_VARARGS
, NULL
},
562 {"directory_created", DirectoryCreated
, METH_VARARGS
, NULL
},
566 * This function returns one of the following error codes:
567 * 1 if the Python-dll does not export the functions we need
568 * 2 if no install-script is specified in pathname
569 * 3 if the install-script file could not be opened
570 * the return value of PyRun_SimpleFile() otherwise,
571 * which is 0 if everything is ok, -1 if an exception had occurred
572 * in the install-script.
576 run_installscript(HINSTANCE hPython
, char *pathname
, int argc
, char **argv
)
578 DECLPROC(hPython
, void, Py_Initialize
, (void));
579 DECLPROC(hPython
, int, PySys_SetArgv
, (int, char **));
580 DECLPROC(hPython
, int, PyRun_SimpleFile
, (FILE *, char *));
581 DECLPROC(hPython
, void, Py_Finalize
, (void));
582 DECLPROC(hPython
, PyObject
*, PyImport_ImportModule
, (char *));
583 DECLPROC(hPython
, int, PyObject_SetAttrString
,
584 (PyObject
*, char *, PyObject
*));
585 DECLPROC(hPython
, PyObject
*, PyObject_GetAttrString
,
586 (PyObject
*, char *));
587 DECLPROC(hPython
, PyObject
*, Py_BuildValue
, (char *, ...));
588 DECLPROC(hPython
, PyObject
*, PyCFunction_New
,
589 (PyMethodDef
*, PyObject
*));
590 DECLPROC(hPython
, int, PyArg_ParseTuple
, (PyObject
*, char *, ...));
591 DECLPROC(hPython
, PyObject
*, PyErr_Format
, (PyObject
*, char *));
598 if (!Py_Initialize
|| !PySys_SetArgv
599 || !PyRun_SimpleFile
|| !Py_Finalize
)
602 if (!PyImport_ImportModule
|| !PyObject_SetAttrString
606 if (!PyCFunction_New
|| !PyArg_ParseTuple
|| !PyErr_Format
)
609 if (!PyObject_GetAttrString
)
612 g_Py_BuildValue
= Py_BuildValue
;
613 g_PyArg_ParseTuple
= PyArg_ParseTuple
;
614 g_PyErr_Format
= PyErr_Format
;
616 if (pathname
== NULL
|| pathname
[0] == '\0')
619 fp
= fopen(pathname
, "r");
621 fprintf(stderr
, "Could not open postinstall-script %s\n",
626 SetDlgItemText(hDialog
, IDC_INFO
, "Running Script...");
630 mod
= PyImport_ImportModule("__builtin__");
634 g_PyExc_ValueError
= PyObject_GetAttrString(mod
,
636 g_PyExc_OSError
= PyObject_GetAttrString(mod
, "OSError");
637 for (i
= 0; i
< DIM(meth
); ++i
) {
638 PyObject_SetAttrString(mod
, meth
[i
].ml_name
,
639 PyCFunction_New(&meth
[i
], NULL
));
643 PySys_SetArgv(argc
, argv
);
644 result
= PyRun_SimpleFile(fp
, pathname
);
652 static BOOL
SystemError(int error
, char *msg
)
660 FORMAT_MESSAGE_ALLOCATE_BUFFER
|
661 FORMAT_MESSAGE_FROM_SYSTEM
,
664 MAKELANGID(LANG_NEUTRAL
, SUBLANG_DEFAULT
),
669 strncpy(Buffer
, lpMsgBuf
, sizeof(Buffer
));
674 _snprintf(Buffer
+n
, sizeof(Buffer
)-n
, msg
);
675 MessageBox(hwndMain
, Buffer
, "Runtime Error", MB_OK
| MB_ICONSTOP
);
679 static BOOL
AskOverwrite(char *filename
)
683 if (allow_overwrite
== ALWAYS
)
685 if (allow_overwrite
== NEVER
)
687 result
= MessageBox(hDialog
,
688 "Overwrite existing files?\n"
690 "Press YES to ALWAYS overwrite existing files,\n"
691 "press NO to NEVER overwrite existing files.",
693 MB_YESNO
| MB_ICONQUESTION
);
695 allow_overwrite
= ALWAYS
;
696 else if (result
== IDNO
)
697 allow_overwrite
= NEVER
;
701 static BOOL
notify (int code
, char *fmt
, ...)
709 va_start(marker
, fmt
);
710 _vsnprintf(Buffer
, sizeof(Buffer
), fmt
, marker
);
715 result
= AskOverwrite(Buffer
);
718 /* Information notification */
721 fprintf(logfile
, "100 Made Dir: %s\n", fmt
);
726 fprintf(logfile
, "200 File Copy: %s\n", fmt
);
727 goto add_to_filelist_label
;
730 case FILE_OVERWRITTEN
:
732 fprintf(logfile
, "200 File Overwrite: %s\n", fmt
);
733 add_to_filelist_label
:
734 if ((cp
= strrchr(fmt
, '.')) && (0 == strcmp (cp
, ".py")))
735 add_to_filelist(fmt
);
740 MessageBox(GetFocus(), Buffer
, "Error",
741 MB_OK
| MB_ICONWARNING
);
745 SystemError(GetLastError(), Buffer
);
749 a
= va_arg(marker
, int);
750 b
= va_arg(marker
, int);
751 SendMessage(hDialog
, WM_NUMFILES
, 0, MAKELPARAM(0, a
));
752 SendMessage(hDialog
, WM_NEXTFILE
, b
,(LPARAM
)fmt
);
759 static char *MapExistingFile(char *pathname
, DWORD
*psize
)
761 HANDLE hFile
, hFileMapping
;
762 DWORD nSizeLow
, nSizeHigh
;
765 hFile
= CreateFile(pathname
,
766 GENERIC_READ
, FILE_SHARE_READ
, NULL
,
768 FILE_ATTRIBUTE_NORMAL
, NULL
);
769 if (hFile
== INVALID_HANDLE_VALUE
)
771 nSizeLow
= GetFileSize(hFile
, &nSizeHigh
);
772 hFileMapping
= CreateFileMapping(hFile
,
773 NULL
, PAGE_READONLY
, 0, 0, NULL
);
776 if (hFileMapping
== INVALID_HANDLE_VALUE
)
779 data
= MapViewOfFile(hFileMapping
,
780 FILE_MAP_READ
, 0, 0, 0);
782 CloseHandle(hFileMapping
);
788 static void create_bitmap(HWND hwnd
)
790 BITMAPFILEHEADER
*bfh
;
802 bfh
= (BITMAPFILEHEADER
*)bitmap_bytes
;
803 bi
= (BITMAPINFO
*)(bitmap_bytes
+ sizeof(BITMAPFILEHEADER
));
805 hBitmap
= CreateDIBitmap(hdc
,
808 bitmap_bytes
+ bfh
->bfOffBits
,
811 ReleaseDC(hwnd
, hdc
);
814 static char *ExtractIniFile(char *data
, DWORD size
, int *pexe_size
)
816 /* read the end of central directory record */
817 struct eof_cdir
*pe
= (struct eof_cdir
*)&data
[size
- sizeof
820 int arc_start
= size
- sizeof (struct eof_cdir
) - pe
->nBytesCDir
-
823 int ofs
= arc_start
- sizeof (struct meta_data_hdr
);
825 /* read meta_data info */
826 struct meta_data_hdr
*pmd
= (struct meta_data_hdr
*)&data
[ofs
];
829 char tempdir
[MAX_PATH
];
831 if (pe
->tag
!= 0x06054b50) {
835 if (pmd
->tag
!= 0x1234567A || ofs
< 0) {
839 if (pmd
->bitmap_size
) {
840 /* Store pointer to bitmap bytes */
841 bitmap_bytes
= (char *)pmd
- pmd
->uncomp_size
- pmd
->bitmap_size
;
844 *pexe_size
= ofs
- pmd
->uncomp_size
- pmd
->bitmap_size
;
846 src
= ((char *)pmd
) - pmd
->uncomp_size
;
847 ini_file
= malloc(MAX_PATH
); /* will be returned, so do not free it */
850 if (!GetTempPath(sizeof(tempdir
), tempdir
)
851 || !GetTempFileName(tempdir
, "~du", 0, ini_file
)) {
852 SystemError(GetLastError(),
853 "Could not create temporary file");
857 dst
= map_new_file(CREATE_ALWAYS
, ini_file
, NULL
, pmd
->uncomp_size
,
858 0, 0, NULL
/*notify*/);
861 memcpy(dst
, src
, pmd
->uncomp_size
);
862 UnmapViewOfFile(dst
);
866 static void PumpMessages(void)
869 while (PeekMessage(&msg
, NULL
, 0, 0, PM_REMOVE
)) {
870 TranslateMessage(&msg
);
871 DispatchMessage(&msg
);
876 WindowProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
884 hdc
= BeginPaint(hwnd
, &ps
);
885 h
= GetSystemMetrics(SM_CYSCREEN
) / 10;
886 hFont
= CreateFont(h
, 0, 0, 0, 700, TRUE
,
887 0, 0, 0, 0, 0, 0, 0, "Times Roman");
888 hFont
= SelectObject(hdc
, hFont
);
889 SetBkMode(hdc
, TRANSPARENT
);
890 TextOut(hdc
, 15, 15, title
, strlen(title
));
891 SetTextColor(hdc
, RGB(255, 255, 255));
892 TextOut(hdc
, 10, 10, title
, strlen(title
));
893 DeleteObject(SelectObject(hdc
, hFont
));
897 return DefWindowProc(hwnd
, msg
, wParam
, lParam
);
900 static HWND
CreateBackground(char *title
)
906 wc
.style
= CS_VREDRAW
| CS_HREDRAW
;
907 wc
.lpfnWndProc
= WindowProc
;
910 wc
.hInstance
= GetModuleHandle(NULL
);
912 wc
.hCursor
= LoadCursor(NULL
, IDC_ARROW
);
913 wc
.hbrBackground
= CreateSolidBrush(RGB(0, 0, 128));
914 wc
.lpszMenuName
= NULL
;
915 wc
.lpszClassName
= "SetupWindowClass";
917 if (!RegisterClass(&wc
))
919 "Could not register window class",
922 wsprintf(buffer
, "Setup %s", title
);
923 hwnd
= CreateWindow("SetupWindowClass",
927 GetSystemMetrics(SM_CXFULLSCREEN
),
928 GetSystemMetrics(SM_CYFULLSCREEN
),
931 GetModuleHandle(NULL
),
933 ShowWindow(hwnd
, SW_SHOWMAXIMIZED
);
939 * Center a window on the screen
941 static void CenterWindow(HWND hwnd
)
946 GetWindowRect(hwnd
, &rc
);
947 w
= GetSystemMetrics(SM_CXSCREEN
);
948 h
= GetSystemMetrics(SM_CYSCREEN
);
950 (w
- (rc
.right
-rc
.left
))/2,
951 (h
- (rc
.bottom
-rc
.top
))/2,
952 rc
.right
-rc
.left
, rc
.bottom
-rc
.top
, FALSE
);
958 IntroDlgProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
967 SendDlgItemMessage(hwnd
, IDC_BITMAP
, STM_SETIMAGE
,
968 IMAGE_BITMAP
, (LPARAM
)hBitmap
);
969 CenterWindow(GetParent(hwnd
));
971 "This Wizard will install %s on your computer. "
972 "Click Next to continue "
973 "or Cancel to exit the Setup Wizard.",
975 SetDlgItemText(hwnd
, IDC_TITLE
, Buffer
);
976 SetDlgItemText(hwnd
, IDC_INTRO_TEXT
, info
);
977 SetDlgItemText(hwnd
, IDC_BUILD_INFO
, build_info
);
981 lpnm
= (LPNMHDR
) lParam
;
983 switch (lpnm
->code
) {
985 PropSheet_SetWizButtons(GetParent(hwnd
), PSWIZB_NEXT
);
1001 #ifdef USE_OTHER_PYTHON_VERSIONS
1002 /* These are really private variables used to communicate
1003 * between StatusRoutine and CheckPythonExe
1005 char bound_image_dll
[_MAX_PATH
];
1006 int bound_image_major
;
1007 int bound_image_minor
;
1009 static BOOL __stdcall
StatusRoutine(IMAGEHLP_STATUS_REASON reason
,
1015 char fname
[_MAX_PATH
];
1019 case BindOutOfMemory
:
1020 case BindRvaToVaFailed
:
1021 case BindNoRoomInImage
:
1022 case BindImportProcedureFailed
:
1025 case BindImportProcedure
:
1027 case BindForwarderNOT
:
1028 case BindImageModified
:
1029 case BindExpandFileHeaders
:
1030 case BindImageComplete
:
1031 case BindSymbolsNotUpdated
:
1032 case BindMismatchedSymbols
:
1033 case BindImportModuleFailed
:
1036 case BindImportModule
:
1037 if (1 == sscanf(DllName
, "python%d", &int_version
)) {
1038 SearchPath(NULL
, DllName
, NULL
, sizeof(fname
),
1040 strcpy(bound_image_dll
, fname
);
1041 bound_image_major
= int_version
/ 10;
1042 bound_image_minor
= int_version
% 10;
1043 OutputDebugString("BOUND ");
1044 OutputDebugString(fname
);
1045 OutputDebugString("\n");
1054 static LPSTR
get_sys_prefix(LPSTR exe
, LPSTR dll
)
1056 void (__cdecl
* Py_Initialize
)(void);
1057 void (__cdecl
* Py_SetProgramName
)(char *);
1058 void (__cdecl
* Py_Finalize
)(void);
1059 void* (__cdecl
* PySys_GetObject
)(char *);
1060 void (__cdecl
* PySys_SetArgv
)(int, char **);
1061 char* (__cdecl
* Py_GetPrefix
)(void);
1062 char* (__cdecl
* Py_GetPath
)(void);
1064 LPSTR prefix
= NULL
;
1065 int (__cdecl
* PyRun_SimpleString
)(char *);
1069 wsprintf(Buffer
, "PYTHONHOME=%s", exe
);
1070 *strrchr(Buffer
, '\\') = '\0';
1071 // MessageBox(GetFocus(), Buffer, "PYTHONHOME", MB_OK);
1073 _putenv("PYTHONPATH=");
1076 hPython
= LoadLibrary(dll
);
1079 Py_Initialize
= (void (*)(void))GetProcAddress
1080 (hPython
,"Py_Initialize");
1082 PySys_SetArgv
= (void (*)(int, char **))GetProcAddress
1083 (hPython
,"PySys_SetArgv");
1085 PyRun_SimpleString
= (int (*)(char *))GetProcAddress
1086 (hPython
,"PyRun_SimpleString");
1088 Py_SetProgramName
= (void (*)(char *))GetProcAddress
1089 (hPython
,"Py_SetProgramName");
1091 PySys_GetObject
= (void* (*)(char *))GetProcAddress
1092 (hPython
,"PySys_GetObject");
1094 Py_GetPrefix
= (char * (*)(void))GetProcAddress
1095 (hPython
,"Py_GetPrefix");
1097 Py_GetPath
= (char * (*)(void))GetProcAddress
1098 (hPython
,"Py_GetPath");
1100 Py_Finalize
= (void (*)(void))GetProcAddress(hPython
,
1102 Py_SetProgramName(exe
);
1104 PySys_SetArgv(1, &exe
);
1106 MessageBox(GetFocus(), Py_GetPrefix(), "PREFIX", MB_OK
);
1107 MessageBox(GetFocus(), Py_GetPath(), "PATH", MB_OK
);
1110 FreeLibrary(hPython
);
1116 CheckPythonExe(LPSTR pathname
, LPSTR version
, int *pmajor
, int *pminor
)
1118 bound_image_dll
[0] = '\0';
1119 if (!BindImageEx(BIND_NO_BOUND_IMPORTS
| BIND_NO_UPDATE
| BIND_ALL_IMAGES
,
1124 return SystemError(0, "Could not bind image");
1125 if (bound_image_dll
[0] == '\0')
1126 return SystemError(0, "Does not seem to be a python executable");
1127 *pmajor
= bound_image_major
;
1128 *pminor
= bound_image_minor
;
1129 if (version
&& *version
) {
1130 char core_version
[12];
1131 wsprintf(core_version
, "%d.%d", bound_image_major
, bound_image_minor
);
1132 if (strcmp(version
, core_version
))
1133 return SystemError(0, "Wrong Python version");
1135 get_sys_prefix(pathname
, bound_image_dll
);
1140 * Browse for other python versions. Insert it into the listbox specified
1141 * by hwnd. version, if not NULL or empty, is the version required.
1143 static BOOL
GetOtherPythonVersion(HWND hwnd
, LPSTR version
)
1145 char vers_name
[_MAX_PATH
+ 80];
1148 char pathname
[_MAX_PATH
];
1151 strcpy(pathname
, "python.exe");
1153 memset(&of
, 0, sizeof(of
));
1154 of
.lStructSize
= sizeof(OPENFILENAME
);
1155 of
.hwndOwner
= GetParent(hwnd
);
1156 of
.hInstance
= NULL
;
1157 of
.lpstrFilter
= "python.exe\0python.exe\0";
1158 of
.lpstrCustomFilter
= NULL
;
1159 of
.nMaxCustFilter
= 0;
1160 of
.nFilterIndex
= 1;
1161 of
.lpstrFile
= pathname
;
1162 of
.nMaxFile
= sizeof(pathname
);
1163 of
.lpstrFileTitle
= NULL
;
1164 of
.nMaxFileTitle
= 0;
1165 of
.lpstrInitialDir
= NULL
;
1166 of
.lpstrTitle
= "Python executable";
1167 of
.Flags
= OFN_FILEMUSTEXIST
| OFN_PATHMUSTEXIST
;
1168 of
.lpstrDefExt
= "exe";
1170 result
= GetOpenFileName(&of
);
1173 if (!CheckPythonExe(pathname
, version
, &major
, &minor
)) {
1176 *strrchr(pathname
, '\\') = '\0';
1177 wsprintf(vers_name
, "Python Version %d.%d in %s",
1178 major
, minor
, pathname
);
1179 itemindex
= SendMessage(hwnd
, LB_INSERTSTRING
, -1,
1180 (LPARAM
)(LPSTR
)vers_name
);
1181 SendMessage(hwnd
, LB_SETCURSEL
, itemindex
, 0);
1182 SendMessage(hwnd
, LB_SETITEMDATA
, itemindex
,
1183 (LPARAM
)(LPSTR
)strdup(pathname
));
1188 #endif /* USE_OTHER_PYTHON_VERSIONS */
1192 * Fill the listbox specified by hwnd with all python versions found
1193 * in the registry. version, if not NULL or empty, is the version
1196 static BOOL
GetPythonVersions(HWND hwnd
, HKEY hkRoot
, LPSTR version
)
1199 char core_version
[80];
1204 if (ERROR_SUCCESS
!= RegOpenKeyEx(hkRoot
,
1205 "Software\\Python\\PythonCore",
1206 0, KEY_READ
, &hKey
))
1208 bufsize
= sizeof(core_version
);
1209 while (ERROR_SUCCESS
== RegEnumKeyEx(hKey
, index
,
1210 core_version
, &bufsize
, NULL
,
1211 NULL
, NULL
, NULL
)) {
1212 char subkey_name
[80], vers_name
[80], prefix_buf
[MAX_PATH
+1];
1217 bufsize
= sizeof(core_version
);
1219 if (version
&& *version
&& strcmp(version
, core_version
))
1222 wsprintf(vers_name
, "Python Version %s (found in registry)",
1224 wsprintf(subkey_name
,
1225 "Software\\Python\\PythonCore\\%s\\InstallPath",
1227 value_size
= sizeof(subkey_name
);
1228 if (ERROR_SUCCESS
== RegOpenKeyEx(hkRoot
, subkey_name
, 0, KEY_READ
, &hk
)) {
1229 if (ERROR_SUCCESS
== RegQueryValueEx(hk
, NULL
, NULL
, NULL
, prefix_buf
,
1231 itemindex
= SendMessage(hwnd
, LB_ADDSTRING
, 0,
1232 (LPARAM
)(LPSTR
)vers_name
);
1233 SendMessage(hwnd
, LB_SETITEMDATA
, itemindex
,
1234 (LPARAM
)(LPSTR
)strdup(prefix_buf
));
1243 /* Return the installation scheme depending on Python version number */
1244 SCHEME
*GetScheme(int major
, int minor
)
1248 else if((major
== 2) && (minor
>= 2))
1254 SelectPythonDlgProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1261 SendDlgItemMessage(hwnd
, IDC_BITMAP
, STM_SETIMAGE
,
1262 IMAGE_BITMAP
, (LPARAM
)hBitmap
);
1263 GetPythonVersions(GetDlgItem(hwnd
, IDC_VERSIONS_LIST
),
1264 HKEY_LOCAL_MACHINE
, target_version
);
1265 GetPythonVersions(GetDlgItem(hwnd
, IDC_VERSIONS_LIST
),
1266 HKEY_CURRENT_USER
, target_version
);
1267 { /* select the last entry which is the highest python
1270 count
= SendDlgItemMessage(hwnd
, IDC_VERSIONS_LIST
,
1272 if (count
&& count
!= LB_ERR
)
1273 SendDlgItemMessage(hwnd
, IDC_VERSIONS_LIST
, LB_SETCURSEL
,
1276 /* If a specific Python version is required,
1277 * display a prominent notice showing this fact.
1279 if (target_version
&& target_version
[0]) {
1282 "Python %s is required for this package. "
1283 "Select installation to use:",
1285 SetDlgItemText(hwnd
, IDC_TITLE
, buffer
);
1291 if (target_version
&& target_version
[0]) {
1293 "Python version %s required, which was not found"
1294 " in the registry.", target_version
);
1297 msg
= "No Python installation found in the registry.";
1298 MessageBox(hwnd
, msg
, "Cannot install",
1299 MB_OK
| MB_ICONSTOP
);
1302 goto UpdateInstallDir
;
1306 switch (LOWORD(wParam
)) {
1308 case IDC_OTHERPYTHON:
1309 if (GetOtherPythonVersion(GetDlgItem(hwnd, IDC_VERSIONS_LIST),
1311 goto UpdateInstallDir;
1314 case IDC_VERSIONS_LIST
:
1315 switch (HIWORD(wParam
)) {
1320 PropSheet_SetWizButtons(GetParent(hwnd
),
1321 PSWIZB_BACK
| PSWIZB_NEXT
);
1322 id
= SendDlgItemMessage(hwnd
, IDC_VERSIONS_LIST
,
1323 LB_GETCURSEL
, 0, 0);
1325 PropSheet_SetWizButtons(GetParent(hwnd
),
1327 SetDlgItemText(hwnd
, IDC_PATH
, "");
1328 SetDlgItemText(hwnd
, IDC_INSTALL_PATH
, "");
1329 strcpy(python_dir
, "");
1330 strcpy(pythondll
, "");
1334 PropSheet_SetWizButtons(GetParent(hwnd
),
1335 PSWIZB_BACK
| PSWIZB_NEXT
);
1336 /* Get the python directory */
1337 cp
= (LPSTR
)SendDlgItemMessage(hwnd
,
1342 strcpy(python_dir
, cp
);
1343 SetDlgItemText(hwnd
, IDC_PATH
, python_dir
);
1344 /* retrieve the python version and pythondll to use */
1345 result
= SendDlgItemMessage(hwnd
, IDC_VERSIONS_LIST
,
1346 LB_GETTEXTLEN
, (WPARAM
)id
, 0);
1347 pbuf
= (char *)malloc(result
+ 1);
1349 /* guess the name of the python-dll */
1350 SendDlgItemMessage(hwnd
, IDC_VERSIONS_LIST
,
1351 LB_GETTEXT
, (WPARAM
)id
,
1353 result
= sscanf(pbuf
, "Python Version %d.%d",
1354 &py_major
, &py_minor
);
1357 wsprintf(pythondll
, "c:\\python22\\PCBuild\\python%d%d_d.dll",
1358 py_major
, py_minor
);
1360 wsprintf(pythondll
, "python%d%d.dll",
1361 py_major
, py_minor
);
1365 strcpy(pythondll
, "");
1366 /* retrieve the scheme for this version */
1368 char install_path
[_MAX_PATH
];
1369 SCHEME
*scheme
= GetScheme(py_major
, py_minor
);
1370 strcpy(install_path
, python_dir
);
1371 if (install_path
[strlen(install_path
)-1] != '\\')
1372 strcat(install_path
, "\\");
1373 strcat(install_path
, scheme
[0].prefix
);
1374 SetDlgItemText(hwnd
, IDC_INSTALL_PATH
, install_path
);
1383 lpnm
= (LPNMHDR
) lParam
;
1385 switch (lpnm
->code
) {
1388 id
= SendDlgItemMessage(hwnd
, IDC_VERSIONS_LIST
,
1389 LB_GETCURSEL
, 0, 0);
1391 PropSheet_SetWizButtons(GetParent(hwnd
),
1394 PropSheet_SetWizButtons(GetParent(hwnd
),
1395 PSWIZB_BACK
| PSWIZB_NEXT
);
1414 static BOOL
OpenLogfile(char *dir
)
1416 char buffer
[_MAX_PATH
+1];
1421 char subkey_name
[256];
1422 static char KeyName
[] =
1423 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
1426 result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
1431 if (result
!= ERROR_SUCCESS
) {
1432 if (result
== ERROR_ACCESS_DENIED
) {
1433 MessageBox(GetFocus(),
1434 "You do not seem to have sufficient access rights\n"
1435 "on this machine to install this software",
1437 MB_OK
| MB_ICONSTOP
);
1440 MessageBox(GetFocus(), KeyName
, "Could not open key", MB_OK
);
1444 sprintf(buffer
, "%s\\%s-wininst.log", dir
, meta_name
);
1445 logfile
= fopen(buffer
, "a");
1447 now
= localtime(<ime
);
1448 strftime(buffer
, sizeof(buffer
),
1449 "*** Installation started %Y/%m/%d %H:%M ***\n",
1451 fprintf(logfile
, buffer
);
1452 fprintf(logfile
, "Source: %s\n", modulename
);
1454 sprintf(subkey_name
, "%s-py%d.%d", meta_name
, py_major
, py_minor
);
1456 result
= RegCreateKeyEx(hKey
, subkey_name
,
1463 if (result
!= ERROR_SUCCESS
)
1464 MessageBox(GetFocus(), subkey_name
, "Could not create key", MB_OK
);
1468 if (disposition
== REG_CREATED_NEW_KEY
)
1469 fprintf(logfile
, "020 Reg DB Key: [%s]%s\n", KeyName
, subkey_name
);
1471 sprintf(buffer
, "Python %d.%d %s", py_major
, py_minor
, title
);
1473 result
= RegSetValueEx(hSubkey
, "DisplayName",
1479 if (result
!= ERROR_SUCCESS
)
1480 MessageBox(GetFocus(), buffer
, "Could not set key value", MB_OK
);
1482 fprintf(logfile
, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1483 KeyName
, subkey_name
, "DisplayName", buffer
);
1487 sprintf(buffer
, "%s\\Remove%s.exe", dir
, meta_name
);
1488 fp
= fopen(buffer
, "wb");
1489 fwrite(arc_data
, exe_size
, 1, fp
);
1492 sprintf(buffer
, "\"%s\\Remove%s.exe\" -u \"%s\\%s-wininst.log\"",
1493 dir
, meta_name
, dir
, meta_name
);
1495 result
= RegSetValueEx(hSubkey
, "UninstallString",
1501 if (result
!= ERROR_SUCCESS
)
1502 MessageBox(GetFocus(), buffer
, "Could not set key value", MB_OK
);
1504 fprintf(logfile
, "040 Reg DB Value: [%s\\%s]%s=%s\n",
1505 KeyName
, subkey_name
, "UninstallString", buffer
);
1510 static void CloseLogfile(void)
1512 char buffer
[_MAX_PATH
+1];
1517 now
= localtime(<ime
);
1518 strftime(buffer
, sizeof(buffer
),
1519 "*** Installation finished %Y/%m/%d %H:%M ***\n",
1521 fprintf(logfile
, buffer
);
1527 InstallFilesDlgProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1536 SendDlgItemMessage(hwnd
, IDC_BITMAP
, STM_SETIMAGE
,
1537 IMAGE_BITMAP
, (LPARAM
)hBitmap
);
1539 "Click Next to begin the installation of %s. "
1540 "If you want to review or change any of your "
1541 " installation settings, click Back. "
1542 "Click Cancel to exit the wizard.",
1544 SetDlgItemText(hwnd
, IDC_TITLE
, Buffer
);
1548 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETRANGE
, 0, lParam
);
1553 SendDlgItemMessage(hwnd
, IDC_PROGRESS
, PBM_SETPOS
, wParam
,
1555 SetDlgItemText(hwnd
, IDC_INFO
, (LPSTR
)lParam
);
1560 lpnm
= (LPNMHDR
) lParam
;
1562 switch (lpnm
->code
) {
1564 PropSheet_SetWizButtons(GetParent(hwnd
),
1565 PSWIZB_BACK
| PSWIZB_NEXT
);
1572 /* Handle a Next button click here */
1575 /* Make sure the installation directory name ends in a */
1577 if (python_dir
[strlen(python_dir
)-1] != '\\')
1578 strcat(python_dir
, "\\");
1579 /* Strip the trailing backslash again */
1580 python_dir
[strlen(python_dir
)-1] = '\0';
1582 if (!OpenLogfile(python_dir
))
1586 * The scheme we have to use depends on the Python version...
1587 if sys.version < "2.2":
1591 'headers': '$base/Include/$dist_name',
1592 'scripts': '$base/Scripts',
1597 'purelib': '$base/Lib/site-packages',
1598 'platlib': '$base/Lib/site-packages',
1599 'headers': '$base/Include/$dist_name',
1600 'scripts': '$base/Scripts',
1604 scheme
= GetScheme(py_major
, py_minor
);
1606 /* Extract all files from the archive */
1607 SetDlgItemText(hwnd
, IDC_TITLE
, "Installing files...");
1608 success
= unzip_archive(scheme
,
1609 python_dir
, arc_data
,
1611 /* Compile the py-files */
1615 SetDlgItemText(hwnd
, IDC_TITLE
,
1616 "Compiling files to .pyc...");
1618 SetDlgItemText(hDialog
, IDC_INFO
, "Loading python...");
1619 hPython
= LoadLibrary(pythondll
);
1621 errors
= compile_filelist(hPython
, FALSE
);
1622 FreeLibrary(hPython
);
1624 /* Compilation errors are intentionally ignored:
1625 * Python2.0 contains a bug which will result
1626 * in sys.path containing garbage under certain
1627 * circumstances, and an error message will only
1634 SetDlgItemText(hwnd
, IDC_TITLE
,
1635 "Compiling files to .pyo...");
1637 SetDlgItemText(hDialog
, IDC_INFO
, "Loading python...");
1638 hPython
= LoadLibrary(pythondll
);
1640 errors
= compile_filelist(hPython
, TRUE
);
1641 FreeLibrary(hPython
);
1643 /* Errors ignored: see above */
1661 FinishedDlgProc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
1668 SendDlgItemMessage(hwnd
, IDC_BITMAP
, STM_SETIMAGE
,
1669 IMAGE_BITMAP
, (LPARAM
)hBitmap
);
1671 SetDlgItemText(hwnd
, IDC_INFO
, "Installation failed.");
1673 /* async delay: will show the dialog box completely before
1674 the install_script is started */
1675 PostMessage(hwnd
, WM_USER
, 0, 0L);
1680 if (install_script
&& install_script
[0]) {
1681 char fname
[MAX_PATH
];
1689 char *argv
[3] = {NULL
, "-install", NULL
};
1691 SetDlgItemText(hwnd
, IDC_TITLE
,
1692 "Please wait while running postinstall script...");
1693 strcpy(fname
, python_dir
);
1694 strcat(fname
, "\\Scripts\\");
1695 strcat(fname
, install_script
);
1698 fprintf(logfile
, "300 Run Script: [%s]%s\n", pythondll
, fname
);
1700 tempname
= tmpnam(NULL
);
1702 if (!freopen(tempname
, "a", stderr
))
1703 MessageBox(GetFocus(), "freopen stderr", NULL
, MB_OK
);
1704 if (!freopen(tempname
, "a", stdout
))
1705 MessageBox(GetFocus(), "freopen stdout", NULL
, MB_OK
);
1707 if (0 != setvbuf(stdout, NULL, _IONBF, 0))
1708 MessageBox(GetFocus(), "setvbuf stdout", NULL, MB_OK);
1710 hCursor
= SetCursor(LoadCursor(NULL
, IDC_WAIT
));
1714 hPython
= LoadLibrary(pythondll
);
1717 result
= run_installscript(hPython
, fname
, 2, argv
);
1719 fprintf(stderr
, "*** run_installscript: internal error 0x%X ***\n", result
);
1721 FreeLibrary(hPython
);
1723 fprintf(stderr
, "*** Could not load Python ***");
1728 fp
= fopen(tempname
, "rb");
1729 n
= fread(buffer
, 1, sizeof(buffer
), fp
);
1735 SetDlgItemText(hwnd
, IDC_INFO
, buffer
);
1736 SetDlgItemText(hwnd
, IDC_TITLE
,
1737 "Postinstall script finished.\n"
1738 "Click the Finish button to exit the Setup wizard.");
1747 lpnm
= (LPNMHDR
) lParam
;
1749 switch (lpnm
->code
) {
1750 case PSN_SETACTIVE
: /* Enable the Finish button */
1751 PropSheet_SetWizButtons(GetParent(hwnd
), PSWIZB_FINISH
);
1770 void RunWizard(HWND hwnd
)
1772 PROPSHEETPAGE psp
= {0};
1773 HPROPSHEETPAGE ahpsp
[4] = {0};
1774 PROPSHEETHEADER psh
= {0};
1776 /* Display module information */
1777 psp
.dwSize
= sizeof(psp
);
1778 psp
.dwFlags
= PSP_DEFAULT
|PSP_HIDEHEADER
;
1779 psp
.hInstance
= GetModuleHandle (NULL
);
1781 psp
.pfnDlgProc
= IntroDlgProc
;
1782 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_INTRO
);
1784 ahpsp
[0] = CreatePropertySheetPage(&psp
);
1786 /* Select python version to use */
1787 psp
.dwFlags
= PSP_DEFAULT
|PSP_HIDEHEADER
;
1788 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_SELECTPYTHON
);
1789 psp
.pfnDlgProc
= SelectPythonDlgProc
;
1791 ahpsp
[1] = CreatePropertySheetPage(&psp
);
1793 /* Install the files */
1794 psp
.dwFlags
= PSP_DEFAULT
|PSP_HIDEHEADER
;
1795 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_INSTALLFILES
);
1796 psp
.pfnDlgProc
= InstallFilesDlgProc
;
1798 ahpsp
[2] = CreatePropertySheetPage(&psp
);
1800 /* Show success or failure */
1801 psp
.dwFlags
= PSP_DEFAULT
|PSP_HIDEHEADER
;
1802 psp
.pszTemplate
= MAKEINTRESOURCE(IDD_FINISHED
);
1803 psp
.pfnDlgProc
= FinishedDlgProc
;
1805 ahpsp
[3] = CreatePropertySheetPage(&psp
);
1807 /* Create the property sheet */
1808 psh
.dwSize
= sizeof(psh
);
1809 psh
.hInstance
= GetModuleHandle(NULL
);
1810 psh
.hwndParent
= hwnd
;
1812 psh
.dwFlags
= PSH_WIZARD
/*97*//*|PSH_WATERMARK|PSH_HEADER*/;
1813 psh
.pszbmWatermark
= NULL
;
1814 psh
.pszbmHeader
= NULL
;
1818 PropertySheet(&psh
);
1823 char ini_buffer
[4096];
1825 /* Read installation information */
1826 GetPrivateProfileString("Setup", "title", "", ini_buffer
,
1827 sizeof(ini_buffer
), ini_file
);
1828 unescape(title
, ini_buffer
, sizeof(title
));
1830 GetPrivateProfileString("Setup", "info", "", ini_buffer
,
1831 sizeof(ini_buffer
), ini_file
);
1832 unescape(info
, ini_buffer
, sizeof(info
));
1834 GetPrivateProfileString("Setup", "build_info", "", build_info
,
1835 sizeof(build_info
), ini_file
);
1837 pyc_compile
= GetPrivateProfileInt("Setup", "target_compile", 1,
1839 pyo_compile
= GetPrivateProfileInt("Setup", "target_optimize", 1,
1842 GetPrivateProfileString("Setup", "target_version", "",
1843 target_version
, sizeof(target_version
),
1846 GetPrivateProfileString("metadata", "name", "",
1847 meta_name
, sizeof(meta_name
),
1850 GetPrivateProfileString("Setup", "install_script", "",
1851 install_script
, sizeof(install_script
),
1855 hwndMain
= CreateBackground(title
);
1857 RunWizard(hwndMain
);
1860 UnmapViewOfFile(arc_data
);
1862 DeleteFile(ini_file
);
1865 DeleteObject(hBitmap
);
1870 /*********************** uninstall section ******************************/
1872 static int compare(const void *p1
, const void *p2
)
1874 return strcmp(*(char **)p2
, *(char **)p1
);
1878 * Commit suicide (remove the uninstaller itself).
1880 * Create a batch file to first remove the uninstaller
1881 * (will succeed after it has finished), then the batch file itself.
1883 * This technique has been demonstrated by Jeff Richter,
1886 void remove_exe(void)
1888 char exename
[_MAX_PATH
];
1889 char batname
[_MAX_PATH
];
1892 PROCESS_INFORMATION pi
;
1894 GetModuleFileName(NULL
, exename
, sizeof(exename
));
1895 sprintf(batname
, "%s.bat", exename
);
1896 fp
= fopen(batname
, "w");
1897 fprintf(fp
, ":Repeat\n");
1898 fprintf(fp
, "del \"%s\"\n", exename
);
1899 fprintf(fp
, "if exist \"%s\" goto Repeat\n", exename
);
1900 fprintf(fp
, "del \"%s\"\n", batname
);
1903 ZeroMemory(&si
, sizeof(si
));
1905 si
.dwFlags
= STARTF_USESHOWWINDOW
;
1906 si
.wShowWindow
= SW_HIDE
;
1907 if (CreateProcess(NULL
,
1912 CREATE_SUSPENDED
| IDLE_PRIORITY_CLASS
,
1917 SetThreadPriority(pi
.hThread
, THREAD_PRIORITY_IDLE
);
1918 SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL
);
1919 SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS
);
1920 CloseHandle(pi
.hProcess
);
1921 ResumeThread(pi
.hThread
);
1922 CloseHandle(pi
.hThread
);
1926 void DeleteRegistryKey(char *string
)
1935 line
= strdup(string
); /* so we can change it */
1937 keyname
= strchr(line
, '[');
1942 subkeyname
= strchr(keyname
, ']');
1946 delim
= strchr(subkeyname
, '\n');
1950 result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
1956 if (result
!= ERROR_SUCCESS
)
1957 MessageBox(GetFocus(), string
, "Could not open key", MB_OK
);
1959 result
= RegDeleteKey(hKey
, subkeyname
);
1960 if (result
!= ERROR_SUCCESS
)
1961 MessageBox(GetFocus(), string
, "Could not delete key", MB_OK
);
1967 void DeleteRegistryValue(char *string
)
1976 line
= strdup(string
); /* so we can change it */
1978 /* Format is 'Reg DB Value: [key]name=value' */
1979 keyname
= strchr(line
, '[');
1983 valuename
= strchr(keyname
, ']');
1986 *valuename
++ = '\0';
1987 value
= strchr(valuename
, '=');
1993 result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
1998 if (result
!= ERROR_SUCCESS
)
1999 MessageBox(GetFocus(), string
, "Could not open key", MB_OK
);
2001 result
= RegDeleteValue(hKey
, valuename
);
2002 if (result
!= ERROR_SUCCESS
)
2003 MessageBox(GetFocus(), string
, "Could not delete value", MB_OK
);
2009 BOOL
MyDeleteFile(char *line
)
2011 char *pathname
= strchr(line
, ':');
2015 while (isspace(*pathname
))
2017 return DeleteFile(pathname
);
2020 BOOL
MyRemoveDirectory(char *line
)
2022 char *pathname
= strchr(line
, ':');
2026 while (isspace(*pathname
))
2028 return RemoveDirectory(pathname
);
2031 BOOL
Run_RemoveScript(char *line
)
2035 static char lastscript
[MAX_PATH
];
2037 /* Format is 'Run Scripts: [pythondll]scriptname' */
2038 /* XXX Currently, pythondll carries no path!!! */
2039 dllname
= strchr(line
, '[');
2043 scriptname
= strchr(dllname
, ']');
2046 *scriptname
++ = '\0';
2047 /* this function may be called more than one time with the same
2048 script, only run it one time */
2049 if (strcmp(lastscript
, scriptname
)) {
2051 char *argv
[3] = {NULL
, "-remove", NULL
};
2057 argv
[0] = scriptname
;
2059 tempname
= tmpnam(NULL
);
2061 if (!freopen(tempname
, "a", stderr
))
2062 MessageBox(GetFocus(), "freopen stderr", NULL
, MB_OK
);
2063 if (!freopen(tempname
, "a", stdout
))
2064 MessageBox(GetFocus(), "freopen stdout", NULL
, MB_OK
);
2066 hPython
= LoadLibrary(dllname
);
2068 if (0x80000000 == run_installscript(hPython
, scriptname
, 2, argv
))
2069 fprintf(stderr
, "*** Could not load Python ***");
2070 FreeLibrary(hPython
);
2076 fp
= fopen(tempname
, "rb");
2077 n
= fread(buffer
, 1, sizeof(buffer
), fp
);
2083 MessageBox(GetFocus(), buffer
, "uninstall-script", MB_OK
);
2085 strcpy(lastscript
, scriptname
);
2090 int DoUninstall(int argc
, char **argv
)
2101 int lines_buffer_size
= 10;
2105 "Wrong number of args",
2108 return 1; /* Error */
2110 if (strcmp(argv
[1], "-u")) {
2115 return 1; /* Error */
2121 static char KeyName
[] =
2122 "Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
2124 result
= RegOpenKeyEx(HKEY_LOCAL_MACHINE
,
2129 if (result
== ERROR_ACCESS_DENIED
) {
2130 MessageBox(GetFocus(),
2131 "You do not seem to have sufficient access rights\n"
2132 "on this machine to uninstall this software",
2134 MB_OK
| MB_ICONSTOP
);
2135 return 1; /* Error */
2140 logfile
= fopen(argv
[2], "r");
2143 "could not open logfile",
2146 return 1; /* Error */
2149 lines
= (char **)malloc(sizeof(char *) * lines_buffer_size
);
2151 return SystemError(0, "Out of memory");
2153 /* Read the whole logfile, realloacting the buffer */
2154 while (fgets(buffer
, sizeof(buffer
), logfile
)) {
2155 int len
= strlen(buffer
);
2156 /* remove trailing white space */
2157 while (isspace(buffer
[len
-1]))
2160 lines
[nLines
++] = strdup(buffer
);
2161 if (nLines
>= lines_buffer_size
) {
2162 lines_buffer_size
+= 10;
2163 lines
= (char **)realloc(lines
,
2164 sizeof(char *) * lines_buffer_size
);
2166 return SystemError(0, "Out of memory");
2171 /* Sort all the lines, so that highest 3-digit codes are first */
2172 qsort(&lines
[0], nLines
, sizeof(char *),
2175 if (IDYES
!= MessageBox(NULL
,
2176 "Are you sure you want to remove\n"
2177 "this package from your computer?",
2179 MB_YESNO
| MB_ICONQUESTION
))
2183 for (i
= 0; i
< nLines
; ++i
) {
2184 /* Ignore duplicate lines */
2185 if (strcmp(cp
, lines
[i
])) {
2188 /* Parse the lines */
2189 if (2 == sscanf(cp
, "%d Made Dir: %s", &ign
, &buffer
)) {
2190 if (MyRemoveDirectory(cp
))
2193 int code
= GetLastError();
2194 if (code
!= 2 && code
!= 3) { /* file or path not found */
2198 } else if (2 == sscanf(cp
, "%d File Copy: %s", &ign
, &buffer
)) {
2199 if (MyDeleteFile(cp
))
2202 int code
= GetLastError();
2203 if (code
!= 2 && code
!= 3) { /* file or path not found */
2207 } else if (2 == sscanf(cp
, "%d File Overwrite: %s", &ign
, &buffer
)) {
2208 if (MyDeleteFile(cp
))
2211 int code
= GetLastError();
2212 if (code
!= 2 && code
!= 3) { /* file or path not found */
2216 } else if (2 == sscanf(cp
, "%d Reg DB Key: %s", &ign
, &buffer
)) {
2217 DeleteRegistryKey(cp
);
2218 } else if (2 == sscanf(cp
, "%d Reg DB Value: %s", &ign
, &buffer
)) {
2219 DeleteRegistryValue(cp
);
2220 } else if (2 == sscanf(cp
, "%d Run Script: %s", &ign
, &buffer
)) {
2221 Run_RemoveScript(cp
);
2226 if (DeleteFile(argv
[2])) {
2230 SystemError(GetLastError(), argv
[2]);
2234 "%d files and %d directories removed\n"
2235 "%d files or directories could not be removed",
2236 nFiles
, nDirs
, nErrors
);
2238 wsprintf(buffer
, "%d files and %d directories removed",
2240 MessageBox(NULL
, buffer
, "Uninstall Finished!",
2241 MB_OK
| MB_ICONINFORMATION
);
2246 int WINAPI
WinMain(HINSTANCE hInst
, HINSTANCE hPrevInst
,
2247 LPSTR lpszCmdLine
, INT nCmdShow
)
2250 extern char **__argv
;
2253 GetModuleFileName(NULL
, modulename
, sizeof(modulename
));
2255 /* Map the executable file to memory */
2256 arc_data
= MapExistingFile(modulename
, &arc_size
);
2258 SystemError(GetLastError(), "Could not open archive");
2262 /* OK. So this program can act as installer (self-extracting
2263 * zip-file, or as uninstaller when started with '-u logfile'
2264 * command line flags.
2266 * The installer is usually started without command line flags,
2267 * and the uninstaller is usually started with the '-u logfile'
2268 * flag. What to do if some innocent user double-clicks the
2270 * The following implements a defensive strategy...
2273 /* Try to extract the configuration data into a temporary file */
2274 ini_file
= ExtractIniFile(arc_data
, arc_size
, &exe_size
);
2279 if (!ini_file
&& __argc
> 1) {
2280 return DoUninstall(__argc
, __argv
);
2284 basename
= strrchr(modulename
, '\\');
2288 /* Last guess about the purpose of this program */
2289 if (basename
&& (0 == strncmp(basename
, "Remove", 6)))
2290 SystemError(0, "This program is normally started by windows");
2292 SystemError(0, "Setup program invalid or damaged");