1 /* Main program when embedded in a UWP application on Windows */
2
3 #include "Python.h"
4 #include <string.h>
5
6 #define WIN32_LEAN_AND_MEAN
7 #include <Windows.h>
8 #include <shellapi.h>
9 #include <shlobj.h>
10
11 #include <string>
12
13 #include <appmodel.h>
14 #include <winrt\Windows.ApplicationModel.h>
15 #include <winrt\Windows.Storage.h>
16
17 #ifdef PYTHONW
18 #ifdef _DEBUG
19 const wchar_t *PROGNAME = L"pythonw_d.exe";
20 #else
21 const wchar_t *PROGNAME = L"pythonw.exe";
22 #endif
23 #else
24 #ifdef _DEBUG
25 const wchar_t *PROGNAME = L"python_d.exe";
26 #else
27 const wchar_t *PROGNAME = L"python.exe";
28 #endif
29 #endif
30
31 static std::wstring
get_package_family()32 get_package_family()
33 {
34 try {
35 UINT32 nameLength = MAX_PATH;
36 std::wstring name;
37 name.resize(nameLength);
38 DWORD rc = GetCurrentPackageFamilyName(&nameLength, name.data());
39 if (rc == ERROR_SUCCESS) {
40 name.resize(nameLength - 1);
41 return name;
42 }
43 else if (rc != ERROR_INSUFFICIENT_BUFFER) {
44 throw rc;
45 }
46 name.resize(nameLength);
47 rc = GetCurrentPackageFamilyName(&nameLength, name.data());
48 if (rc != ERROR_SUCCESS) {
49 throw rc;
50 }
51 name.resize(nameLength - 1);
52 return name;
53 }
54 catch (...) {
55 }
56
57 return std::wstring();
58 }
59
60 static std::wstring
get_user_base()61 get_user_base()
62 {
63 try {
64 const auto appData = winrt::Windows::Storage::ApplicationData::Current();
65 if (appData) {
66 const auto localCache = appData.LocalCacheFolder();
67 if (localCache) {
68 std::wstring path { localCache.Path().c_str() };
69 if (!path.empty()) {
70 return path + L"\\local-packages";
71 }
72 }
73 }
74 } catch (...) {
75 }
76
77 return std::wstring();
78 }
79
80 static std::wstring
get_package_home()81 get_package_home()
82 {
83 try {
84 UINT32 pathLength = MAX_PATH;
85 std::wstring path;
86 path.resize(pathLength);
87 DWORD rc = GetCurrentPackagePath(&pathLength, path.data());
88 if (rc == ERROR_SUCCESS) {
89 path.resize(pathLength - 1);
90 return path;
91 }
92 else if (rc != ERROR_INSUFFICIENT_BUFFER) {
93 throw rc;
94 }
95 path.resize(pathLength);
96 rc = GetCurrentPackagePath(&pathLength, path.data());
97 if (rc != ERROR_SUCCESS) {
98 throw rc;
99 }
100 path.resize(pathLength - 1);
101 return path;
102 }
103 catch (...) {
104 }
105
106 return std::wstring();
107 }
108
109 static PyStatus
set_process_name(PyConfig * config)110 set_process_name(PyConfig *config)
111 {
112 PyStatus status = PyStatus_Ok();
113 std::wstring executable;
114
115 const auto home = get_package_home();
116 const auto family = get_package_family();
117
118 if (!family.empty()) {
119 PWSTR localAppData;
120 if (SUCCEEDED(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0,
121 NULL, &localAppData))) {
122 executable = std::wstring(localAppData)
123 + L"\\Microsoft\\WindowsApps\\"
124 + family
125 + L"\\"
126 + PROGNAME;
127
128 CoTaskMemFree(localAppData);
129 }
130 }
131
132 /* Only use module filename if we don't have a home */
133 if (home.empty() && executable.empty()) {
134 executable.resize(MAX_PATH);
135 while (true) {
136 DWORD len = GetModuleFileNameW(
137 NULL, executable.data(), (DWORD)executable.size());
138 if (len == 0) {
139 executable.clear();
140 break;
141 } else if (len == executable.size() &&
142 GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
143 executable.resize(len * 2);
144 } else {
145 executable.resize(len);
146 break;
147 }
148 }
149 size_t i = executable.find_last_of(L"/\\");
150 if (i == std::wstring::npos) {
151 executable = PROGNAME;
152 } else {
153 executable.replace(i + 1, std::wstring::npos, PROGNAME);
154 }
155 }
156
157 if (!home.empty()) {
158 status = PyConfig_SetString(config, &config->home, home.c_str());
159 if (PyStatus_Exception(status)) {
160 return status;
161 }
162 }
163
164 const wchar_t *launcherPath = _wgetenv(L"__PYVENV_LAUNCHER__");
165 if (launcherPath) {
166 if (!executable.empty()) {
167 status = PyConfig_SetString(config, &config->base_executable,
168 executable.c_str());
169 if (PyStatus_Exception(status)) {
170 return status;
171 }
172 }
173
174 status = PyConfig_SetString(
175 config, &config->executable, launcherPath);
176
177 /* bpo-35873: Clear the environment variable to avoid it being
178 * inherited by child processes. */
179 _wputenv_s(L"__PYVENV_LAUNCHER__", L"");
180 } else if (!executable.empty()) {
181 status = PyConfig_SetString(
182 config, &config->executable, executable.c_str());
183 }
184
185 return status;
186 }
187
188 int
wmain(int argc,wchar_t ** argv)189 wmain(int argc, wchar_t **argv)
190 {
191 PyStatus status;
192 PyPreConfig preconfig;
193 PyConfig config;
194
195 const wchar_t *moduleName = NULL;
196 const wchar_t *p = wcsrchr(argv[0], L'\\');
197 if (!p) {
198 p = argv[0];
199 }
200 if (p) {
201 if (*p == L'\\') {
202 p++;
203 }
204
205 if (wcsnicmp(p, L"pip", 3) == 0) {
206 moduleName = L"pip";
207 } else if (wcsnicmp(p, L"idle", 4) == 0) {
208 moduleName = L"idlelib";
209 }
210 }
211
212 PyPreConfig_InitPythonConfig(&preconfig);
213 if (!moduleName) {
214 status = Py_PreInitializeFromArgs(&preconfig, argc, argv);
215 if (PyStatus_Exception(status)) {
216 goto fail_without_config;
217 }
218 }
219
220 PyConfig_InitPythonConfig(&config);
221
222 status = PyConfig_SetArgv(&config, argc, argv);
223 if (PyStatus_Exception(status)) {
224 goto fail;
225 }
226 if (moduleName) {
227 config.parse_argv = 0;
228 }
229
230 status = set_process_name(&config);
231 if (PyStatus_Exception(status)) {
232 goto fail;
233 }
234
235 p = _wgetenv(L"PYTHONUSERBASE");
236 if (!p || !*p) {
237 _wputenv_s(L"PYTHONUSERBASE", get_user_base().c_str());
238 }
239
240 if (moduleName) {
241 status = PyConfig_SetString(&config, &config.run_module, moduleName);
242 if (PyStatus_Exception(status)) {
243 goto fail;
244 }
245 status = PyConfig_SetString(&config, &config.run_filename, NULL);
246 if (PyStatus_Exception(status)) {
247 goto fail;
248 }
249 status = PyConfig_SetString(&config, &config.run_command, NULL);
250 if (PyStatus_Exception(status)) {
251 goto fail;
252 }
253 }
254
255 status = Py_InitializeFromConfig(&config);
256 if (PyStatus_Exception(status)) {
257 goto fail;
258 }
259 PyConfig_Clear(&config);
260
261 return Py_RunMain();
262
263 fail:
264 PyConfig_Clear(&config);
265 fail_without_config:
266 if (PyStatus_IsExit(status)) {
267 return status.exitcode;
268 }
269 assert(PyStatus_Exception(status));
270 Py_ExitStatusException(status);
271 /* Unreachable code */
272 return 0;
273 }
274
275 #ifdef PYTHONW
276
wWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPWSTR lpCmdLine,int nCmdShow)277 int WINAPI wWinMain(
278 HINSTANCE hInstance, /* handle to current instance */
279 HINSTANCE hPrevInstance, /* handle to previous instance */
280 LPWSTR lpCmdLine, /* pointer to command line */
281 int nCmdShow /* show state of window */
282 )
283 {
284 return wmain(__argc, __wargv);
285 }
286
287 #endif
288