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