##// END OF EJS Templates
exewrapper: find the proper python3X.dll in the registry...
Matt Harbison -
r48993:67d14d4e default
parent child Browse files
Show More
@@ -1,169 +1,303 b''
1 1 /*
2 2 exewrapper.c - wrapper for calling a python script on Windows
3 3
4 4 Copyright 2012 Adrian Buehlmann <adrian@cadifra.com> and others
5 5
6 6 This software may be used and distributed according to the terms of the
7 7 GNU General Public License version 2 or any later version.
8 8 */
9 9
10 10 #include <Python.h>
11 11 #include <stdio.h>
12 12 #include <tchar.h>
13 13 #include <windows.h>
14 14
15 15 #include "hgpythonlib.h"
16 16
17 17 #ifdef __GNUC__
18 18 int strcat_s(char *d, size_t n, const char *s)
19 19 {
20 20 return !strncat(d, s, n);
21 21 }
22 22 int strcpy_s(char *d, size_t n, const char *s)
23 23 {
24 24 return !strncpy(d, s, n);
25 25 }
26 26
27 27 #define _tcscpy_s strcpy_s
28 28 #define _tcscat_s strcat_s
29 29 #define _countof(array) (sizeof(array) / sizeof(array[0]))
30 30 #endif
31 31
32 #if PY_MAJOR_VERSION >= 3
33
34 #pragma comment(lib, "Advapi32.lib")
35
36 /* python.org installations */
37 #define CORE_PATH L"SOFTWARE\\Python\\PythonCore"
38
39 /* Microsoft Store installations */
40 #define LOOKASIDE_PATH \
41 L"SOFTWARE\\Microsoft\\AppModel\\Lookaside\\user\\Software\\Python\\" \
42 L"PythonCore"
43
44 static wchar_t *_locate_python_for_key(HKEY root, LPCWSTR subkey, size_t *size)
45 {
46 wchar_t installPathKey[512];
47 wchar_t *executable = NULL;
48 DWORD type;
49 DWORD sz = 0;
50 HKEY ip_key;
51 LSTATUS status;
52
53 _snwprintf_s(installPathKey, sizeof(installPathKey), _TRUNCATE,
54 L"%ls\\%d.%d\\InstallPath", subkey, PY_MAJOR_VERSION,
55 PY_MINOR_VERSION);
56
57 status =
58 RegOpenKeyExW(root, installPathKey, 0, KEY_QUERY_VALUE, &ip_key);
59
60 if (status != ERROR_SUCCESS)
61 return NULL;
62
63 status = RegQueryValueExW(ip_key, L"ExecutablePath", NULL, &type,
64 (LPBYTE)executable, &sz);
65 if (status == ERROR_SUCCESS) {
66 /* Allocate extra space so path\to\python.exe can be converted
67 * to path\to\python39.dll + NUL.
68 */
69 *size = sz + sizeof(_T(HGPYTHONLIB ".dll")) + sizeof(wchar_t);
70 executable = malloc(*size);
71
72 if (executable) {
73 status =
74 RegQueryValueExW(ip_key, L"ExecutablePath", NULL,
75 &type, (LPBYTE)executable, &sz);
76
77 if (status != ERROR_SUCCESS) {
78 free(executable);
79 executable = NULL;
80 } else {
81 /* Not all values are stored NUL terminated */
82 executable[sz] = L'\0';
83 }
84 }
85 }
86
87 RegCloseKey(ip_key);
88 return executable;
89 }
90
91 static HMODULE load_system_py3(void)
92 {
93 wchar_t *subkeys[] = {CORE_PATH, LOOKASIDE_PATH};
94 HKEY roots[] = {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER};
95
96 /* Give priority to python.org installs, because MS Store installs can
97 * break with user profile corruption, and also use some NTFS feature
98 * that MSYS doesn't understand.
99 */
100 for (int s = 0; s < _countof(subkeys); s++) {
101 for (int r = 0; r < _countof(roots); r++) {
102 size_t size = 0;
103 wchar_t *py =
104 _locate_python_for_key(roots[r], subkeys[s], &size);
105 wchar_t *cut = NULL;
106 HMODULE pydll;
107
108 if (!py) {
109 continue;
110 }
111
112 /* Cut off the python executable component */
113 cut = wcsrchr(py, L'\\');
114 if (cut == NULL) {
115 free(py);
116 continue;
117 }
118 *cut = 0;
119
120 wcscat_s(py, size, _T("\\" HGPYTHONLIB ".dll"));
121
122 pydll = LoadLibrary(py);
123
124 /* Also load python3.dll, so we don't pick up a random
125 * one on PATH. We don't search {sys.prefix}\DLLs like
126 * python.exe because this is commented as "not been a
127 * normal install layout for a while", and don't search
128 * LOAD_LIBRARY_SEARCH_APPLICATION_DIR because it's not
129 * clear what the use case is.
130 */
131 if (pydll != NULL) {
132 HANDLE py3dll = NULL;
133
134 *cut = 0;
135 wcscat_s(py, size, L"\\python3.dll");
136
137 py3dll = LoadLibrary(py);
138 if (py3dll == NULL) {
139 const char *err;
140 err = "failed to load python3.dll "
141 "for " HGPYTHONLIB ".dll";
142 fprintf(stderr, "abort: %s (0x%X)\n",
143 err, GetLastError());
144 exit(255);
145 }
146 }
147
148 free(py);
149
150 if (pydll != NULL) {
151 return pydll;
152 }
153 }
154 }
155
156 return NULL;
157 }
158 #endif
159
32 160 static TCHAR pyscript[MAX_PATH + 10];
33 161 static TCHAR pyhome[MAX_PATH + 10];
34 162 static TCHAR pydllfile[MAX_PATH + 10];
35 163
36 164 int _tmain(int argc, TCHAR *argv[])
37 165 {
38 166 TCHAR *p;
39 167 int ret;
40 168 int i;
41 169 int n;
42 170 TCHAR **pyargv;
43 171 WIN32_FIND_DATA fdata;
44 172 HANDLE hfind;
45 173 const char *err;
46 174 HMODULE pydll;
47 175 void(__cdecl * Py_SetPythonHome)(TCHAR * home);
48 176 int(__cdecl * Py_Main)(int argc, TCHAR *argv[]);
49 177
50 178 #if PY_MAJOR_VERSION >= 3
51 179 _wputenv(L"PYTHONLEGACYWINDOWSSTDIO=1");
52 180 #endif
53 181
54 182 if (GetModuleFileName(NULL, pyscript, _countof(pyscript)) == 0) {
55 183 err = "GetModuleFileName failed";
56 184 goto bail;
57 185 }
58 186
59 187 p = _tcsrchr(pyscript, '.');
60 188 if (p == NULL) {
61 189 err = "malformed module filename";
62 190 goto bail;
63 191 }
64 192 *p = 0; /* cut trailing ".exe" */
65 193 _tcscpy_s(pyhome, _countof(pyhome), pyscript);
66 194
67 195 hfind = FindFirstFile(pyscript, &fdata);
68 196 if (hfind != INVALID_HANDLE_VALUE) {
69 197 /* pyscript exists, close handle */
70 198 FindClose(hfind);
71 199 } else {
72 200 /* file pyscript isn't there, take <pyscript>exe.py */
73 201 _tcscat_s(pyscript, _countof(pyscript), _T("exe.py"));
74 202 }
75 203
76 204 pydll = NULL;
77 205
78 206 p = _tcsrchr(pyhome, _T('\\'));
79 207 if (p == NULL) {
80 208 err = "can't find backslash in module filename";
81 209 goto bail;
82 210 }
83 211 *p = 0; /* cut at directory */
84 212
85 213 /* check for private Python of HackableMercurial */
86 214 _tcscat_s(pyhome, _countof(pyhome), _T("\\hg-python"));
87 215
88 216 hfind = FindFirstFile(pyhome, &fdata);
89 217 if (hfind != INVALID_HANDLE_VALUE) {
90 218 /* Path .\hg-python exists. We are probably in HackableMercurial
91 219 scenario, so let's load python dll from this dir. */
92 220 FindClose(hfind);
93 221 _tcscpy_s(pydllfile, _countof(pydllfile), pyhome);
94 222 _tcscat_s(pydllfile, _countof(pydllfile),
95 223 _T("\\") _T(HGPYTHONLIB) _T(".dll"));
96 224 pydll = LoadLibrary(pydllfile);
97 225 if (pydll == NULL) {
98 226 err = "failed to load private Python DLL " HGPYTHONLIB
99 227 ".dll";
100 228 goto bail;
101 229 }
102 230 Py_SetPythonHome =
103 231 (void *)GetProcAddress(pydll, "Py_SetPythonHome");
104 232 if (Py_SetPythonHome == NULL) {
105 233 err = "failed to get Py_SetPythonHome";
106 234 goto bail;
107 235 }
108 236 Py_SetPythonHome(pyhome);
109 237 }
110 238
239 #if PY_MAJOR_VERSION >= 3
240 if (pydll == NULL) {
241 pydll = load_system_py3();
242 }
243 #endif
244
111 245 if (pydll == NULL) {
112 246 pydll = LoadLibrary(_T(HGPYTHONLIB) _T(".dll"));
113 247 if (pydll == NULL) {
114 248 err = "failed to load Python DLL " HGPYTHONLIB ".dll";
115 249 goto bail;
116 250 }
117 251 }
118 252
119 253 Py_Main = (void *)GetProcAddress(pydll, "Py_Main");
120 254 if (Py_Main == NULL) {
121 255 err = "failed to get Py_Main";
122 256 goto bail;
123 257 }
124 258
125 259 /*
126 260 Only add the pyscript to the args, if it's not already there. It may
127 261 already be there, if the script spawned a child process of itself, in
128 262 the same way as it got called, that is, with the pyscript already in
129 263 place. So we optionally accept the pyscript as the first argument
130 264 (argv[1]), letting our exe taking the role of the python interpreter.
131 265 */
132 266 if (argc >= 2 && _tcscmp(argv[1], pyscript) == 0) {
133 267 /*
134 268 pyscript is already in the args, so there is no need to copy
135 269 the args and we can directly call the python interpreter with
136 270 the original args.
137 271 */
138 272 return Py_Main(argc, argv);
139 273 }
140 274
141 275 /*
142 276 Start assembling the args for the Python interpreter call. We put the
143 277 name of our exe (argv[0]) in the position where the python.exe
144 278 canonically is, and insert the pyscript next.
145 279 */
146 280 pyargv = malloc((argc + 5) * sizeof(TCHAR *));
147 281 if (pyargv == NULL) {
148 282 err = "not enough memory";
149 283 goto bail;
150 284 }
151 285 n = 0;
152 286 pyargv[n++] = argv[0];
153 287 pyargv[n++] = pyscript;
154 288
155 289 /* copy remaining args from the command line */
156 290 for (i = 1; i < argc; i++)
157 291 pyargv[n++] = argv[i];
158 292 /* argv[argc] is guaranteed to be NULL, so we forward that guarantee */
159 293 pyargv[n] = NULL;
160 294
161 295 ret = Py_Main(n, pyargv); /* The Python interpreter call */
162 296
163 297 free(pyargv);
164 298 return ret;
165 299
166 300 bail:
167 301 fprintf(stderr, "abort: %s\n", err);
168 302 return 255;
169 303 }
General Comments 0
You need to be logged in to leave comments. Login now