Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A way to load DLL from central repository

Tags:

c++

c

windows

dll

We have lot of products and there are some common DLLs across each product's application. Right now we copy each common DLL into each product's bin directory and treat them as private assembly. This unnecessarily increase the msi size of each product and when a problem occurs in a DLL we have to build each product's msi comprising the DLL and deploy it.

Is there anyway to instruct the product application to use a common private directory to be used for loading DLLs [ using manifest scheme.. ]? [ Note: Adding the private directory to PATH env will not provide a solution as if there is a DLL with same name exist in SYSTEM directory, that would take the privilege over our private directory ]

-Kartlee

like image 448
Kartlee Avatar asked Dec 28 '09 13:12

Kartlee


People also ask

How do I load a DLL in C++?

Using a DLL in C++ code using LoadLibraryA functionh header and aliased to LoadLibraryW using #define macros. It accepts only one argument: a long pointer to wide string as the file name of the library. In return, it will give you a handle of the DLL module mapped into the virtual address of the current process.

What is DLL library load?

A dynamic link library (DLL) is a collection of small programs that larger programs can load when needed to complete specific tasks. The small program, called a DLL file, contains instructions that help the larger program handle what may not be a core function of the original program.

How does DLL load work?

In run-time dynamic linking, an application calls either the LoadLibrary function or the LoadLibraryEx function to load the DLL at run time. After the DLL is successfully loaded, you use the GetProcAddress function to obtain the address of the exported DLL function that you want to call.


3 Answers

You don't specify if your environment is .NET or straight Win32.

I am assuming its Win32 because if its .NET the technologies to do this are all much closer to hand in terms of things like the Global Assembly Cache.

In terms of Win32 it is possible to load Dlls from a shared location in one of two ways:

  • Use LoadLibrary with explicit full paths. This means you cannot use static linking - all dll functions used in all products will have to be accessed via GetProcAddress. You cannot import c++ classes from dll's loaded via LoadLibrary - they must be statically linked to work so this approach may or may not be viable. Its not terribly hard to write shim header files that masquerade as the dll's interface and do a just in time dll load and GetProcAddress as needed for each call.

  • The other option is to turn the dll's into what are called "side by side assemblies" and install them into the WinSxS store. Don't be scared by the big name. "side by side assembly" means "A Dll file plus manifest file with version information". Each of the various applications would then put 'strong name' - which includes version information - into its application manifest for each dll it uses, and the Win32 Dll loader will use this to pick the correct instance of the common dll from the WinSxS store. The basic process is described in the MSDN article Guidelines for Creating Side-by-side Assemblies


On Windows versions 6.1 and up (Windows Server 2008 and the ironically named Windows 7) application configuration files DO NOW support the probing element in Application Configuration Files

This means you should be able to provide a path (relative to your application) to a folder containing containing dll assemblies you want to load.


I've done some testing on Windows 7, and this works:

Assuming you have an application app1.exe installed in \Program Files\App1 that depends on some common dll "thedll.dll"

In the application folder (\Program Files\App1) create a file App1.exe.config and give it the following contents :

<configuration>   
   <windows>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <probing privatePath="..\AcmeCommon"/>
    </assemblyBinding>
  </windows>
</configuration>

Now, create a folder called \Program Files\AcmeCommon, and in it a folder acme.thedll, and copy thedll.dll into \Program Files\AcmeCommon\acme.thedll

Also create a file in AcmeCommon\acme.thedll called acme.thedll.manifest - this will be the assembly manifest describing the assembly called 'acme.thedll'

The contents of acme.thedll.manifest will be:-

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    <assemblyIdentity name="acme.thedll" version="1.2.3.4" processorArchitecture="x86"  type="win32"/>
    <file name="thedll.dll"/>
</assembly>

Now we have the common dll, in a common location, as a native sxs assembly. We have the app, with a config file that will, on Windows 7 and 2008 server (and up) tell it to search for assemblies in the common location. But the app is still trying to link to the dll as a dll, rather than via an assembly.

To get the app to load the assembly, we need to add a manifest file to the application. If you are using visual studio, your applications are probably already configured to create and embed manifests via the linker and manifest tool project settings. In which case the easiest way to tell the app about the assembly is to rebuild it after adding the following code to at least one header or c/cpp file in the project :-

#pragma comment(linker,"/manifestdependency:\"type='win32' name='acme.thedll' version='1.2.3.4' processorArchitecture='x86' language='*'\"")

If you are using an older build environment where the manifests are hand made you would need to merge the following xml with app1.exe.manifest in the App1 folder:

<dependency>
  <dependentassembly>
    <assemblyIdentity type="win32" name="acme.thedll" version="1.2.3.4"   processorArchitecture="x86" language="*"/>
  </dependentassembly>
</dependency>

This should close the circle: When the app loads the win32 loader will load the application manifest (app1.exe.manifest or embedded as a RT_MANIFEST resource) and learn about the "acme.thedll" assembly. It will also load the application config file (app1.exe.config) and learn about the private path to search for assemblies. And it will then load and add "acme.thedll.manifest" to the apps "activation context". Then, when the loader tries to load "thedll.dll" it will search the activation context db, find that it's in the acme.thedll assembly, and load it from the assemblies location.

like image 193
Chris Becke Avatar answered Oct 08 '22 05:10

Chris Becke


If you're talking about .NET, you can:

  • Load your DLL directly from a database by using Assembly.Load(byte[])
  • By using Assembly.TypeResolve event
  • By using TypeProvider class
  • By defining a probe directory in your config file

Like:

<configuration>   
    <runtime>
        <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
            <probing privatePath="bin"/>
        </assemblyBinding>
    </runtime>
</configuration>
like image 21
Rubens Farias Avatar answered Oct 08 '22 04:10

Rubens Farias


I am following Chris's answer. Make sure the case is correct on the manifests and configs. Otherwise they will fail. I was able to get the assembly to be loaded, but the DLL wouldn't be chosen. In my case a Windows DLL in system32 is being chosen instead of my own with the same name. In Dependency Walker, my DLL is loaded, but at runtime, with Process Explorer, Windows' copy is loaded. Any ideas?

like image 1
r590 Avatar answered Oct 08 '22 05:10

r590