Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Altering DLL search path for static linked DLL

I've searched for any hints how I can do this, but all I found was how to redirect a SxS DLL to the local application folder. Here is what I want to accomplish: The (C++) Application.exe is linked to a DLL, Plugin.DLL (dependant project). This DLL is not placed inside the application directory, but in a subfolder called "plugins". As the DLL is statically linked, the application would try to load it from the application folder.

Is there any way how I can change the search path for this particular DLL? Either through manifests or VS2008 linker configurations?

like image 483
Oliver Avatar asked Sep 30 '10 15:09

Oliver


People also ask

Can a DLL be statically linked?

The client executable calls the exported functions of the DLL the same way as if the functions were statically linked and contained within the executable. Implicit linking is sometimes referred to as static load or load-time dynamic linking.

Can a DLL call another DLL?

You can use load-time dynamic linking or run-time dynamic linking in your DLL in the same way as in the executable. The only restriction is not to call LoadLibrary from your DllMain function to avoid deadlocks.

Where is the DLL path in Visual Studio?

In your Project properties(Right click on project, click on property button) ▶ Configuration Properties ▶ Build Events ▶ Post Build Events ▶ Command Line.


2 Answers

My first thought is, if you are statically linking a dll, it isnt a plugin. Just put the dll in the EXE folder and be done with it. That is the deployment configuration supported by windows for statically loaded DLLs.

That said, there are ways to achieve what you want. But they are mostly stupid, or complicated for no good reason: Your options are:

  • Don't statically link. Use LoadLibrary("plugins/Plugin.dll") & GetProcAddress to access plugin content.
  • Add "the path to your plugins folder" to the systems PATH environment variable.
  • Use the delay load mechanism to delay accessing the plugins functionality, set a custom helper function that can load the dll(s) using a provided path.
  • Turn the plugins folder into an assembly (by creating a .manifest file in it that lists the plugin.dll). Add "plugins" as a dependent assembly to your app. Now it will look in the plugins folder.
  • Split your application into a stub exe and a dynamically loaded part. In the stub exe call SetDllDirectory to point to the plugin folder, then call LoadLibrary passing the full path to "appstub.dll".

To turn a folder, with one or more dll's into an "assembly", simply add a file to the folder with the folders name.manifest.

So, plugins.manifest :-

<assembly manifestVersion="1.0">
  <assemblyIdentity type="Win32" name="Plugins" version="1.0.0.0" processorArchitecture="x86" />
  <file name="Plugin.dll"/>
</assembly>

It is a VERY good idea to ensure that the folder and the dll's name is different as if the dll name is the assembly name windows starts looking at its embedded manifest file for information about the assembly.

Assuming you are using Visual Studio 7 or later, the following directive added to a .c/.cpp or .h file in the project will then make your application attempt to load dlls from the assembly rather than just the local directory:

#pragma comment(linker, "/manifestdependency:\"name='Plugins' "\
                        "processorArchitecture='*' version='1.0.0.0' "\
                        "type='win32'\"")
like image 116
Chris Becke Avatar answered Sep 27 '22 16:09

Chris Becke


Expanding and detailing Chris' proposal of an "assembly" subdirectory:


Side note: Chris also has two excellent write ups of more details:

  • Answer to DLL redirection using manifests
  • Answer to A way to load DLL from central repository

    This on also explains how you can leverage probing privatePath in the application config files to put the DLLs outside of a subfolder.


MS docs

This is actually called a "Private Assembly" and MS docs explain it this way:

Private assemblies are installed in a folder of the application's directory structure. Typically, this is the folder containing the application's executable file. Private assemblies may be deployed in the same folder as the application, in a folder with the same name as the assembly, or in a language specific subfolder with the same name as the assembly.

For example (...)

Appdir\Microsoft.Tools.Pop\Microsoft.Tools.Pop.MANIFEST : The manifest is deployed as a separate file in a subfolder having the assembly's name.

(...)

Private assemblies can be installed by any installation method that can copy the assembly's file into this folder, such as the xcopy command.

The example

You got you're trusty old executable in you program folder C:\Test\Program\app.exe and you want to -- at Load-Time -- load your DLL file from the Plugins subfolder, i.e. C:\Test\Program\plugins\tool1.dll without messing with PATHor any other stuff.

You need to:

  • Compile app.exe with:

    #pragma comment(linker, "/manifestdependency:\"name='Plugins' version='1.0.0.0' type='win32'\"")
    // name, type and version seems to be the minimum info to get away with
    

    Note: Compiling/Linking this in, instead of using an external manifest (app.exe.manifest) is required on my test system, I didn't find out why yet. (*a)

    What also works however is embedding/merging the manifest file listed below into the executable with the mttool, instead of with the linker pragma. (Configuration > Manifest Tool > Additional Manifest Files)

  • put tool1.dll intro the plugins subfolder

  • add a plugins.manifest file into the plugins subfolder, i.e. C:\Test\Program\plugins\plugins.manifest and that one looks like this:

plugins.manifest:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity
                            type="win32"
                            name="Plugins"
                            version="1.0.0.0"
            />
    <file name="tool1.dll"/>
</assembly>

That's it. Starting app.exe will automatically find the dll in the subfolder at Load-Time.


(*a) : Merging this manifest file works, but you can't use that as the sole external manifest file, and I suspect it's because it's missing all the other manifest info the build system already places into your executable!

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <dependency>
        <dependentAssembly>
            <!-- Note: type: The value must be win32 and all in lower case. Required. --> 
            <!-- Note: version: The value must be win32 and all in lower case. Required. --> 
            <assemblyIdentity
                                    type="win32"
                                    name="plugins"
                                    version="1.0.0.0"
            />
        </dependentAssembly>
    </dependency>
</assembly>
like image 39
Martin Ba Avatar answered Sep 27 '22 17:09

Martin Ba