Skip to content

Conversation

charles-zablit
Copy link

This patch delays the load of python310.dll on Windows, by using MSVC's /DELAYLOAD parameter.

This allows the use of the SetDllDirectoryW API to manually add C:/toolchain-install-dir/Python-3.10.1 to the list of DLL search paths.

This patch is part of 2 other PRs which aim at packaging the Embeddable version of Python with the toolchain's installer:

)

target_link_libraries(${name} PRIVATE ${ARG_LINK_LIBS})
if(MSVC)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be WIN32 not MSVC otherwise this will regress if you use a different compiler.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks!

// toolchain, therefore the directory might not exist.
void AddPythonDLLToSearchPath() {
wchar_t buffer[MAX_PATH];
DWORD length = GetModuleFileNameW(NULL, buffer, MAX_PATH);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is risky - the path is possibly a long path (i.e. >MAX_PATH). In such a case, LLDB will be unusable permanently. The path returned is the path used to load, so this is not something we can control.

It is likely best to use GetModuleHandleW and then GetFinalPathNameByHandleW to get the path.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not able to use GetModuleHandleW with GetFinalPathNameByHandleW directly after, there had to be an intermiedary GetModuleFileNameW and it was back to the original problem.

I found this SO thread where others are struggling with this API. I settled with the grow the buffer until it fits solution.

inline std::wstring GetPathToExecutableW() {
// Iterate until we max out the Windows max path length (256*2^7 > 32,767).
static const size_t maximumIterations = 7;
std::wstring buffer;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use std::vector<WCHAR>?

Comment on lines 437 to 441
DWORD length = GetModuleFileNameW(NULL, &buffer[0], bufferSize);
if (length < buffer.length()) {
buffer.resize(length);
return buffer;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could then be re-written as:

if (GetModuleFileNameW(nullptr, buffer.data(), buffer.size()) < buffer.size())
return std::wstring(buffer.begin(), buffer.end());

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, this looks better, thanks!

void AddPythonDLLToSearchPath() {
std::wstring modulePath = GetPathToExecutableW();
if (modulePath.empty()) {
WithColor::error() << "Failed to get module file path. Error: "
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This error message doesn't really give the user much information. How about:

"Unable to find python: " << GetLastError() << '\n';

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks

std::wstring modulePath = GetPathToExecutableW();
if (modulePath.empty()) {
WithColor::error() << "Failed to get module file path. Error: "
<< GetLastError() << "\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<< GetLastError() << "\n";
<< GetLastError() << '\n';

Note that "\n" would cause unnecessary buffering.

@charles-zablit charles-zablit force-pushed the charles-zablit/windows/delay-python-loading branch from 764df35 to 8807d68 Compare October 7, 2025 15:17
static const size_t maximumIterations = 7;
std::vector<WCHAR> buffer;
DWORD bufferSize = MAX_PATH;
for (size_t i = 0; i < maximumIterations; i++) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this is better written as:

Suggested change
for (size_t i = 0; i < maximumIterations; i++) {
for (size_t i = 0; buffer.size() < 32767; i++) {

// `Swift/Toolchains/x.x.x/usr/bin/lldb.exe`.
for (int i = 0; i < 5; i++) {
sys::path::remove_filename(pythonDir);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can make this a CMake configuration option for a relative path, and then upstream this.

@JDevlieghere
Copy link

Can we support this configuration upstream and add all the necessary support there?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants