I'm running a WCF application CoreApplication whose VS project has a reference to AncillaryProject. CoreApplication uses a class Provider from AncillaryProject; however, it is never explicitly referenced - it's invoked via Reflection.
My problem is that sometimes CoreApplication fails to find Provider because AncillaryProject does not come up in the call to GetAssemblies(). Sometimes it works fine, but sometimes (I'm guessing it may be after a JIT) it fails.
Here's my original code:
var providers = from d in AppDomain.CurrentDomain.GetAssemblies()
from c in d.GetTypes()
where typeof(BaseProvider).IsAssignableFrom(c)
select c;
After looking at this question, I tried using GetReferencedAssemblies():
var allAssemblies = AppDomain.CurrentDomain.GetAssemblies();
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
{
allAssemblies = allAssemblies.Union(
a.GetReferencedAssemblies()
.Select(b => System.Reflection.Assembly.Load(b)));
}
var providers = from d in allAssemblies
from c in d.GetTypes()
where typeof(BaseProvider).IsAssignableFrom(c)
select c;
I realize that the question I referenced solves the problem through dynamically loading all dll files in the bin directory, but that doesn't sound particularly good to me. Is there a better way to do this, or is .NET simply not loading the other Assemblies in at all? How does this work under the hood, and is there anything I can do about it?
According to Microsoft documentation AppDomain.CurrentDomain.GetAssemblies() gets the assemblies that have been loaded into the execution context of this application domain. About AppDomain.CurrentDomain.GetAssemblies()
It seems that you need to change strategy of loading the assemblies you need from using the appdomain to looking for dlls in your applications folder.
I found a discussion on a similar problem here
You can handle the AssemblyResolve event and load AncillaryProject.dll in that event handler
http://msdn.microsoft.com/en-us/library/ff527268.aspx
You should download the .NET Development SDK and start up FuslogVw.exe (fusion log viewer). It will report on CLR Application trying to resolve .NET dependencies. It will show you were it is looking and how it evaluates the candidates located at those places.
Related
I have an application, linked with other DLLs, according to MEF technology.
The "core" of that MEF looks as follows:
var cat = new AggregateCatalog();
cat.Catalogs.Add(new AssemblyCatalog(Assembly.GetAssembly(typeof(MyApp))));
cat.Catalogs.Add(new DirectoryCatalog(...));
ModuleCatalog = new CompositionContainer(cat);
MyAppModules = ModuleCatalog.GetExportedValues<IMyAppModule>();
I would like to downgrade one of the DLLs (from 84 to 83). I have done this in some of the DLLs which refer to the newest version.
However, the last line of code (with the GetExportedValues) generates an exception, mentioning Could not load file or assembly 'Relevant_DLL, Version=1.0.84.1.
This means that still at least one other DLL is referring to that 84 version. How can I know which one it is? (As you can see, I'm not able to launch the application, so debugging or using Process Explorer is out of the question)
You can use the Fusion logging to diagnose binding errors.
See this question for more info: How to enable assembly bind failure logging (Fusion) in .NET
I highly recommend this answer with using Fusion++: https://stackoverflow.com/a/56067961/6594467
I've been working along very happily in an app in VS2017. Debugging just fine. Then, all of a sudden...
When I am debugging and try to hover over a variable, I don't get the normal popup with details of the object. And if I put it in the Watch, I get this for the value:
Internal error in the C# compiler
I closed and re-opened VS, then I rebooted. Still get the same error.
There is very little about this out there. Has anyone ever seen this before?
Found an answer here that worked:
Please view menu Tools-> Options > debugging > enable Use Managed
Compatibility Mode, and then debug your dump file, how about the
result?
No clue why it was working and then suddenly stopped. But, it's working again now.
Edit LONG After:
This recently got an upvote, so the problem is still out there, lurking. I should add that after this solution solved the problem, it soon came back and nothing I could try got rid of it. I ended up totally removing all traces of VS and re-installing it, which pretty much took all day.
Edit LONG LONG After:
Even after uninstalling and re-installing VS, the problem eventually came back. This time I took my laptop to my employer's helpdesk team and they ended up just giving me a new laptop.
If that's isn't an option for you, may God help you, because I have no idea. I found some threads about this on various MS forums, and the MS employees always just close them with "not able to reproduce".
I also run into this problem right after the following code:
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.RunAndCollect);
Eventually I understood that it's happened only when passing the executing assembly (which received by calling to Assembly.GetExecutingAssembly()).
I assuming that the compiler don't know how to debug the executing assembly in such case (maybe because it's cause to a conflict between those assemblies).
Soluting:
Don't use executing assembly
I ran into this problem as well.
It turns out that I had two using statements that defined a type from a specific namespace.
using Type = namespaces.Type;
namespace namespaces
{
using Type = otherNamespace.Type;
...
}
It's probably a rare occurrence in the world outside my company, but if you're running into this error, it's something to consider.
I was running into this with a web project in VS2022, rolled back to VS2019, turned on Managed Compatibility Mode, still had the error. Turned out it was just my smelly code.
var assembly = AssemblyName.GetAssemblyName(Assembly.GetExecutingAssembly().Location);
var appDomain = System.Threading.Thread.GetDomain();
var assemblyBuilder = appDomain.DefineDynamicAssembly(assembly, AssemblyBuilderAccess.Run);
I had this in a startup routine. Once the debugger hit that third line of code I start getting the error. I can inspect variables right up to that line.
In my case, this was due to dynamic assembly loading.
The issue would only appear after the debugger stepped over a call to the Assembly.LoadFrom(...) method that tried to load an assembly with the same name as an assembly in the assembly loader context.
I fixed the issue by checking if the assembly that was about to be loaded was already in the assembly loader context. If it was, the assembly would be skipped.
Rough code of my solution:
// Load the assembly to the reflection context to check for any conflicts in the assembly load context
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(assemblyPath);
AssemblyName assemblyName = assembly .GetName();
List<AssemblyName> existingAssemblies = AppDomain.CurrentDomain
.GetAssemblies()
.Select(assembly => assembly.GetName())
.Where(x => x.Name == assemblyName.Name && x.Version.ToString() != assemblyName.Version.ToString())
.ToList();
if (existingAssemblies.Any())
{
// Skip the assembly if there are already assemblies with the same name but a different version
return;
}
else
{
// Load the assembly into the assembly loader context
assembly = Assembly.LoadFrom(assemblyPath);
}
References:
Strange behavior when loading assemblies and its dependencies programatically
https://github.com/dotnet/runtime/issues/10859
https://github.com/dotnet/roslyn/issues/19257
I have many .NET WinForm applications that use a particular .dll assembly. The company that puts out this particular product occasionally releases a new version. Since they offer strong-name assemblies, I need to download the new version of their .dll, but also go through every one of my applications and rebuild them with the reference to the new .dll file. Then, I need to deploy every one of my applications. It is a lot of time, paperwork, and testing to do this, so I asked the maker of this .dll for a workaround.
They gave me this Stack Overflow thread as a suggestion and said it would "work perfectly." However, I haven't quite figured this out.
In my application, I have added this to Program.cs:
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
{
AssemblyName requestedName = new AssemblyName(e.Name);
if (requestedName.Name.ToUpper().StartsWith("THE.PRODUCT.NAME"))
{
AssemblyName assemblyName = AssemblyName.GetAssemblyName(#"C:\The.Product.Name.15.5.0.0.dll");
return Assembly.LoadFile(#"C:\The.Product.Name.15.5.0.0.dll");
}
else
{
return null;
}
};
}
In order to get this to work, I have to remove/rename the normal product .dll file in its default location, and I put the new version in simply the root C:\ directory (as the code shows). The reason for removing the .dll from its normal location is so the "AssemblyResolve" handler fires, because the referenced assembly cannot be found.
Anyway, with the above code my application gives the following exception: "An attempt was made to load an assembly from a network location which would have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, ..." And so on.
What exactly am I doing wrong here? Does anyone even understand what I'm trying to accomplish? I can't believe this hasn't been done before but I am kind of at a loss at this point.
Any help would be greatly appreciated. Thank you.
I am working on a plug-in based framework that allows plug-ins to exchange data between one another based on some contract (interface).
Currently, a Windows Service can load the plug-ins in two ways:
In the same AppDomain as the Windows Service hosting the plug-ins
In another process that the Windows service communicates with via named pipes (WCF).
This works nicely most of the time. However, there are certain scenarios where one plug-in might reference an assembly, and another plug-in references a newer version of the assembly. In this scenario, I always want the newer version of the dependency to be loaded regardless of which plug-in is loaded first.
Here is the folder structure:
Windows Service Directory (AppDomainBase)
Plugins
Plugin1
Plugin1.dll
SharedDependency.dll (1.0.0.0)
Plugin2
Plugin2.dll
SharedDependency.dll (1.0.1.0)
I have done a lot of research already, and tried many different things. That said:
I cannot redirect assembly bindings via an app.config file. Although this works, it is not practical because I do not know all of the dependencies ahead of time and cannot add every single one to the app.config.
I can't use the GAC
I don't want multiple versions of the same assembly loaded, just the newest one.
I have read about Assembly.Load, LoadFrom, and LoadFile and have tried using all of them. I am still not 100% clear on the difference between Load and LoadFrom. They both seem to automatically load each plug-in's dependencies through fusion and probing from the directory where they are loaded.
My current solution is to search all sub-directories of the AppDomainBase to find and cache all of the DLLs in each plug-in's folder. If I encounter the same assembly more than once, I always keep track of the newest version and its location.
Then, I load each plug-in by calling Assembly.LoadFile so that no dependencies are loaded by fusion. I am subscribing to the AppDomain.CurrentDomain.AssemblyResolve event. When that event is raised, I inspect the Name of the assembly to determine which assembly should be loaded that was pre-cached, and then I load it by calling Assembly.Load.
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
try
{
Log(string.Format("Resolving: {0}", args.Name));
// Determine if the assembly is already loaded in the AppDomain. Only the name of the assembly is compared here.
var asm = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(a => a.GetName().FullName.Split(',')[0] == args.Name.Split(',')[0]);
if (asm == null)
{
// The requsted assembly is not loaded in the current AppDomain
Log(string.Format("Assembly is not loaded in AppDomain: [{0}]", args.Name));
// Determine if the assembly is one that has already been found and cached
var asmName = _RefCandidates.Find(a => a.Name.FullName.Split(',')[0] == args.Name.Split(',')[0]);
if (asmName != null)
{
// The assembly exists in the cache, but has not been loaded. Load it.
Log(string.Format("Pre-loaded assembly found in cache. Loading: [{0}], [{1}]", asmName.Name, asmName.Name.CodeBase));
return Assembly.LoadFile(asmName.File.FullName);
}
}
else
Log(string.Format("Assembly is already loaded in AppDomain: [{0}], [{1}]", asm.GetName(), asm.GetName().CodeBase));
return asm;
}
catch (Exception ex)
{
Logger.Write(ex, LogEntryType.Error);
return null;
}
}
First of all, what is the best way to accomplish what I need to do, and what am I doing wrong?
Second, what happens if a dependency in the chain is expecting to reference something that is in the GAC? I assume it will no longer be found since I am using LoadFile and skipping fusion all together. Also, I have read some things about serialization not working with LoadFile. What specifically? How about resource assemblies?
This model is assuming that all newer versions of dependent assemblies are backward compatible since I'll be loading only the newest version.
Any input is greatly appreciated.
You can't load assembly manually, if the resolution of this assembly not fails(only in this case AssemblyResolve is raised).
So, your plugin architecture can not be implemented with Assembly.Load* methods.
There are two paths now.
First(not recommended): somehow remove the restriction(for example use Assembly.Load at start, and in any point you need an object, search for CurrentDomain assemblies, pick right assembly and by reflection construct objects and use them).
Second(recommended): review your initial problem and search for solution, that can be simple to implement and maintain.
Take a look, what Managed Extensibility Framework offers for plugin architecture.
SharpDevelop folks wrote a book, telling us about their plugins tree.
Find your way.
Also see this link about assembly loading context to understand why Assembly.Load without AssemblyResolve event will not work.
Before you say anything, I have read the previously asked questions about this issue. The answers there did not fix my problem.
It's pretty simple, I guess, if you know the answer. Here's my problem:
I've got a solution with several projects, I'm creating a plugin-based application where I use Reflection to load all assemblies. This part goes fine, I load all my assemblies like so
var filePaths = Directory.GetFiles(#"C:\CustomerServiceModule\", "*.dll", SearchOption.AllDirectories).Where(n => n.Contains("bin"));
foreach (var f in filePaths)
{
Assembly.LoadFile(f);
}
Now I want to create an instance of a type, so I can work with it:
var assembly = AppDomain.CurrentDomain.GetAssemblies().Where(a => a.ManifestModule.Name == "Kayako.dll").SingleOrDefault();
var name = assembly.GetTypes();
var type = assembly.GetType("Kayako.KayakoData");
var lol = Activator.CreateInstance(type);
This goes badly because inside KayakoData I have this:
KayakoService _service = new KayakoService("xxx", "yyy", "zzz");
This service is an assembly that works, I've used it before. Version number is fine, there's nothing in the GAC that overrides it, I can't see any errors using Assembly Binding Log Viewer. I still get this error:
[System.LoadTypeException]{"Could not load type 'KayakoRestAPI.KayakoService' from assembly 'KayakoRestAPI, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.":"KayakoRestAPI.KayakoService"}
Anyone have any bright ideas? I've been staring myself blind at this. If I remove the service part from KayakoData the whole thing works, but I really need to run the service.
Quote from the documentation of the LoadFile method:
Use the LoadFile method to load and examine assemblies that have the
same identity, but are located in different paths. LoadFile does not
load files into the LoadFrom context, and does not resolve
dependencies using the load path, as the LoadFrom method does.
LoadFile is useful in this limited scenario because LoadFrom cannot be
used to load assemblies that have the same identities but different
paths; it will load only the first such assembly.
Conclusion: try LoadFrom in order to load dependent assemblies as well.
You just need to change the version of .dll from assembly info of that project. Then rebuild your solution.