Skip to content

Commit 51236c2

Browse files
committed
Add UTF-8 to UTF-16 conversion functions and update environment variable handling for Windows
1 parent 68b5e84 commit 51236c2

File tree

1 file changed

+109
-35
lines changed

1 file changed

+109
-35
lines changed

src/serious_python_windows/windows/serious_python_windows_plugin.cpp

Lines changed: 109 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,84 @@
2525
#include <string>
2626
#include <thread>
2727

28+
namespace
29+
{
30+
// Convert UTF-8 text from Flutter/Dart to UTF-16 for Windows APIs.
31+
std::wstring Utf8ToWide(const std::string &value)
32+
{
33+
if (value.empty())
34+
{
35+
return L"";
36+
}
37+
UINT codepage = CP_UTF8;
38+
DWORD flags = MB_ERR_INVALID_CHARS;
39+
int size = MultiByteToWideChar(codepage, flags, value.data(),
40+
static_cast<int>(value.size()), nullptr, 0);
41+
if (size <= 0)
42+
{
43+
codepage = CP_ACP;
44+
flags = 0;
45+
size = MultiByteToWideChar(codepage, flags, value.data(),
46+
static_cast<int>(value.size()), nullptr, 0);
47+
}
48+
if (size <= 0)
49+
{
50+
return L"";
51+
}
52+
std::wstring wide(size, L'\0');
53+
MultiByteToWideChar(codepage, flags, value.data(),
54+
static_cast<int>(value.size()), wide.data(), size);
55+
return wide;
56+
}
57+
58+
// Convert UTF-16 Windows strings to UTF-8 for logs/interop.
59+
std::string WideToUtf8(const std::wstring &value)
60+
{
61+
if (value.empty())
62+
{
63+
return "";
64+
}
65+
int size = WideCharToMultiByte(CP_UTF8, 0, value.data(),
66+
static_cast<int>(value.size()), nullptr, 0,
67+
nullptr, nullptr);
68+
if (size <= 0)
69+
{
70+
return "";
71+
}
72+
std::string utf8(size, '\0');
73+
WideCharToMultiByte(CP_UTF8, 0, value.data(),
74+
static_cast<int>(value.size()), utf8.data(), size,
75+
nullptr, nullptr);
76+
return utf8;
77+
}
78+
79+
// Set environment variables using Unicode-safe Windows APIs.
80+
void SetEnvWide(const std::wstring &key, const std::wstring &value)
81+
{
82+
if (key.empty())
83+
{
84+
return;
85+
}
86+
_wputenv_s(key.c_str(), value.c_str());
87+
SetEnvironmentVariableW(key.c_str(), value.c_str());
88+
}
89+
90+
// Join paths without narrowing, preserving non-ASCII characters.
91+
std::wstring JoinPaths(const std::vector<std::wstring> &paths, wchar_t sep)
92+
{
93+
std::wstring joined;
94+
for (size_t i = 0; i < paths.size(); ++i)
95+
{
96+
joined += paths[i];
97+
if (i + 1 < paths.size())
98+
{
99+
joined += sep;
100+
}
101+
}
102+
return joined;
103+
}
104+
}
105+
28106
namespace serious_python_windows
29107
{
30108

@@ -134,55 +212,49 @@ namespace serious_python_windows
134212
return;
135213
}
136214

137-
std::string exe_dir = std::filesystem::path(exe_path).parent_path().string();
138-
std::string app_dir = std::filesystem::path(app_path).parent_path().string();
215+
// Treat Dart strings as UTF-8 to avoid lossy conversions.
216+
std::filesystem::path exe_path_fs(Utf8ToWide(exe_path));
217+
std::filesystem::path app_path_fs(Utf8ToWide(app_path));
218+
std::wstring exe_dir = exe_path_fs.parent_path().wstring();
219+
std::wstring app_dir = app_path_fs.parent_path().wstring();
139220

140221
printf("exePath: %s\n", exe_path.c_str());
141-
printf("exeDir: %s\n", exe_dir.c_str());
222+
printf("exeDir: %s\n", WideToUtf8(exe_dir).c_str());
142223
printf("appPath: %s\n", app_path.c_str());
143224

144-
std::vector<std::string> python_paths;
225+
std::vector<std::wstring> python_paths;
145226

146227
// add user module paths to the top
147228
for (const auto &item : module_paths)
148229
{
149230
if (auto str_value = std::get_if<std::string>(&item))
150231
{
151232
printf("module_path: %s\n", str_value->c_str());
152-
python_paths.push_back(*str_value);
233+
python_paths.push_back(Utf8ToWide(*str_value));
153234
}
154235
}
155236

156237
// add system paths
157238
python_paths.push_back(app_dir);
158-
python_paths.push_back(app_dir + "\\__pypackages__");
159-
python_paths.push_back(exe_dir + "\\site-packages");
160-
python_paths.push_back(exe_dir + "\\DLLs");
161-
python_paths.push_back(exe_dir + "\\Lib");
162-
python_paths.push_back(exe_dir + "\\Lib\\site-packages");
163-
164-
std::string python_path;
165-
for (int i = 0; i < python_paths.size(); i++)
166-
{
167-
python_path += python_paths[i];
168-
if (i < python_paths.size() - 1)
169-
{ // Don't add separator after the last element
170-
python_path += ";";
171-
}
172-
}
173-
174-
printf("PYTHONPATH: %s\n", python_path.c_str());
175-
176-
// set python-related env vars
177-
_putenv_s("PYTHONINSPECT", "1");
178-
_putenv_s("PYTHONDONTWRITEBYTECODE", "1");
179-
_putenv_s("PYTHONNOUSERSITE", "1");
180-
_putenv_s("PYTHONUNBUFFERED", "1");
181-
_putenv_s("LC_CTYPE", "UTF-8");
182-
_putenv_s("PYTHONHOME", exe_dir.c_str());
183-
_putenv_s("PYTHONPATH", python_path.c_str());
184-
185-
// set user environment variables
239+
python_paths.push_back(app_dir + L"\\__pypackages__");
240+
python_paths.push_back(exe_dir + L"\\site-packages");
241+
python_paths.push_back(exe_dir + L"\\DLLs");
242+
python_paths.push_back(exe_dir + L"\\Lib");
243+
python_paths.push_back(exe_dir + L"\\Lib\\site-packages");
244+
245+
std::wstring python_path = JoinPaths(python_paths, L';');
246+
printf("PYTHONPATH: %s\n", WideToUtf8(python_path).c_str());
247+
248+
// Set Python-related env vars using wide APIs so Unicode paths survive.
249+
SetEnvWide(L"PYTHONINSPECT", L"1");
250+
SetEnvWide(L"PYTHONDONTWRITEBYTECODE", L"1");
251+
SetEnvWide(L"PYTHONNOUSERSITE", L"1");
252+
SetEnvWide(L"PYTHONUNBUFFERED", L"1");
253+
SetEnvWide(L"LC_CTYPE", L"UTF-8");
254+
SetEnvWide(L"PYTHONHOME", exe_dir);
255+
SetEnvWide(L"PYTHONPATH", python_path);
256+
257+
// Set user-provided env vars as UTF-16 for Windows.
186258
for (const auto &kv : env_vars)
187259
{
188260
auto key = kv.first;
@@ -191,7 +263,7 @@ namespace serious_python_windows
191263
auto str_value = std::get_if<std::string>(&value))
192264
{
193265
printf("env_var: %s=%s\n", str_key->c_str(), str_value->c_str());
194-
_putenv_s(str_key->c_str(), str_value->c_str());
266+
SetEnvWide(Utf8ToWide(*str_key), Utf8ToWide(*str_value));
195267
}
196268
}
197269

@@ -256,7 +328,9 @@ namespace serious_python_windows
256328
Py_Initialize();
257329

258330
FILE *file;
259-
errno_t err = fopen_s(&file, appPath.c_str(), "r");
331+
// Use wide fopen to open Unicode paths on Windows.
332+
std::wstring appPathWide = Utf8ToWide(appPath);
333+
errno_t err = _wfopen_s(&file, appPathWide.c_str(), L"r");
260334
if (err == 0 && file != NULL)
261335
{
262336
PyRun_SimpleFileEx(file, appPath.c_str(), 1);

0 commit comments

Comments
 (0)