xref: /third_party/python/PC/python_uwp.cpp (revision 7db96d56)
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
19const wchar_t *PROGNAME = L"pythonw_d.exe";
20#else
21const wchar_t *PROGNAME = L"pythonw.exe";
22#endif
23#else
24#ifdef _DEBUG
25const wchar_t *PROGNAME = L"python_d.exe";
26#else
27const wchar_t *PROGNAME = L"python.exe";
28#endif
29#endif
30
31static std::wstring
32get_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
60static std::wstring
61get_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
80static std::wstring
81get_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
109static PyStatus
110set_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
188int
189wmain(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
263fail:
264    PyConfig_Clear(&config);
265fail_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
277int 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