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