2

I'm trying to dynamically load a DLL (python3.dll) which isn't on the normal search path. I can load it with an altered search path, but when I do GetProcAddress, it's failing (because the function I'm loading forwards to another DLL, python37.dll).

I think what I need to do is get GetProcAddress to follow the same search logic as in my original DLL load, but I don't know how to so that without altering the system PATH (which has all sorts of other potential issues, as my code goes on to let the user run their own code, which needs the original path).

Is there a clean solution for this?

Here's the code I tried:

HMODULE py_dll = LoadLibraryW(L"python3.dll");
if (!py_dll) {
    py_dll = LoadLibraryExW(L"C:\\Work\\Projects\\pylaunch\\py3embed\\python3.dll", NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
}

if (!py_dll) {
    printf("Cannot load Python DLL\n");
    err();
    return 1;
}
char buf[1000];
GetModuleFileName(py_dll, buf, 1000);
printf("DLL is %s\n", buf);
Py_Main_t Py_Main = (Py_Main_t)GetProcAddress(py_dll, "Py_Main");
if (!Py_Main) {
    printf("Cannot load Py_Main\n");
    err();
    return 1;
}

Adding SetDllDirectory() didn't seem to help, but I just dumped a call to it into the code, as I wasn't 100% clear on how it worked, so it's possible I did something wrong.

For reference, the definition of the Py_Main symbol is

 747  2EA          Py_Main (forwarded to python37.Py_Main)
Paul Moore
  • 6,569
  • 6
  • 40
  • 47
  • Never worked with *pylaunch*, but I have a feeling that *python3.dll* is a shim, that references the real *Python3* one. Why not loading the referenced *.dll* directly? Also, could you post *SetDllDirectory* code? Is there any case when *python3.dll* points to *python35.dll* (wherever this is located)? – CristiFati Oct 04 '18 at 11:53
  • Your scenario makes me wonder if your python installation went well. Is it a standard installation? Here are _[some conversations down that path](https://stackoverflow.com/questions/47289279/error-loading-python-dll-loadlibrary-the-specified-module-could-not-be-found)_ if you think that might be an issue. (dealing with the _other_ python, but paths are still paths in any language.) – ryyker Oct 04 '18 at 12:06
  • @CristiFati `python3.dll` is part of the Python distribution, it forwards all references to `python37.dll`. It's needed so that your code can load a (minor) version independent DLL and work with any Python version. See PEP 384 for the details. The SetDllDirectory code is nothing more than `SetDllDirectoryW(L"C:\\Work\\Projects\\pylaunch\\py3embed");` (before `LoadLibrary`). – Paul Moore Oct 04 '18 at 12:11
  • @ryyker The installation is a copy of the standard embedded Python distribution. It's sound (it works for other embedded apps). – Paul Moore Oct 04 '18 at 12:13
  • Which of the Python37 installers is the one you used, 32bit or 64bit? I am wondering if the `system` versus `SysWoW64` locations are a factor in default search behavior of `LoadLibrary_x_` functions. – ryyker Oct 04 '18 at 12:38
  • @ryyker `python-3.7.0-embed-amd64.zip` on a 64-bit machine. If I move the exe to the `py3embed` directory (so the `LoadLibrary` call with no path succeeds) it works fine. It's only when I need to load from a relative path that it fails. The `SysWow` location is irrelevant (as far as I know) because it's not an installed Python distribution. – Paul Moore Oct 04 '18 at 13:04

1 Answers1

2

It appears that this is a problem with Windows 7. The discussion here gives more details.

Basically, GetProcAddress doesn't appear to respect changes to the search path using SetDllDirectory when loading additional DLLs to resolve forwarded symbols. If the DLL containing the forwarding symbol has a dependency on the target DLL, it does work, but python3.dll doesn't have such a dependency.

From a report in the Python issue linked above, it appears that this behaviour has been fixed in Windows 10, so you need to be on Windows 7 to see the problem.

The solution seems to be to update Windows, or avoid using GetProcAddress with forwarded functions (unless the forwarder has a dependency on the target).

Paul Moore
  • 6,569
  • 6
  • 40
  • 47