Pin the host module instead of wrapping a borrowed HMODULE#147
Open
JustinMDotNet wants to merge 1 commit into
Open
Pin the host module instead of wrapping a borrowed HMODULE#147JustinMDotNet wants to merge 1 commit into
JustinMDotNet wants to merge 1 commit into
Conversation
PluginSystem::Load stored the result of GetModuleHandleA(nullptr) in a wil::unique_hmodule for the .exe path. GetModuleHandle returns a borrowed handle that, per the Microsoft docs, must not be passed to FreeLibrary. wil::unique_hmodule calls FreeLibrary on destruction, so the loader refcount on the host module was being decremented in two paths every startup: the immediate destruct when the host did not export Supports, and the eventual unload through PluginBase::m_module when the host was accepted as a plugin. Acquire a real reference with GetModuleHandleExW( GET_MODULE_HANDLE_EX_FLAG_PIN, ...) instead. Pinning makes the eventual FreeLibrary a documented no-op, the loader bookkeeping stays balanced, and the existing RAII model in PluginBase does not have to change. Fixes wopss#142 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #142.
What this changes
PluginSystem::Loadnow acquires a real owning reference for the host executable path instead of wrapping the borrowed handle fromGetModuleHandleA(nullptr):GET_MODULE_HANDLE_EX_FLAG_PINpins the host module for the life of the process. Per the docs, subsequentFreeLibrarycalls on a pinned module are a no-op, so the eventualwil::unique_hmoduledestructor (here or inPluginBase) is harmless and the loader refcount stays balanced.Why
The previous code stored the result of
GetModuleHandledirectly inwil::unique_hmodule. Microsoft'sGetModuleHandledocs say:wil::unique_hmodulecallsFreeLibraryon destruction, so the borrowed handle was being passed toFreeLibraryin two paths:.exedid not exportSupports, the localhandlewent out of scope at the end ofLoad—FreeLibrarywas called on the process image..exewas accepted as a plugin,PluginBase::m_module(also awil::unique_hmodule, seePluginBase.hpp:38) would do the same on unload.Both paths fire on every startup because of the unconditional
Load(m_paths.GetExe(), false)self-probe inPluginSystem.cpp:172. Windows happens to be forgiving about the process image specifically, which is why this has been invisible, but it is still a real Win32 contract violation.Design choice
Two reasonable alternatives, both rejected for being more invasive:
PluginBase's member layout and every site that touches it.std::optional<wil::unique_hmodule>. Same problem at thePluginBaseboundary.Pinning is a one-line change that keeps the existing RAII model intact and is exactly what
GET_MODULE_HANDLE_EX_FLAG_PINwas designed for. The host executable can never be unloaded anyway, so pinning costs nothing.If
GetModuleHandleExWfails for any reason,handlestays empty and the existingif (!handle)check atPluginSystem.cpp:242reports it as a load failure, same as before.What I tested
Local rebuild. Behavior change in the common case is just that the loader refcount stays correct.