Isolating a plugin into its own directory
Let’s assume the following directory structure:
appdir\host.exe
plugindir\plugin.dll
plugindir\plugindependency.dll
plugindir\pluginresource.txt
where host.exe
explicitly loads plugindir\plugin.dll
, which in turn has a static dependency on plugindependency.dll
.
As-is, this structure doesn't work, because the DLL search order has the directory of the application appdir
in the search path, but not plugindir
. So plugindependency.dll
won't be found.
This is solvable by isolating plugin.dll
. The way to do this is to embed a manifest under the RT_MANIFEST section of its resources as ID 2 (aka ISOLATION_AWARE_MANIFEST_RESOURCE_ID.) E.g.:
<?xml version='1.0' encoding='UTF-8' standalone='yes'?>
<assembly xmlns='urn:schemas-microsoft-com:asm.v1' manifestVersion='1.0'>
<file name="plugindependency.dll" />
</assembly>
When plugin.dll
is loaded, the loader automatically activates this manifest when resolving static dependencies and doing DllMain callouts. Files listed the manifest bypass the DLL search order and are directly loaded out of the directory of the manifest, in this case plugindir
, which is what we want. Problem solved.
Notes
Beyond static imports, the embedded manifest isn't always active when code inside plugin.dll
is executing. To have it be active, you either need to use ISOLATION_AWARE_ENABLED or manual activation/deactivation, as described in Junfeng’s post, for example if you want LoadLibraryEx to be able to find plugindependency.dll
Beyond dlls, you can also list all of your plugin's resource files in the manifest as well. They won't automatically be found, but SearchPath with a NULL first parameter while the appropriate manifest is active will find them in the correct directory (for files listed in the manifest, you can disregard the remarks section of the API documentation.) Unfortunately ISOLATION_AWARE_ENABLED doesn't wrap SearchPath, so you'll have to do manual activation/deactivation here.
Caution: if plugin.dll
depends on some external redistributable components, don't list them directly as <file>
elements in the plugin's manifest file, because it will prevent them from being serviced in the future (listing dependencies on other side-by-side assemblies is fine.) For example, a Microsoft dll might get updated by installation into the system directory -- if the dll is listed in the manifest, your plugin won't pick up the serviced version, opening you up to security issues.