My program runs perfectly except when it is launched as a startup program. When launched as a start up program it is not able to find a first party DLL (or one of it's dependencies).
I get the following exception:
Could not find file or assembly X or one of it's dependencies: C:\Windows\SysWow64\X.dll
It is looking for the first party DLL in the C:\Windows\SysWOW64\ directory instead of in the local directory C:\Program Files\MyProgram\.
The confusing part is, if I launch the program manually everything works fine.
How do I go about finding the source of this problem? I tried using Fusion Log but it only told me the same thing as the exception: That it was trying to load from C:\Windows\SysWOW64 only.
I read that this can occur when your application uses Assembly.Load - The culprit program does use Assembly.LoadFrom - but again, this works fine except when done at start up.
Furthermore, the culprit program does have some [DllImport] attributes.
The issue was to do with using Assembly.LoadFrom and the current directory at start up.
The Assembly.LoadFrom states that it:
assemblyFile may be absolute or relative to the current directory, and the assembly is loaded into the domain of the caller.
When the program is set as a start up program the current directory is
C:\Windows\SysWOW64 for whatever reason.
As you can demonstrate by outputting Directory.GetCurrentDirectory().
Instead of using Assembly.LoadFrom which does not follow the normal probing I am using Assembly.Load as this searches the normal directories where DLLs are contained.
The load context contains assemblies found by probing: in the GAC, in a host assembly store if the runtime is hosted, or in the ApplicationBase and PrivateBinPath of the application domain. Most overloads of the Load method load assemblies into this context.
The load-from context contains assemblies for which the user provided a path not included in the directories searched by probing. LoadFrom, CreateInstanceFrom, and ExecuteAssembly are examples of methods that load by path.
This also explains why Fusion was telling me it was only searching for DLLs in C:\Windows\SysWOW64 - as that's what LoadFrom does, only searches in the directory given. Which in my case was the current directory.
Related
We have a .net plugin for an application that does not load a dependent library from the plugin folder.
Scenario:
Application (Revit.exe in program files) -> Loads plugin from c:\programdata\revit\plugindir\ourplugindir\<plugin.dll+dependencies>
On most machines, the load works fine. For the context of the issue, the dll dependencies are as follows
Revit.exe loads plugin.dll (revit.exe is in programfiles, plugins are in a separate predefined directory under programdata)
plugin.dll loads IdentityModel.dll (in ourplugindir directory)
IdentityModel.dll loads System.Text.Encodings.Web.DLL (in ourplugindir)
Successful probing looks like this:
IdentityModel requires System.Text.Encodings.Web.DLL
Initiate probing
Check GAC (fail)
Check root folder where Revit.exe is present (fail)
Check private sub folders where Revit.ext is present (fail)
Check ourplugindir (success)
On the machine where the plugin load fails, for some reason it does not probe ourplugindir and is hence unable to find System.Text.Encodings.Web.DLL.
The plugin dll is built using .net 4.7. The dependency is an indirect dependency.
From MSDN: https://learn.microsoft.com/en-us/dotnet/framework/deployment/how-the-runtime-locates-assemblies
Assembly location can also be determined using the current binding
context. This most often occurs when the Assembly.LoadFrom method is
used and in COM interop scenarios. If an assembly uses the LoadFrom
method to reference another assembly, the calling assembly's location
is considered to be a hint about where to find the referenced
assembly.
Since the plugins are dynamically loaded by Revit.exe, I can only assume that the application uses Assembly.LoadFrom or something similar to load the plugins.
So the question is, why does the runtime correctly probe and find the dependent dll in the plugin folder on some machines while it doesn't probe the same folder on other machines?
Have you tried using an assembly resolver?
I am working on assembly project and external program is executed through the assembly program.
System.Environment.CurrentDirectory assembly and Application.ExecutablePath paths are different due to external application.
System.Environment.CurrentDirectory is Assembly path.
Application.ExecutablePath External application path.
While deserialization it throw expcetion
assembly not found
because "Application.ExecutablePath " not having my dlls .
So i want to change use System.Environment.CurrentDirectory path for deserialization.
I recommend setting up an AssemblyResolve event on the current domain.
That way you can explicitly determine where your DLLs are and load them if they're not found automatically.
Jeremy Tammik wrote about it here:
http://thebuildingcoder.typepad.com/blog/2014/05/rvtva3c-assembly-resolver.html
The only thing to be careful of is that you should make sure the event is looking specifically for your DLLs (not any DLLs) - and that if it's not appropriate for you to load a DLL you return null (I occasionally run into scenarios where other people's addins don't implement this nicely, and it messes up my addin doing it).
I'm loading a dll with DllImport and the name of the dll (as it is in the same folder as my application):
[DllImport("myDll.dll")]
and till here all works fine if application is opened from the same location. But if I run cmd and type:
"C:\path\to\my\application\app.exe"
the application opens but the dll called from the application itself isn't loaded anymore.
So to sum up if I open manually app.exe from C:\path\to\my\application\ the DllImport works fine and loads the dll in the same path.
If I open the application from another location, it isn't loaded anymore.
Any suggestions? Tried also
[DllImport("C:\\path\\to\\my\\application\\myDll.dll")]
and
[DllImport("\\myDll.dll")]
but no way, it doesn't work.
The DLL is located using the DLL search order, as described here: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682586.aspx. Since the DLL is in the same directory as the executable, it will be found because the directory containing the executable is the first place that the system searches.
So we can conclude that the DLL is found by the loader. Since you report that the behaviour changes when the working directory changes it would seem that is the problem. Your DLL has a dependency on the working directory. It's probably a mistake to have such a dependency. You should find a way to avoid such a dependency.
I'm confused by the behavior of Assembly.LoadFrom. In my application I call Assembly.LoadFrom to a .NET .exe and start it using EntryPoint.Invoke (this odd approach is useful for building a launcher app on non-Windows platforms).
I had assumed that since the assemblyFile was in a different folder, that it would fail to find some managed .dll dependencies that are located in the same folder as it. But it worked; it didn't fail...
It would appear that when I called Assembly.LoadFrom(assemblyFile), it checked the folder containing assemblyFile for managed dependencies of assemblyFile. I didn't expect this. What would happen if that assembly had unmanaged dependencies (such as a DllImport), would it still search that same directory? And is this behavior framework specific?
The Assembly has nothing to do with loading unmanagged libraries. What does the loading for unmanaged libraries is the DllImport call which is lazy (does not load till the first call).
DllImport in turn (in .NET, I don't know what Mono does on other platforms) calls LoadLibary on windows. LoadLibary has a known set of rules for how it resolves it's dependencies:
If a DLL with the same module name is already loaded in memory, the system uses the loaded DLL, no matter which directory it is in. The system does not search for the DLL.
If the DLL is on the list of known DLLs for the version of Windows on which the application is running, the system uses its copy of the known DLL (and the known DLL's dependent DLLs, if any). The system does not search for the DLL. For a list of known DLLs on the current system, see the following registry key: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\KnownDLLs.
If those two points are not met and SafeDllSearchMode is enabled (it is by default for for XP SP2 and newer) it uses the following order
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.
So to answer your question, no the directory your managed assembly is located in is not searched when looking for unmanaged assemblies, only the directory of the application that loaded your managed assembly.
However all hope is not lost, you can call SetDLLDirectory and add the folder of your managed assembly and it will include it in the search when looking for the unmanaged DLL, it will change the search order to
The directory from which the application loaded.
The directory specified by the lpPathName parameter (in the SetDLLDirectory call).
The system directory. Use the GetSystemDirectory function to get the path of this directory. The name of this directory is System32.
The 16-bit system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
The directories that are listed in the PATH environment variable.
If you need to add more than one folder for searching see the MSDN for documenation on AddDllDirectory for the steps required to allow for multiple search directories.
None of the .NET specific configuration affects the way unmanaged DLLs are located. Normal operating system search rules are in effect, regardless of how the CLR finds managed assemblies. The underlying OS call on Windows is LoadLibrary(), a winapi function that has no support for altering search rules itself. You can specify a full path name to avoid searching but that's almost never practical in pinvoke.
If this is a problem then you'll have to write explicit code to help the OS locate the file. Common techniques are pinvoking SetDllDirectory(), altering the PATH environment variable with Environment.SetEnvironmentVariable() and changing the default directory by assigning Environment.CurrentDirectory
This is strange...
In a project of mine I need to load external dll's at runtime. I've done this frequently before and I thought I had stepped on all the mines there where but this one has got the best of me so far.
It's very very basic really. I use Assembly.LoadFrom("c:\\test\\mytestlibrary.dll") but Fusion cannot find the file (I get a FileNotFoundException).
I have examined the fusion logs as usual and it just doesn't make sense. Are there some circumstances that would somehow prevent Fusion from finding a file even when I provide the complete and absolute path to it? I suspected the dll in question needed some other assembly but looking at the fusion logs doesn't indicate this. Besides, the test library references nothing that's not also referenced by the host assembly.
Any suggestions?
Perhaps this blog entry by Suzzanne Cook will provide some clues?
For FileNotFoundException:
At the bottom of the log will be the paths that Fusion tried probing for this assembly. If this was a load by path (as in Assembly.LoadFrom()), there will be just one path, and your assembly will need to be there to be found. Otherwise, your assembly will need to be on one of the probing paths listed or in the GAC if it's to be found.
You may also get this exception if an unmanaged dependency or internal module of the assembly failed to load. Try running depends.exe on the file to verify that unmanaged dependencies can be loaded. Note that if you re using ASP.NET, the PATH environment variable it's using may differ from the one the command line uses. If all of them could be loaded, try ildasm.exe on the file, double-click on "MANIFEST" and look for ".file" entries. Each of those files will need to be in the same directory as the manifest-containing file.
-- http://blogs.msdn.com/b/suzcook/archive/2003/05/29/57120.aspx
IIRC, the fusion log should also show the list of paths sought. Is your path included? Further, is the assembly already loaded as a project reference or otherwise previously in the same app domain?