Our C# / .NET program is calling a third party DLL (x.dll) in the target folder. x.dll is then calling another third party DLL y.dll in the same folder. At some systems, this does not work, because x.dll can't find y.dll.
The same program with the same dlls works on most other systems, but we can't find out what the differences are to the systems were it does not work. We checked the properties, the dlls are not blocked by Windows. Also the virus-scanner was disabled.
Please go through Dynamic-Link Library Search Order and see which case it applies. the most likely case is standard DLL search order with SafeDllSearchMode enabled:
If SafeDllSearchMode is enabled, the search order is as follows:
The directory from which the application loaded.
The system directory. Use the GetSystemDirectory function to get the path of this directory.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The current directory.
The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by
the App Paths registry key. The App Paths key is not used when
computing the DLL search path.
Note that if the DLL is a managed assembly (you do not mention it is) then the .Net assembly search rules apply, see How the Runtime Locates Assemblies. Obviously, GAC would also play a role for managed DLLs.
I'm just giving you the pointers to do your own investigations, since your post is missing the relevant info.
Related
I am working on an application where users can develop their own plugins as dll files. They need to put these dll files to a specific path so my application(C++) can load them and run. The problem is, when user develops a dll file depends another dll file and put both files to my dll path, my application can't find second dll file.
Ex:
DllA(C++) depends DllB(C#)
my app loads DllA but can't find DllB and fails.
If user puts DllB into application binary path and DllA to specific dll path, I am able to execute plugin. I also tried to put DllB into various directories such as windows or system32 but it again fails. I also tried to add my dll path to PATH environment variable but no effect.
I am already executing SetDLLDirectory function to load DllA in the specific path. Since I've no control over user code, I can't make them to call this function in DllA code also. My question is that how can I force windows to search my specific dll path?
I have to use the C# class library code in my native code which is written in c++. For this I added one interface written in c++ add the my C# class library project as dependency to it. This works fine till I put the C# dll in .exe folder , but I put this in libs folders (where we keep our dll's), I get file not found exception when tried to call the C# dll function.
Is there any way to keep c# dll in libs folder and make it work?
Thanks.
Yes, you need to re-add it from the libs folder and it will be fine. Or if you are naming the DLL in your code itself, then just provide the full path along with the DLL name. Then when you recompile, the .exe will contain the path you gave it in the source code and will look for the DLL there.
From the MSDN(LoadLibrary),
Remarks section::
The first directory searched is the directory containing the image
file used to create the calling process (for more information, see the
CreateProcess function). Doing this allows private dynamic-link
library (DLL) files associated with a process to be found without
adding the process's installed directory to the PATH environment
variable. If a relative path is specified, the entire relative path is
appended to every token in the DLL search path list. To load a module
from a relative path without searching any other path, use
GetFullPathName to get a nonrelative path and call LoadLibrary with
the nonrelative path. For more information on the DLL search order,
see Dynamic-Link Library Search Order.
The search path can be altered
using the SetDllDirectory function. This solution is recommended
instead of using SetCurrentDirectory or hard-coding the full path to
the DLL.
If a path is specified and there is a redirection file for
the application, the function searches for the module in the
application's directory. If the module exists in the application's
directory, LoadLibrary ignores the specified path and loads the module
from the application's directory. If the module does not exist in the
application's directory, LoadLibrary loads the module from the
specified directory. For more information, see Dynamic Link Library
Redirection.
If you call LoadLibrary with the name of an assembly
without a path specification and the assembly is listed in the system
compatible manifest, the call is automatically redirected to the
side-by-side assembly.
This is all you need to know about path search of a dll. The easy solution, enter the full path in LoadLibrary call or set your PATH environment variable to contain the alternate path of the dll.
I'm loading dynamically a .NET assembly that depends on several bunches of native .dlls
located in various folders. But Windows finds those DLLs only if their folders are in the PATH environment variable when my application is started.
I would like to modify my PATH variable from my program to allow finding the necessary libraries.
According to
MSDN
"the search order is as follows: ...
The directories that are listed in the PATH environment variable."
Which instance of the PATH environment variable is used?
Every process has an instance.
I tried Environment.SetEnvironmentVariable("PATH", ...) but it did not help.
I also tried
SetDefaultDllDirectories()
together with
AddDllDirectory()
but these made no difference either.
The symptom is that when %PATH% contains the necessary folders when starting my .exe
(from a CMD prompt – it is a console application), ProcessMonitor shows that the native .dlls are probed in all the PATH folders, and are eventually found.
But when %PATH% does not contain the necessary folders at the time of starting, then the native .dlls are probed in the .exe folder and in SYSTEM32 only (although %PATH% contains far more),
regardless of the above-mentioned SetEnvironmentVariable()/SetDefaultDllDirectories()/AddDllDirectory() calls.
What is going here? What am I doing wrong? Why I cannot adjust the PATH for my process effectively?
Note: The AppDomain.AssemblyResolve event cannot help me because it's not fired when native .dlls
load other native .dlls.
That is because each process inherits its environment from the process that spawned it. And it didn't occur to the dit at Microsoft that something like PATH might change during the course of execution, so the CLR never refreshes the environment during process execution (and doesn't provide a means for the process to do so itself). See http://social.msdn.microsoft.com/Forums/vstudio/en-US/acf2d0f3-143e-4ba5-acdc-76a70a5c9830/environment-variables-refresh?forum=csharpgeneral for details.
Since the loader is resolving references to unmanaged DLLs via the normal Win32 way, you should probably look at P/Invoking these Win32 functions to alter the DLL search order used by the Win32 LoadLibrary() and LoadLibraryEx():
SetDllDirectory().
Adds a directory to the search path used to locate DLLs for the application.
AddDllDirectory().
Adds a directory to the process DLL search path.
SetDefaultDllDirectories().
Specifies a default set of directories to search when the calling process loads a DLL.
RemoveDllDirectory().
Removes a directory that was added to the process DLL search path by using AddDllDirectory.
See also DLL Search Order.
I'm using mkbundle and trying to create an embedded version of a little program IdaTester that uses Isis2. That system in turn uses features from Mono that depend on MonoPosixHelper
My problem is that mkbundle doesn't recognize the dependency and I end up with an executable that still needs to dynamically link against ~/bin/lib/libMonoPosixHelper.so, causing problems when I move this executable to a system where I don't have Mono installed. In effect, the bundle is missing one of the things it should be statically linked against.
My executable does work, but only if I make sure to run it only on machines that have the dynamic library in the "right place". This defeats the purpose of an embedded executable... I was hoping to be able to hand people this program as a kind of server they could put anywhere and launch as a binary, and obviously if they need to install the library for this to work, the server isn't exactly standalone!
I see how to force mkbundle to include any dll files the program depends upon, but MonoPosixHelper doesn't exist as a dll; this is a Linux-only library and exists only as a shared library. Does anyone know of a way to "force" the bundle to embed it statically?
In case this helps, my little compile script is as follows:
mcs -debug+ IdaTester.cs Isis.cs -r:System.dll -r:Microsoft.CSharp.dll -r:Mono.Posix.dll
mkbundle --static -o IdaTester IdaTester.exe --deps
I then run IdaTester; this works on platforms where the libMonoPosixHelper library can be found, but will fail at runtime when trying to dynamically load that library if running on a platform where libMonoPosixHelper hasn't been installed...
One needs to distribute libMonoPosixHelper.so with the application and change the dll map to make this work.
Background on the problem - Library is Loaded at Runtime
libMonoPosixHelper is not statically linked but searched for and loaded as a P/Invoke call, such as the example below:
[DllImport ("MonoPosixHelper")]
static extern int zipClose (ZipHandle handle, string globalComment);
That is, it is only requested at runtime, not compile time, and so cannot be linked in ahead of time.
Fixing it - Distributing libMonoPosixHelper.so
Four steps are required.
Copy libMonoPosixHelper to the directory you will be distributing the program with.
Update the DllMap config file to avoid a hard coded location.
Embed the config file with mkbundle
Add the path with libMonoPosixHelper.so to the LD_LIBRARY_PATH on the install machine.
To perform each:
1. Copy libMonoPosixHelper to the directory you will be distributing the program with.
libMonoPosixHelper is usually located in the lib folder, simply copy it to the folder you will be making a tarball out of.
cp $MONO_ROOT/lib/libMonoPosixHelper.so ~/MY_PROGRAM/
2. Update the DllMap config file to avoid a hard coded location.
This is the critical bit to avoid the hard coded paths issue. We need to embed a config file with mkbundle that does not specify the path. To do this, first find the mono config file, and also copy that to the local directory
cp $MONO_ROOT/etc/mono/config ~/MY_PROGRAM/config
Now we need to alter this file to remove the specific path for the dll, open it with your favorite editor and change the paths to avoid the specific prefix:
<dllmap dll="MonoPosixHelper" target="MACHINE_SPECIFIC/lib/libMonoPosixHelper.dylib" os="!windows" />
to
<dllmap dll="MonoPosixHelper" target="libMonoPosixHelper.dylib" os="!windows" />
3. Embed the config file with mkbundle
Add the following option to your mkbundle command to embed the newly edited config file:
--config MY_PROGRAM/config
4. Add the path with libMonoPosixHelper.so to the LD_LIBRARY_PATH on the install machine.
Now you can zip up your mkbundled executable, libMonoPosixHelper.so and any other files for distribution. When unzipped and run on a machine, dlopen will now look for libMonoPosixHelper.so just like any other dll. So simply add whatever directory contains your distributed version of libMonoPosixHelper to their LD_LIBRARY_PATH environmental variable
As far as I can tell, the best option available to me is either to build a non-shared Mono library containing the same methods as are currently found in MonoPosixHelper.so, or to provide a copy of MonoPosixHelper.so as a component to be installed in the same folder as my server. Neither seems ideal: the former forces me to "reach into" the Mono distribution, which creates a longer term maintenance issue, while the latter forces me into a more complex distribution and installation mode. But it seems that once one generates a shared library, you simply can't statically link against that version of the library; the Linux loader just doesn't treat such a thing as a library in the way it handles more standard libraries.
In contrast, if I do generate a standard library from the same .o files, the loader will be happy to statically link against it, and because mxbundle ultimately runs cc and hence uses the standard ld, that option would work for me. So I guess that's the answer to my question.
If I reference an external unmanaged DLL in C# by the following:
[DLLImport("MyDLL.dll", ...
Where should the DLL be placed when I want to run the code from my IDE? Should it be in the bin\Debug folder?
Which compiling configuration you are using?
If you want to run the application from Visual Studio Debug/Run it would be in your project folder
If you want to run by double click, it should be near your exe which is in
bin\Debug
or
bin\Release
folder.
Anywhere locatable by the LoadLibrary function. See Dynamic-Link Library Search Order for more information:
If SafeDllSearchMode is enabled, the search order is as follows:
The directory from which the application loaded.
The system directory. Use the GetSystemDirectory function to get the path of this directory.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The current directory.
The directories that are listed in the PATH environment variable. Note that this does not include the per-application path specified by the App Paths registry key. The App Paths key is not used when computing the DLL search path.
bin\Debug satisfies the first point, assuming that you're only ever running the application in its Debug configuration.
Yes, alongside the .exe file produced by the compiler (which ends up in bin\Debug\ as you said) should be perfect.