godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.16k stars 21.19k forks source link

Incorrect usage of `AddDllDirectory` #95905

Closed 0x0ACB closed 2 months ago

0x0ACB commented 2 months ago

Tested versions

4.3

System information

Windows 11

Issue description

Loading GDExtension dll files on Windows does not work if the project has been exported as a .pck file and the .exe is launched from a different folder. This is because AddDllDirectory is called with a relative path and fails. According to the documentation AddDllDirectory needs to be called with an absolute path.

The AddDllDirectory function can be used to add any absolute path to the set of directories that are searched for a DLL.

image

It works if the addons folder is next to the .exe because then LoadLibraryExW gets the correct relative path.

Steps to reproduce

Add GDExtension addon to project export as .pck copy the addon folder next to the .pck place the .exe in a different folder run the .exe from the folder containing the .pck

Minimal reproduction project (MRP)

|- test.pck |- addons/ |- bin/ |--test.exe

dsnopek commented 2 months ago

Thanks!

Unfortunately, I'm not able to reproduce this.

I tested with Godot v4.3 using the godot-jolt extension and its sample project. When I export, the DLL gets placed in the same folder as the EXE, which loads just fine. Then I tried moving the EXE so that it was in the same path as it was in the project, so I put it under addons/godot-jolt/windows/godot-jolt_windows-x64.dll - this also worked fine!

The only way I could prevent it from being loaded was to put it under some random directory, that wasn't the same as the directory that's specified in the godot-jolt.gdextension file. But it makes sense that it can't load it, because how's it supposed to know which directory its in?

I also re-ran these tests with Godot v4.2.2, and it behaved exactly the same. If the DLL was either next to the EXE, or in the path specified in the godot-jolt.gdextension, then it loads fine. If I put in some arbitrary directory below the EXE, then it doesn't load. So, this doesn't appear to be a regression.

Can you give some more details about how to reproduce? Am I missing a step?

0x0ACB commented 2 months ago

Your folder structure is incorrect. But I see now that the diagram I added maybe wasn't that clear about it:

│    launcher.exe
│    game.pck
├─────bin
│     │ game.exe      
│     └────addons
│          └───godot-jolt
│              └───windows
│                  │ godot-jolt_windows-x64.dll

results in platform\windows\os_windows.cpp(383): OS_Windows::open_dynamic_library> Condition "!FileAccess::exists(path)" is true. Returning: ERR_FILE_NOT_FOUND

│    launcher.exe
│    game.pck
├───bin
│   │ game.exe      
│   └── addons
│       └───godot-jolt
│           └───windows
│               │ godot-jolt_windows-x64.dll

results in

<platform\windows\os_windows.cpp(455): OS_Windows::open_dynamic_library> Parameter "p_library_handle" is null.:
Can't open dynamic library: addons/godot-jolt/windows/godot-jolt_windows-x64.dll. Error: Error 126: The specified module could not be found..

Placing the .dll in the same folder as the .exe does indeed work. The call to AddDllDirectory is still incorrect though. We are using a launcher that is started first for updating and the actual game is stored in /bin. Resulting in this slightly unconventional folder structure

dsnopek commented 2 months ago

Ah, ok, thanks!

Using that directory structure, I am able to reproduce the issue, and I can confirm that passing an absolute path to AddDllDirectory() fixes it. (I'm simulating having a launcher.exe by launching Godot from cmd.exe with the current directory set to the directory of the launcher.)

Here's PR https://github.com/godotengine/godot/pull/96192 which makes that change