How to load an assembly from GAC with wildcards in version number - c#

In our application, we have the need to dynamically load 3rd-party assemblies where we do not know in advance all released assembly version numbers. All we know is, for example, that the major version number for the assembly must be "12". On a PC, multiple versions of the same assembly may be installed, having both higher and lower major version numbers.
I.e. we would need something like
Assembly myAssembly = Assembly.Load("SampleAssembly, Version=12.*.*.*");
and if the assembly versions 11.1.2.3, 12.7.6.5, and 13.9.8.7 are installed, it should load version 12.7.6.5.
I.e. it should be possible to specify wildcards for version number components and it also should be possible to omit Culture and PublicKeyToken.
When we do this with Assembly.Load(), we get a FileNotFoundException.
We cannot use Assembly.LoadWithPartialName() because it always loads the assembly with the highest version number, but we want a specific major version number instead, which possibly is less than the greatest installed assembly version number.
Is it possible to do this?

You could manually list the content of the GAC and compare it to your wildcards as so
class Program
{
static void Main(string[] args)
{
var assemblyName = "SimpleAssembly";
var versionRegex = new Regex(#"^12\.");
var assemblyFile = FindAssemblyFile(assemblyName, versionRegex);
if (assemblyFile == null)
throw new FileNotFoundException();
Assembly.LoadFile(assemblyFile.FullName);
}
static FileInfo FindAssemblyFile(string assemblyName, Regex versionRegex)
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "assembly", "GAC_MSIL", assemblyName);
var assemblyDirectory = new DirectoryInfo(path);
foreach (var versionDirectory in assemblyDirectory.GetDirectories())
{
if (versionRegex.IsMatch(versionDirectory.Name))
{
return versionDirectory.GetFiles()[0];
}
}
return null;
}
}

Related

How to load assemblies in ASP.NET Core 1.0 RC2

I am migrating my web app from ASP.NET Core RC1 to RC2. I'm trying to load my referenced class libraries.
This code snippet doesn't work with RC2 any more:
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// libraryManager is null ....
ILibraryManager libraryManager = app.GetService<ILibraryManager>();
List<Assembly> result = new List<Assembly>();
IEnumerable<Library> libraries = libraryManager.GetLibraries();
IEnumerable<AssemblyName> assemblyNames = libraries.SelectMany(e => e.Assemblies).Distinct();
assemblyNames = Enumerable.Where(assemblyNames, e => e.Name.StartsWith("projectNamespace"));
foreach (AssemblyName assemblyName in assemblyNames)
{
Assembly assembly = Assembly.Load(assemblyName);
.
.
.
}
}
}
I found a solution. I'm using now DependencyContext instead of ILibraryManager
var loadableAssemblies = new List<Assembly>();
var deps = DependencyContext.Default;
foreach (var compilationLibrary in deps.CompileLibraries)
{
if (compilationLibrary.Name.Contains(projectNamespace))
{
var assembly = Assembly.Load(new AssemblyName(compilationLibrary.Name));
loadableAssemblies.Add(assembly);
}
}
I think stevo has made 2 wrong assumptions:
1) that project namespace should be part of compilation library name.
2) that compilation library name is the same as binary name.
First one is wrong when you change it in project settings.
Second one is wrong when you specify it in buildOptions in project.json.
So your idea is correct but implementation is wrong.
To fix that we need to forget about resolving by namespace until assembly is loaded.
I guess since all assemblies will be loaded in any case we will not get big performance lag.
But it is not panacea... assembly can have multiple root namespaces inside!
So maybe better way will be to define some attribute on assembly level and check it instead of namespace.
In any case if you want to limit your search by assembly name it should be made like this:
IEnumerable<AssemblyName> names = DependencyContext.Default.GetDefaultAssemblyNames();
foreach (AssemblyName name in names)
{
if (name.Name.StartsWith("MyRoot") == true)
{
Assembly assembly = Assembly.Load(name);
// Process assembly here...
// I will check attribute for each loaded assembly found in MyRoot.
}
}

Get Type from Fully qualified name only in Windows Store App

I have a string containing a full qualified name like MyNamespace.MyType which I know is in a loaded assembly.
I need to get the Type instance of this, in a windows store app.
The issue I'm having is that while there is some reflection classes in windows store apps, it's very limited, and I simply can't use what I've been using for a desktop app.
If I can get an assembly I can find my type within it so I'm currently trying to get all loaded assemblies, which I can't do easily as AppDomain doesn't exist.
I found the following:
private async System.Threading.Tasks.Task<IEnumerable<Assembly>> GetAssembliesCore()
{
var folder = Windows.ApplicationModel.Package.Current.InstalledLocation;
List<Assembly> assemblies = new List<Assembly>();
foreach (Windows.Storage.StorageFile file in
await folder.GetFilesAsync().AsTask().ConfigureAwait(false))
{
if (file.FileType == ".dll")
{
AssemblyName name = new AssemblyName() { Name = file.Name };
Assembly asm = Assembly.Load(name);
assemblies.Add(asm);
}
}
return assemblies;
}
I added the .AsTask().ConfigureAwait(false) to stop it hanging, but it now fails trying to load the assembly:
"Could not load file or assembly 'FDE.Audio.dll' or one of its dependencies.
The system cannot find the file specified.":"FDE.Audio.dll"
Is there something I need to set up in the manifest? Something else?
How can I load an assembly in my program's folder (AppX I think)?
Try to extract file name without extension
var filename = file.Name.Substring(0, file.Name.Length - file.FileType.Length);
AssemblyName name = new AssemblyName() { Name = filename };
Assembly asm = Assembly.Load(name);

How to get the current product version in C#?

How can I programmatically get the current product version in C#?
My code:
VersionNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
I am getting VersionNumber=1.0.0.0, but the current version is 1.0.0.12.
There are three versions: assembly, file, and product. To get the product version:
using System.Reflection;
using System.Diagnostics;
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fileVersionInfo.ProductVersion;
I got the answer to my question its Just give the reference to System.Deployment.Application and though it wont work in developement of the visual studio but it will work once the application is deployed.
//using System.Deployment.Application;
//using System.Reflection;
public string CurrentVersion
{
get
{
return ApplicationDeployment.IsNetworkDeployed
? ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString()
: Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
System.Reflection.Assembly.GetEntryAssembly().GetName().Version
Another approach to getting the product version (which is specified using the AssemblyInformationalVersionAttribute) is
private static string AssemblyProductVersion
{
get
{
object[] attributes = Assembly.GetExecutingAssembly()
.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
return attributes.Length == 0 ?
"" :
((AssemblyInformationalVersionAttribute)attributes[0]).InformationalVersion;
}
}
Try this:
var thisApp = Assembly.GetExecutingAssembly();
AssemblyName name = new AssemblyName(thisApp.FullName);
VersionNumber = "v. " + name.Version;
Also, see this Microsoft Doc on the AssemblyName.Version property.
In C# you need to use reflection and diagnostics
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fileVersionInfo.ProductVersion;
All these answers ask for the assembly with .GetExecutingAssembly().
If you have this code in a dll, it will return the dll version number.
Swap that call for GetCallingAssembly() to get the place in your code that wanted to know.
/// <summary>
/// Returns version like 2.1.15
/// </summary>
public static String ProductVersion
{
get
{
return new Version(FileVersionInfo.GetVersionInfo(Assembly.GetCallingAssembly().Location).ProductVersion).ToString();
}
}
var productVersion = FileVersionInfo.GetVersionInfo(typeof(SomeClassFromDesiredAssembly).Assembly.Location).ProductVersion;
I had the same issue as most of you. It would always show 1.0.0.0 unless you manually went in and updated assemblyInfo.cs to the version you wanted to display. I think we wanted to display the publish version-revision number under the project properties but that doesn't seem to be an option (from what I've read).
I'm not sure if back when these comments were made this existed, but now in the assemblyinfo.cs there is a way to do this automatically. I too was not content with having to manually update these with every publish.
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.*")]
That * auto-increments with each publish. It won't be the same as the publish number you see under the project properties, but it definitely increments and is definitely better than doing it by hand.
You then have a couple options to display it as mentioned above. I personally used this which I found on another site
Version version = Assembly.GetExecutingAssembly().GetName().Version;
lblRevision.Text = String.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);

AppDomain.CurrentDomain.AssemblyResolve asking for a <AppName>.resources assembly?

using the code How to embed a satellite assembly into the EXE file provided by csharptest.net, I've created a custom assembly resolver and embedded my assemblies in my resources.
I can successfully resolve my assemblies used in but somehow AppDomain.CurrentDomain.AssemblyResolve asks for an assembly called 'AppName.resources' specifically "MyProgram.resources, Version=0.15.3992.31638, Culture=en-US, PublicKeyToken=null" which i don't know how to resolve?
I've tried to disable loading my custom assemblies from resources (placed all my assembly dll's in program directory) and just enabled AppDomain.CurrentDomain.AssemblyResolve, but it was still asking for it.
I'm a bit confused about this, will appreciate a lot if you can help me on this.
Here's my code for interested ones;
static Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
Assembly assembly = null;
string name = args.Name.Substring(0, args.Name.IndexOf(','));
if (name == "MyProgram.resources") return null;
else name = string.Format("MyProgram.Resources.Assemblies.{0}.dll", name);
lock (_loadedAssemblies)
{
if (!_loadedAssemblies.TryGetValue(name, out assembly))
{
using (Stream io = Assembly.GetExecutingAssembly().GetManifestResourceStream(name))
{
if (io == null)
{
MessageBox.Show("MyProgram can not load one of it's dependencies. Please re-install the program", string.Format("Missing Assembly: {0}", name), MessageBoxButtons.OK, MessageBoxIcon.Error);
Environment.Exit(-1);
}
using (BinaryReader binaryReader = new BinaryReader(io))
{
assembly = Assembly.Load(binaryReader.ReadBytes((int)io.Length));
_loadedAssemblies.Add(name, assembly);
}
}
}
}
return assembly;
}
Answering on my own;
Adding this line to AssemblyInfo.cs solves it and resolver will not get asked for resources any-more.
[assembly: NeutralResourcesLanguageAttribute("en-US", UltimateResourceFallbackLocation.MainAssembly)]
Though this is a work-around should be carefully considered multi-language applications.
More Info:
https://connect.microsoft.com/VisualStudio/feedback/details/526836/wpf-appdomain-assemblyresolve-being-called-when-it-shouldnt
http://blogs.msdn.com/b/kimhamil/archive/2008/11/11/what-does-the-neutralresourceslanguageattribute-do.aspx
http://forums.devshed.com/net-development-87/c-wpf-appdomain-assemblyresolve-being-called-when-it-shouldn-t-669567.html
http://blogs.msdn.com/b/microsoft_press/archive/2010/02/03/jeffrey-richter-excerpt-2-from-clr-via-c-third-edition.aspx
This approach fails for machines with non en-US cultures. A better approach is ignoring resources on assembly resolver;
public Assembly Resolver(object sender, ResolveEventArgs args)
{
lock (this)
{
Assembly assembly;
AssemblyName askedAssembly = new AssemblyName(args.Name);
string[] fields = args.Name.Split(',');
string name = fields[0];
string culture = fields[2];
// failing to ignore queries for satellite resource assemblies or using [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)]
// in AssemblyInfo.cs will crash the program on non en-US based system cultures.
if (name.EndsWith(".resources") && !culture.EndsWith("neutral")) return null;
/* the actual assembly resolver */
...
}
}
My situation was a bit more complex and the above solution did not work for me. (That is changing the AssemblyInfo.cs file)
I have moved all my form and image resources to a seperate dll and the moment any of the images are used the 'filenotfoundexception' exception is thrown.
The important information is the following:
Beginning with the .NET Framework 4, the ResolveEventHandler event is raised for all assemblies, including resource assemblies. See the following reference
https://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve(v=vs.110).aspx
The solution turned out to be very simple. If a resource file is requested in the form 'dllname.resources.dll' always return null;
Here is the event code that I have adapted from other samples found. (I have commented the debugging lines - un-comment them if you have a problem using the code.
Add this line in your class. It is used to prevent loading a dll more than once
readonly static Dictionary<string, Assembly> _libs = new Dictionary<string, Assembly>();
This is the event method.
private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
Assembly assembly = null;
string keyName = new AssemblyName(args.Name).Name;
if (keyName.Contains(".resources"))
{
return null; // This line is what fixed the problem
}
if (_libs.ContainsKey(keyName))
{
assembly = _libs[keyName]; // If DLL is loaded then don't load it again just return
return assembly;
}
string dllName = DllResourceName(keyName);
//string[] names = Assembly.GetExecutingAssembly().GetManifestResourceNames(); // Uncomment this line to debug the possible values for dllName
using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(dllName))
{
if (stream == null)
{
Debug.Print("Error! Unable to find '" + dllName + "'");
// Uncomment the next lines to show message the moment an assembly is not found. (This will also stop for .Net assemblies
//MessageBox.Show("Error! Unable to find '" + dllName + "'! Application will terminate.");
//Environment.Exit(0);
return null;
}
byte[] buffer = new BinaryReader(stream).ReadBytes((int) stream.Length);
assembly = Assembly.Load(buffer);
_libs[keyName] = assembly;
return assembly;
}
}
private static string DllResourceName(string ddlName)
{
if (ddlName.Contains(".dll") == false) ddlName += ".dll";
foreach (string name in Assembly.GetExecutingAssembly().GetManifestResourceNames())
{
if (name.EndsWith(ddlName)) return name;
}
return ddlName;
}

How to get Names of DLLs used by application

I'm looking the way to read all assemblies (.dlls) used by my app.
In a standard C# project there is "References" folder, when it is expanded I can read all libraries used.
My goal is programatically read all assemblies which are used by each project in my solution.
Finally I'd like to see what libraries are used by my compiled *.exe application.
Have you looked at Assembly.GetReferencedAssemblies?
Note that any references you don't use won't end up being emitted into the metadata, so you won't see them at execution time.
I've used GetReferencedAssemblies recursively before now to find a named type without having to specify the assembly.
To do this properly, you need to walk the assemblies, picking up the dependencies... if your exe needs Dll_A, and Dll_A needs Dll_B (even if the exe doesn't reference it), then your exe also needs Dll_B.
You can query this (on any assembly) via reflection; it takes a little work (especially to guard against circular references, which do happen; here's an example that starts at the "entry assembly", but this could just as easily be any assembly:
List<string> refs = new List<string>();
Queue<AssemblyName> pending = new Queue<AssemblyName>();
pending.Enqueue(Assembly.GetEntryAssembly().GetName());
while(pending.Count > 0)
{
AssemblyName an = pending.Dequeue();
string s = an.ToString();
if(refs.Contains(s)) continue; // done already
refs.Add(s);
try
{
Assembly asm = Assembly.Load(an);
if(asm != null)
{
foreach(AssemblyName sub in asm.GetReferencedAssemblies())
{
pending.Enqueue(sub);
}
foreach (Type type in asm.GetTypes())
{
foreach (MethodInfo method in type.GetMethods(
BindingFlags.Static | BindingFlags.Public |
BindingFlags.NonPublic))
{
DllImportAttribute attrib = (DllImportAttribute)
Attribute.GetCustomAttribute(method,
typeof(DllImportAttribute));
if (attrib != null && !refs.Contains(attrib.Value))
{
refs.Add(attrib.Value);
}
}
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
refs.Sort();
foreach (string name in refs)
{
Console.WriteLine(name);
}
System.Reflection.Assembly []ar=AppDomain.CurrentDomain.GetAssemblies();
foreach (System.Reflection.Assembly a in ar)
{
Console.WriteLine("{0}", a.FullName);
}
You can use AppDomain.GetAssemblies.
But this will give ALL assemblies used explicitly or implicitly in your application.
If you have an Assembly object, you can call GetReferencedAssemblies() on it to get any references that assembly uses. To get a list of assemblies the currently running project uses, you can use:
System.Reflection.Assembly.GetExecutingAssembly().GetReferencedAssemblies()
I guess you can use:
AssemblyName[] assemblies = this.GetType().Assembly.GetReferencedAssemblies();

Categories