Programmatically get the version number of a DLL - c#

Is it possible to get the version number programmatically from any .NET DLL?
If yes, how?

This works if the dll is .net or Win32. Reflection methods only work if the dll is .net. Also, if you use reflection, you have the overhead of loading the whole dll into memory. The below method does not load the assembly into memory.
// Get the file version.
FileVersionInfo myFileVersionInfo = FileVersionInfo.GetVersionInfo(#"C:\MyAssembly.dll");
// Print the file name and version number.
Console.WriteLine("File: " + myFileVersionInfo.FileDescription + '\n' +
"Version number: " + myFileVersionInfo.FileVersion);
From: http://msdn.microsoft.com/en-us/library/system.diagnostics.fileversioninfo.fileversion.aspx
original source

Assembly assembly = Assembly.LoadFrom("MyAssembly.dll");
Version ver = assembly.GetName().Version;
Important:
It should be noted that this is not the best answer to the original question. Don't forget to read more on this page.

First of all, there are two possible 'versions' that you might be interested in:
Windows filesystem file version, applicable to all executable files
Assembly build version, which is embedded in a .NET assembly by the compiler (obviously only applicable to .NET assembly dll and exe files)
In the former case, you should use Ben Anderson's answer; in the latter case, use AssemblyName.GetAssemblyName(#"c:\path\to\file.dll").Version, or Tataro's answer, in case the assembly is referenced by your code.
Note that you can ignore all the answers that use .Load()/.LoadFrom() methods, since these actually load the assembly in the current AppDomain - which is analogous to cutting down a tree to see how old it is.

Here's a nice way using a bit of reflection to get a version of a DLL containing a particular class:
var ver = System.Reflection.Assembly.GetAssembly(typeof(!Class!)).GetName().Version;
Just replace !Class! with the name of a class which is defined in the DLL you wish to get the version of.
This is my preferred method because if I move the DLLs around for different deploys I don't have to change the filepath.

To get it for the assembly that was started (winform, console app, etc...)
using System.Reflection;
...
Assembly.GetEntryAssembly().GetName().Version

Kris, your version works great when needing to load the assembly from the actual DLL file (and if the DLL is there!), however, one will get a much unwanted error if the DLL is EMBEDDED (i.e., not a file but an embedded DLL).
The other thing is, if one uses a versioning scheme with something like "1.2012.0508.0101", when one gets the version string you'll actually get "1.2012.518.101"; note the missing zeros.
So, here's a few extra functions to get the version of a DLL (embedded or from the DLL file):
public static System.Reflection.Assembly GetAssembly(string pAssemblyName)
{
System.Reflection.Assembly tMyAssembly = null;
if (string.IsNullOrEmpty(pAssemblyName)) { return tMyAssembly; }
tMyAssembly = GetAssemblyEmbedded(pAssemblyName);
if (tMyAssembly == null) { GetAssemblyDLL(pAssemblyName); }
return tMyAssembly;
}//System.Reflection.Assembly GetAssemblyEmbedded(string pAssemblyDisplayName)
public static System.Reflection.Assembly GetAssemblyEmbedded(string pAssemblyDisplayName)
{
System.Reflection.Assembly tMyAssembly = null;
if(string.IsNullOrEmpty(pAssemblyDisplayName)) { return tMyAssembly; }
try //try #a
{
tMyAssembly = System.Reflection.Assembly.Load(pAssemblyDisplayName);
}// try #a
catch (Exception ex)
{
string m = ex.Message;
}// try #a
return tMyAssembly;
}//System.Reflection.Assembly GetAssemblyEmbedded(string pAssemblyDisplayName)
public static System.Reflection.Assembly GetAssemblyDLL(string pAssemblyNameDLL)
{
System.Reflection.Assembly tMyAssembly = null;
if (string.IsNullOrEmpty(pAssemblyNameDLL)) { return tMyAssembly; }
try //try #a
{
if (!pAssemblyNameDLL.ToLower().EndsWith(".dll")) { pAssemblyNameDLL += ".dll"; }
tMyAssembly = System.Reflection.Assembly.LoadFrom(pAssemblyNameDLL);
}// try #a
catch (Exception ex)
{
string m = ex.Message;
}// try #a
return tMyAssembly;
}//System.Reflection.Assembly GetAssemblyFile(string pAssemblyNameDLL)
public static string GetVersionStringFromAssembly(string pAssemblyDisplayName)
{
string tVersion = "Unknown";
System.Reflection.Assembly tMyAssembly = null;
tMyAssembly = GetAssembly(pAssemblyDisplayName);
if (tMyAssembly == null) { return tVersion; }
tVersion = GetVersionString(tMyAssembly.GetName().Version.ToString());
return tVersion;
}//string GetVersionStringFromAssemblyEmbedded(string pAssemblyDisplayName)
public static string GetVersionString(Version pVersion)
{
string tVersion = "Unknown";
if (pVersion == null) { return tVersion; }
tVersion = GetVersionString(pVersion.ToString());
return tVersion;
}//string GetVersionString(Version pVersion)
public static string GetVersionString(string pVersionString)
{
string tVersion = "Unknown";
string[] aVersion;
if (string.IsNullOrEmpty(pVersionString)) { return tVersion; }
aVersion = pVersionString.Split('.');
if (aVersion.Length > 0) { tVersion = aVersion[0]; }
if (aVersion.Length > 1) { tVersion += "." + aVersion[1]; }
if (aVersion.Length > 2) { tVersion += "." + aVersion[2].PadLeft(4, '0'); }
if (aVersion.Length > 3) { tVersion += "." + aVersion[3].PadLeft(4, '0'); }
return tVersion;
}//string GetVersionString(Version pVersion)
public static string GetVersionStringFromAssemblyEmbedded(string pAssemblyDisplayName)
{
string tVersion = "Unknown";
System.Reflection.Assembly tMyAssembly = null;
tMyAssembly = GetAssemblyEmbedded(pAssemblyDisplayName);
if (tMyAssembly == null) { return tVersion; }
tVersion = GetVersionString(tMyAssembly.GetName().Version.ToString());
return tVersion;
}//string GetVersionStringFromAssemblyEmbedded(string pAssemblyDisplayName)
public static string GetVersionStringFromAssemblyDLL(string pAssemblyDisplayName)
{
string tVersion = "Unknown";
System.Reflection.Assembly tMyAssembly = null;
tMyAssembly = GetAssemblyDLL(pAssemblyDisplayName);
if (tMyAssembly == null) { return tVersion; }
tVersion = GetVersionString(tMyAssembly.GetName().Version.ToString());
return tVersion;
}//string GetVersionStringFromAssemblyEmbedded(string pAssemblyDisplayName)

Answer by #Ben proved to be useful for me. But I needed to check the product version as it was the main increment happening in my software and followed semantic versioning.
myFileVersionInfo.ProductVersion
This method met my expectations
Update: Instead of explicitly mentioning dll path in program (as needed in production version), we can get product version using Assembly.
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fileVersionInfo =FileVersionInfo.GetVersionInfo(assembly.Location);
string ProdVersion= fileVersionInfo.ProductVersion;

var versionAttrib = new AssemblyName(Assembly.GetExecutingAssembly().FullName);

You can use System.Reflection.Assembly.Load*() methods and then grab their AssemblyInfo.

While the original question may not have been specific to a web service, here is a complete testWebService you can add to display a web service non-cached response plus the file version. We use file version instead of assembly version because we want to know a version, but with all assembly versions 1.0.0.0, the web site can be easily patched (signing and demand link still active!). Replace #Class# with the name of the web api controller this service is embedded in. It's good for a go/nogo on a web service plus a quick version check.
[Route("api/testWebService")]
[AllowAnonymous]
[HttpGet]
public HttpResponseMessage TestWebService()
{
HttpResponseMessage responseMessage = Request.CreateResponse(HttpStatusCode.OK);
string loc = Assembly.GetAssembly(typeof(#Class#)).Location;
FileVersionInfo versionInfo = FileVersionInfo.GetVersionInfo(loc);
responseMessage.Content = new StringContent($"<h2>The XXXXX web service GET test succeeded.</h2>{DateTime.Now}<br/><br/>File Version: {versionInfo.FileVersion}");
responseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("text/html");
Request.RegisterForDispose(responseMessage);
return responseMessage;
}
I found it also necessary to add the following to web.config under configuration to make it truly anonymous
<location path="api/testwebservice">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>

Related

HttpContext.Current.Request.Url.IsFile Returns False When Url is ASPX Page

I'm hitting a local URL of the form: http://localhost/example.dev/eu/default.aspx.
My goal is to determine when the request is an aspx file inside of the global.asax file, and subsequently do stuff if it is an aspx file (and only an aspx file) using:
HttpContext.Current.Request.Url.IsFile
It's consistently resolving to false however and I'm not sure why. My complete global.asax code is:
if(HttpContext.Current.Request.Url.IsFile)
{
if(File.Exists(HttpContext.Current.Request.Url.LocalPath))
{
if(new FileInfo(HttpContext.Current.Request.Url.LocalPath).Extension.Equals("aspx"))
{
DoSomethingWithThePagesURL();
}
}
}
Did you take a look at the Documentation for IsFile Property?. It seems very clear from the documentation that Http: is not File:.
The IsFile property is true when the Scheme property equals UriSchemeFile.
DotNetFiddle Example
using System;
public class Program
{
public static void Main()
{
Uri uriAddress2 = new Uri("file://server/filename.ext");
Console.WriteLine(uriAddress2.LocalPath);
Console.WriteLine("Uri {0} a UNC path", uriAddress2.IsUnc ? "is" : "is not");
Console.WriteLine("Uri {0} a local host", uriAddress2.IsLoopback ? "is" : "is not");
Console.WriteLine("Uri {0} a file", uriAddress2.IsFile ? "is" : "is not");
}
}
Results:
\server\filename.ext
Uri is a UNC path
Uri is not a local host
Uri is a file
I use Nuget package walter.web.firewall that inject a IPageRequest in each request, this contains access to the requests underlying resource and will provide access via IPageRequest.LocalFile
However if you do need a firewall and it has been a while since this was asked, and lot's of framework changes have taken place since the question was asked so let my try and answer it in a way that uses no framework classes hoping it will work for all that try and implement it in the future.
here's the code:
public enum FileLocation
{
NotSet,
Disk,
Resource,
}
private static readonly string[] FileExtenstions = new[] {
".js"
,".ts"
,".vue"
,".css"
,".jpg"
,".png"
,".gif"
,".ico"
,".svg"
,".ttf"
,".eot"
,".ttf"
,".woff"
,".woff2"
,".mp4"
,".mp3"
,".emf"
};
public FileLocation IsMappedTo(Uri uri)
{
if (uri is null)
{
throw new ArgumentNullException(nameof(uri));
}
//make sure we support .net default URI contract
if (uri.IsFile)
return FileLocation.Disk;
//now assume you are looking in a web application
var path = uri.AbsolutePath;
if (path.Length == 0 || path.Equals("/",StringComparison.Ordinal) || path.Length< FileExtenstions.Min(s=>s.Length))
return FileLocation.NotSet;
//get the directory normally one would use IWebHostEnvironment.ContentRootPath different versions .net will have other methods
var dir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot");
//get all resources names from the assembly hosting this class out side if the loop from this assembly you can also use
//you can also use GetManifestResourceNames() to use the web application's assembly
var resourceNames = new HashSet<string>(this.GetType().Assembly.GetManifestResourceNames());
var entryAssembly = Assembly.GetEntryAssembly();
if (entryAssembly != null && entryAssembly != this.GetType().Assembly)
{
foreach (var entry in entryAssembly.GetManifestResourceNames())
{
if (string.IsNullOrEmpty(entry))
resourceNames.Add(entry);
}
}
for (var i = 0; i < FileExtenstions.Length; i++)
{
if (FileExtenstions[i].Equals(path[FileExtenstions[i].Length..], StringComparison.OrdinalIgnoreCase) || path.Contains(FileExtenstions[i], StringComparison.OrdinalIgnoreCase))
{
//exists on disk
if (File.Exists(Path.Combine(dir, path.Replace("/", #"\"))))
return FileLocation.Disk;
//has a file as an embedded resource with the same name (ignores the path) so you might have duplicates names
if (resourceNames.Any(a => a.EndsWith(path.Split('/')[^1], StringComparison.OrdinalIgnoreCase)))
return FileLocation.Resource;
}
}
return FileLocation.NotSet;
}

How can I get the executing assembly version information in a Windows Store App?

While porting an application to the Windows Store, I noticed the .NETCore Framework does not include:
System.Reflection.Assembly.GetExecutingAssembly()
I used this to get the version information for display on the menu screen. Is there a replacement or am I forced to store the information elsewhere for retrieval?
EDIT:
I've also found that I can extract a version number out of typeof(MyType).AssemblyQualifiedName but that seems bad.
I am using this :
public string GetApplicationVersion()
{
var ver = Windows.ApplicationModel.Package.Current.Id.Version;
return ver.Major.ToString() + "." + ver.Minor.ToString() + "." + ver.Build.ToString() + "." + ver.Revision.ToString();
}
And if you want assembly version you can get it from Version attribute :
public string GetAssemblyVersion(Assembly asm)
{
var attr = CustomAttributeExtensions.GetCustomAttribute<AssemblyFileVersionAttribute>(asm);
if (attr != null)
return attr.Version;
else
return "";
}
For example using main App's assembly :
Assembly appAsm = typeof(App).GetTypeInfo().Assembly;
string assemblyVersion = GetAssemblyVersion(appAsm);

Simple ASMX WebService and DLL not loaded

I've a problem running an ASMX Web Service. I'm Calling a DLL from a method (AceptaTools.dll) and this DLL load ca4xml.dll.
AceptaTools.dll has been registered with REGSVR32. But ca4xml.dll Can't.
When i Invoke the service:
_objURL = _CA4XML.GetLastResponse();
i get a message "ca4xml.dll not loaded".
Looking al Dependency Walker:
Here both files in detail:
Both DLL are in BIN folder and my project run as x86... Why can't load?? Please help.
[WebMethod]
public string Send(string Ip, string Puerto, string NroDocumento, string TipoDocumento, string Comando, string Impresora, string Linea)
{
try
{
int _Result = 0;
string _Null = "";
string _objURL;
//Config Capsula
string serverConfig = "cfg|" + Ip.ToString() + "|" + Puerto.ToString() + "|10";
//Impresora FACTURA,1 por Defecto.
if (string.IsNullOrEmpty(Impresora)) { Impresora = "FACTURA,1"; }
if (string.IsNullOrEmpty(NroDocumento)) { NroDocumento = "0"; }
if (string.IsNullOrEmpty(Comando)) { Comando = "generar"; }
//Nuevo CAXML Cliente
AceptaTools.CA4XML_Client _CA4XML = new CA4XML_Client();
_Result = _CA4XML.Send(ref serverConfig, ref NroDocumento, ref Comando, ref Impresora, ref Linea, out _Null);
if (_Result != 0)
{
_objURL = _CA4XML.GetLastResponse(); //Get URL
return _objURL.ToString();
}
else
{
return "Error";
}
}
catch (Exception ex)
{
return ex.Message.ToString();
}
}
}
Did you make sure that the ca4xml.dll is deployed properly? Since I guess it is not referenced as .NET assembly the VS will treat it like a normal file and you will need to specifically tell VS to include it when deploying.
Do the following steps to check whether deployment has been setup properly:
Open the Solution Explorer -> Got to the ca4xml.dll -> Right click -> Select Properties -> Set Build Action = None & Copy to Output = Always
Also in addition to the Dependency Walker I suggest to use Process Monitor. When you use the file access view (disregarding registry changes etc.) you can see all the locations where a process tries to load a dll from. Afterwards you can make sure that the dll you are missing is in one of the listed locations, here is the link:
http://technet.microsoft.com/en-us/sysinternals/bb896645
When you do not load DLL in the program because you need to update some things .
you can update project in nuget manager

Embedding Localization Resources .DLL's to the Executable in C#?

I want to make my program multilingual. I have successfully made the program multilingual via Form's Localizable and Language properties. It made some .resx files. Then I deleted non-needed files such as images (which they are the same in all langauges) etc from the .resx files.
The problem is, for example, it also generates a folder called "en" and in that folder, another generated file is called "ProjectName.resources.dll".
Is there anyway to embed this resource file to the .exe? Adding it to the resources and setting Build Action to "Embedded Resource" doesn't work also.
Thanks.
In .NET Framework 4 you can embed resource library into executable.
http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyresolve.aspx
Just create same structure ( with localized folders 'lib/en', 'lib/de' ) and embed them.
private static Assembly MyResolveEventHandler(object sender, ResolveEventArgs args) {
AssemblyName MissingAssembly = new AssemblyName(args.Name);
CultureInfo ci = MissingAssembly.CultureInfo;
...
resourceName = "MyApp.lib." + ci.Name.Replace("-","_") + "." + MissingAssembly.Name + ".dll";
var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)
...
}
You've asked this question a while ago and you've already accepted an answer, but still I'll try to provide an alternative way. I had the same problem and this is how I solved it:
I added the dll as a Ressource to my C#-Project and added this code to my Main-Method (the one that starts your main winform).
public static void Main(string[] args)
{
if (InitdeDEDll()) // Create dll if it's missing.
{
// Restart the application if the language-package was added
Application.Restart();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new YOURMAINFORM());
}
private static bool InitdeDEDll() // Initialize the German de-DE DLL
{
try
{
// Language of my package. This will be the name of the subfolder.
string language = "de-DE";
return TryCreateFileFromRessource(language, #"NAMEOFYOURDLL.dll",
NAMESPACEOFYOURRESSOURCE.NAMEOFYOURDLLINRESSOURCEFILE);
}
catch (Exception)
{
return false;
}
}
private static bool TryCreateFileFromRessource(string subfolder, string fileName, byte[] buffer)
{
try
{
// path of the subfolder
string subfolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + (subfolder != "" ? #"\" : "") + subfolder;
// Create subfolder if it doesn't exist
if (!Directory.Exists(subfolder))
Directory.CreateDirectory(subfolderPath);
fileName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) + #"\" + subfolder + (subfolder!=""?#"\":"") + fileName;
if (!File.Exists(fileName)) // if the dll doesn't already exist, it has to be created
{
// Write dll
Stream stream = File.Create(fileName);
stream.Write(buffer, 0, buffer.GetLength(0));
stream.Close();
}
else
{
return false;
}
}
catch
{
return false;
}
return true;
}
}
Note: This will create the folder and language-dll again if it's missing, so you don't have to care anymore that you copy that folder and the dll with your exe-file. If you want it to be completely vanished this won't be the right approach of course.

Getting the Last Modified Date of an assembly in the GAC

I have implemented the fusion.dll wrapper mentioned in many posts and now find that at least one dll I need to determine if it needs to be updated is not using build and revision numbers. Consequently I cannot compare on version numbers and need to compare on Last Modified date.
fusion.dll or it's wrappers have no such method which I guess is fair enough but how do I determine the 'real' path of the dll so that I can discovers it's last Modified date.
My code so far:
private DateTime getGACVersionLastModified(string DLLName)
{
FileInfo fi = new FileInfo(DLLName);
string dllName = fi.Name.Replace(fi.Extension, "");
DateTime versionDT = new DateTime(1960,01,01);
IAssemblyEnum ae = AssemblyCache.CreateGACEnum();
IAssemblyName an;
AssemblyName name;
while (AssemblyCache.GetNextAssembly(ae, out an) == 0)
{
try
{
name = GetAssemblyName(an);
if (string.Compare(name.Name, dllName, true) == 0)
{
FileInfo dllfi = new FileInfo(string.Format("{0}.dll", name.Name));
if (DateTime.Compare(dllfi.LastWriteTime, versionDT) >= 0)
versionDT = dllfi.LastWriteTime;
}
}
catch (Exception ex)
{
logger.FatalException("Unable to get version number: ", ex);
}
}
return versionDT;
}
From the problem description in your question I can see there are really 2 primary tasks that you are trying to accomplish:
1) Determine if a given assembly name can be loaded from the GAC.
2) Return the file modified date for the given assembly.
I believe these 2 points can be accomplished in a much simpler fashion and without having to work with the unmanaged fusion API. An easier way to go about this task might be as follows:
static void Main(string[] args)
{
// Run the method with a few test values
GetAssemblyDetail("System.Data"); // This should be in the GAC
GetAssemblyDetail("YourAssemblyName"); // This might be in the GAC
GetAssemblyDetail("ImaginaryAssembly"); // This just plain doesn't exist
}
private static DateTime? GetAssemblyDetail(string assemblyName)
{
Assembly a;
a = Assembly.LoadWithPartialName(assemblyName);
if (a != null)
{
Console.WriteLine("'{0}' is in GAC? {1}", assemblyName, a.GlobalAssemblyCache);
FileInfo fi = new FileInfo(a.Location);
Console.WriteLine("'{0}' Modified: {1}", assemblyName, fi.LastWriteTime);
return fi.LastWriteTime;
}
else
{
Console.WriteLine("Assembly '{0}' not found", assemblyName);
return null;
}
}
An example of the resulting output:
'System.Data' is in GAC? True
'System.Data' Modified: 10/1/2010 9:32:27 AM
'YourAssemblyName' is in GAC? False
'YourAssemblyName' Modified: 12/30/2010 4:25:08 AM
Assembly 'ImaginaryAssembly' not found

Categories