Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explicitly link to dinput8.dll in actual system directory at runtime #10

Closed
wants to merge 1 commit into from

Conversation

oblivioncth
Copy link

I haven't actually tested this on a system with its system volume behind a letter other than C:, but it should work given the use of GetSystemDirectoryA().

I did test that mods still load correctly on my own system. I use several mods including Seamless Coop and a few of yours from your other repository and everything still loaded as expected.'

Of course, please test this yourself before merging.

Fixes #9

@oblivioncth
Copy link
Author

oblivioncth commented Apr 8, 2023

One caveat worth noting is that the official Win32 documentation states to never call LoadLibrary from within DllMain due to some potential scenarios in which the loaded library performs thread management such that a deadlock occurs due to DllMain already holding a lock on the OS's module loader.

I'm no DirectX expert, but from what I tell dinput8.dll in particular does not seem to be one of such modules as this seems to be pretty common practice when writing a wrapper for it. SpecialK does this, and even portions of the Win32 implementation violate Microsoft's own guidance; however, ultimately it's unclear how to judge exactly when it's safe to do this or not given that the docs simply say "never".

If you want to play it extra safe, we can switch to a RAII approach and only load the real DLL once any of the 6 passthrough functions are called for the first time, avoiding the use of LoadLibrary in DllMain entirely. I'm happy to make that change.

@techiew
Copy link
Owner

techiew commented Apr 8, 2023

I've called LoadLibrary from DllMain in the past, and usually it causes no issues. However I did have a very obscure bug that I found was caused by calling LoadLibrary in this way (at this point I don't remember what the problem was). Afterwards I've never put LoadLibrary inside DllMain. To be fair, this may or may not be an issue for dinput8 specifically, but I reasoned that simply hardcoding the exports to the original DLL on the C drive was better than having this potential issue, given that 99.99% of people will have C as their system drive.

Of course there's also the approach of calling LoadLibrary on the new thread and dynamically loading from the system drive (which is my usual go-to), but this can theoretically lead to a race condition - unless you add some kind of synchronization between the DLL calls and the loading of the original DLL (more on that later).

I've also already attempted the RAII approach that you mention - calling LoadLibrary and/or GetProcAddress only when one of the functions are called - but this approach caused problems specifically with Elden Ring as for some reason the Epic Online Services DLL makes GetProcAddress point to the wrong function (only after about 5-10 seconds after the game was booted). I did not look too deep into this issue to see if it could be worked around and I dropped the approach.

Currently I am working on a DLL proxying solution that I can use for all of my projects that will provide a simple approach (atleast from the usage side) to DLL proxying no matter which DLL you are proxying. It will dynamically load the original DLL from the system drive without any race conditions or any LoadLibrary calls in DllMain. The proxying will consist of a single call in DllMain and require as little setup as possible to be used in new projects.

Thank you for your interest and time, however I will let the proxying stay the way it is for now until I am able to implement my new solution. Feel free to come with more ideas or comments if you have any.

@techiew techiew closed this Apr 8, 2023
@oblivioncth
Copy link
Author

I've also already attempted the RAII approach that you mention - calling LoadLibrary and/or GetProcAddress only when one of the functions are called - but this approach caused problems specifically with Elden Ring as for some reason the Epic Online Services DLL makes GetProcAddress point to the wrong function (only after about 5-10 seconds after the game was booted). I did not look too deep into this issue to see if it could be worked around and I dropped the approach.

Wow, that's quite strange!

Although in the long run using your general purpose solution is obviously best, I'm fine moving the loading of dinput8 to LoaderThread and handling synchronizing in the interim. Just let me know if I should bother.

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

Successfully merging this pull request may close these issues.

System DLLs are hardcoded to the C: drive
2 participants