##// 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 exewrapper.c - wrapper for calling a python script on Windows
2 exewrapper.c - wrapper for calling a python script on Windows
3
3
4 Copyright 2012 Adrian Buehlmann <adrian@cadifra.com> and others
4 Copyright 2012 Adrian Buehlmann <adrian@cadifra.com> and others
5
5
6 This software may be used and distributed according to the terms of the
6 This software may be used and distributed according to the terms of the
7 GNU General Public License version 2 or any later version.
7 GNU General Public License version 2 or any later version.
8 */
8 */
9
9
10 #include <Python.h>
10 #include <Python.h>
11 #include <stdio.h>
11 #include <stdio.h>
12 #include <tchar.h>
12 #include <tchar.h>
13 #include <windows.h>
13 #include <windows.h>
14
14
15 #include "hgpythonlib.h"
15 #include "hgpythonlib.h"
16
16
17 #ifdef __GNUC__
17 #ifdef __GNUC__
18 int strcat_s(char *d, size_t n, const char *s)
18 int strcat_s(char *d, size_t n, const char *s)
19 {
19 {
20 return !strncat(d, s, n);
20 return !strncat(d, s, n);
21 }
21 }
22 int strcpy_s(char *d, size_t n, const char *s)
22 int strcpy_s(char *d, size_t n, const char *s)
23 {
23 {
24 return !strncpy(d, s, n);
24 return !strncpy(d, s, n);
25 }
25 }
26
26
27 #define _tcscpy_s strcpy_s
27 #define _tcscpy_s strcpy_s
28 #define _tcscat_s strcat_s
28 #define _tcscat_s strcat_s
29 #define _countof(array) (sizeof(array) / sizeof(array[0]))
29 #define _countof(array) (sizeof(array) / sizeof(array[0]))
30 #endif
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 static TCHAR pyscript[MAX_PATH + 10];
160 static TCHAR pyscript[MAX_PATH + 10];
33 static TCHAR pyhome[MAX_PATH + 10];
161 static TCHAR pyhome[MAX_PATH + 10];
34 static TCHAR pydllfile[MAX_PATH + 10];
162 static TCHAR pydllfile[MAX_PATH + 10];
35
163
36 int _tmain(int argc, TCHAR *argv[])
164 int _tmain(int argc, TCHAR *argv[])
37 {
165 {
38 TCHAR *p;
166 TCHAR *p;
39 int ret;
167 int ret;
40 int i;
168 int i;
41 int n;
169 int n;
42 TCHAR **pyargv;
170 TCHAR **pyargv;
43 WIN32_FIND_DATA fdata;
171 WIN32_FIND_DATA fdata;
44 HANDLE hfind;
172 HANDLE hfind;
45 const char *err;
173 const char *err;
46 HMODULE pydll;
174 HMODULE pydll;
47 void(__cdecl * Py_SetPythonHome)(TCHAR * home);
175 void(__cdecl * Py_SetPythonHome)(TCHAR * home);
48 int(__cdecl * Py_Main)(int argc, TCHAR *argv[]);
176 int(__cdecl * Py_Main)(int argc, TCHAR *argv[]);
49
177
50 #if PY_MAJOR_VERSION >= 3
178 #if PY_MAJOR_VERSION >= 3
51 _wputenv(L"PYTHONLEGACYWINDOWSSTDIO=1");
179 _wputenv(L"PYTHONLEGACYWINDOWSSTDIO=1");
52 #endif
180 #endif
53
181
54 if (GetModuleFileName(NULL, pyscript, _countof(pyscript)) == 0) {
182 if (GetModuleFileName(NULL, pyscript, _countof(pyscript)) == 0) {
55 err = "GetModuleFileName failed";
183 err = "GetModuleFileName failed";
56 goto bail;
184 goto bail;
57 }
185 }
58
186
59 p = _tcsrchr(pyscript, '.');
187 p = _tcsrchr(pyscript, '.');
60 if (p == NULL) {
188 if (p == NULL) {
61 err = "malformed module filename";
189 err = "malformed module filename";
62 goto bail;
190 goto bail;
63 }
191 }
64 *p = 0; /* cut trailing ".exe" */
192 *p = 0; /* cut trailing ".exe" */
65 _tcscpy_s(pyhome, _countof(pyhome), pyscript);
193 _tcscpy_s(pyhome, _countof(pyhome), pyscript);
66
194
67 hfind = FindFirstFile(pyscript, &fdata);
195 hfind = FindFirstFile(pyscript, &fdata);
68 if (hfind != INVALID_HANDLE_VALUE) {
196 if (hfind != INVALID_HANDLE_VALUE) {
69 /* pyscript exists, close handle */
197 /* pyscript exists, close handle */
70 FindClose(hfind);
198 FindClose(hfind);
71 } else {
199 } else {
72 /* file pyscript isn't there, take <pyscript>exe.py */
200 /* file pyscript isn't there, take <pyscript>exe.py */
73 _tcscat_s(pyscript, _countof(pyscript), _T("exe.py"));
201 _tcscat_s(pyscript, _countof(pyscript), _T("exe.py"));
74 }
202 }
75
203
76 pydll = NULL;
204 pydll = NULL;
77
205
78 p = _tcsrchr(pyhome, _T('\\'));
206 p = _tcsrchr(pyhome, _T('\\'));
79 if (p == NULL) {
207 if (p == NULL) {
80 err = "can't find backslash in module filename";
208 err = "can't find backslash in module filename";
81 goto bail;
209 goto bail;
82 }
210 }
83 *p = 0; /* cut at directory */
211 *p = 0; /* cut at directory */
84
212
85 /* check for private Python of HackableMercurial */
213 /* check for private Python of HackableMercurial */
86 _tcscat_s(pyhome, _countof(pyhome), _T("\\hg-python"));
214 _tcscat_s(pyhome, _countof(pyhome), _T("\\hg-python"));
87
215
88 hfind = FindFirstFile(pyhome, &fdata);
216 hfind = FindFirstFile(pyhome, &fdata);
89 if (hfind != INVALID_HANDLE_VALUE) {
217 if (hfind != INVALID_HANDLE_VALUE) {
90 /* Path .\hg-python exists. We are probably in HackableMercurial
218 /* Path .\hg-python exists. We are probably in HackableMercurial
91 scenario, so let's load python dll from this dir. */
219 scenario, so let's load python dll from this dir. */
92 FindClose(hfind);
220 FindClose(hfind);
93 _tcscpy_s(pydllfile, _countof(pydllfile), pyhome);
221 _tcscpy_s(pydllfile, _countof(pydllfile), pyhome);
94 _tcscat_s(pydllfile, _countof(pydllfile),
222 _tcscat_s(pydllfile, _countof(pydllfile),
95 _T("\\") _T(HGPYTHONLIB) _T(".dll"));
223 _T("\\") _T(HGPYTHONLIB) _T(".dll"));
96 pydll = LoadLibrary(pydllfile);
224 pydll = LoadLibrary(pydllfile);
97 if (pydll == NULL) {
225 if (pydll == NULL) {
98 err = "failed to load private Python DLL " HGPYTHONLIB
226 err = "failed to load private Python DLL " HGPYTHONLIB
99 ".dll";
227 ".dll";
100 goto bail;
228 goto bail;
101 }
229 }
102 Py_SetPythonHome =
230 Py_SetPythonHome =
103 (void *)GetProcAddress(pydll, "Py_SetPythonHome");
231 (void *)GetProcAddress(pydll, "Py_SetPythonHome");
104 if (Py_SetPythonHome == NULL) {
232 if (Py_SetPythonHome == NULL) {
105 err = "failed to get Py_SetPythonHome";
233 err = "failed to get Py_SetPythonHome";
106 goto bail;
234 goto bail;
107 }
235 }
108 Py_SetPythonHome(pyhome);
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 if (pydll == NULL) {
245 if (pydll == NULL) {
112 pydll = LoadLibrary(_T(HGPYTHONLIB) _T(".dll"));
246 pydll = LoadLibrary(_T(HGPYTHONLIB) _T(".dll"));
113 if (pydll == NULL) {
247 if (pydll == NULL) {
114 err = "failed to load Python DLL " HGPYTHONLIB ".dll";
248 err = "failed to load Python DLL " HGPYTHONLIB ".dll";
115 goto bail;
249 goto bail;
116 }
250 }
117 }
251 }
118
252
119 Py_Main = (void *)GetProcAddress(pydll, "Py_Main");
253 Py_Main = (void *)GetProcAddress(pydll, "Py_Main");
120 if (Py_Main == NULL) {
254 if (Py_Main == NULL) {
121 err = "failed to get Py_Main";
255 err = "failed to get Py_Main";
122 goto bail;
256 goto bail;
123 }
257 }
124
258
125 /*
259 /*
126 Only add the pyscript to the args, if it's not already there. It may
260 Only add the pyscript to the args, if it's not already there. It may
127 already be there, if the script spawned a child process of itself, in
261 already be there, if the script spawned a child process of itself, in
128 the same way as it got called, that is, with the pyscript already in
262 the same way as it got called, that is, with the pyscript already in
129 place. So we optionally accept the pyscript as the first argument
263 place. So we optionally accept the pyscript as the first argument
130 (argv[1]), letting our exe taking the role of the python interpreter.
264 (argv[1]), letting our exe taking the role of the python interpreter.
131 */
265 */
132 if (argc >= 2 && _tcscmp(argv[1], pyscript) == 0) {
266 if (argc >= 2 && _tcscmp(argv[1], pyscript) == 0) {
133 /*
267 /*
134 pyscript is already in the args, so there is no need to copy
268 pyscript is already in the args, so there is no need to copy
135 the args and we can directly call the python interpreter with
269 the args and we can directly call the python interpreter with
136 the original args.
270 the original args.
137 */
271 */
138 return Py_Main(argc, argv);
272 return Py_Main(argc, argv);
139 }
273 }
140
274
141 /*
275 /*
142 Start assembling the args for the Python interpreter call. We put the
276 Start assembling the args for the Python interpreter call. We put the
143 name of our exe (argv[0]) in the position where the python.exe
277 name of our exe (argv[0]) in the position where the python.exe
144 canonically is, and insert the pyscript next.
278 canonically is, and insert the pyscript next.
145 */
279 */
146 pyargv = malloc((argc + 5) * sizeof(TCHAR *));
280 pyargv = malloc((argc + 5) * sizeof(TCHAR *));
147 if (pyargv == NULL) {
281 if (pyargv == NULL) {
148 err = "not enough memory";
282 err = "not enough memory";
149 goto bail;
283 goto bail;
150 }
284 }
151 n = 0;
285 n = 0;
152 pyargv[n++] = argv[0];
286 pyargv[n++] = argv[0];
153 pyargv[n++] = pyscript;
287 pyargv[n++] = pyscript;
154
288
155 /* copy remaining args from the command line */
289 /* copy remaining args from the command line */
156 for (i = 1; i < argc; i++)
290 for (i = 1; i < argc; i++)
157 pyargv[n++] = argv[i];
291 pyargv[n++] = argv[i];
158 /* argv[argc] is guaranteed to be NULL, so we forward that guarantee */
292 /* argv[argc] is guaranteed to be NULL, so we forward that guarantee */
159 pyargv[n] = NULL;
293 pyargv[n] = NULL;
160
294
161 ret = Py_Main(n, pyargv); /* The Python interpreter call */
295 ret = Py_Main(n, pyargv); /* The Python interpreter call */
162
296
163 free(pyargv);
297 free(pyargv);
164 return ret;
298 return ret;
165
299
166 bail:
300 bail:
167 fprintf(stderr, "abort: %s\n", err);
301 fprintf(stderr, "abort: %s\n", err);
168 return 255;
302 return 255;
169 }
303 }
General Comments 0
You need to be logged in to leave comments. Login now