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