I replaced the EXE of a Windows service with a new version after stopping the service. The EXE is definitely the new version, and Windows Explorer Properties shows the new version.
However, the assembly (when loaded with System.Reflection.Assembly.LoadFile
) reports a .FullName
which seems to indicate that it is not the latest version.
This did not happen on the first site I deployed it to, so now my version reporting system seems to indicate that this site isn't upgraded. (The version reporting is managed by a separate ASP.NET web service which publishes all the versions of assemblies in certain directories, it is this web service which is calling System.Reflection.Assembly.LoadFile
).
What's up here? Is there some kind of caching (the web service would have last inspected that assembly yesterday before it was upgraded)?
The code is pretty basic:
System.IO.DirectoryInfo assemblies = new System.IO.DirectoryInfo(DirectoryName);
foreach (var f in assemblies.GetFiles(Pattern))
{
var a = System.Reflection.Assembly.LoadFile(f.FullName);
l.Add(new VersionManager.Assembly(f.Name, f.LastWriteTime, a.FullName));
}
return l; // l is List<VersionManager.Assembly>
Deleting the EXE and replacing it (instead of just overwriting it) had no effect.
I wrote a little console application and ran it directly on the system about 3.5 hours after the last inquiry of the web service and was very surprise开发者_Go百科d to find it returning the correct version. I then queried the web service and it returned the correct version.
Nothing changed with the binary or the web service (although the web app it was part of may have been recycled, the web service itself doesn't store the information it retrieves about the assemblies, it simply sends them out in the response)
UPDATE - 2011-12-20:
This problem has re-occurred. I deployed to one site, then ran my inventory program on all sites and the site reported the correct version. I deployed to two more sites and, properties on the DLL give the correct version, but the version information reported by loading it with System.Reflection on the two additional sites is out of date. It appears to be cached somewhere after the first call and re-used for the second call.
Clarification
There is an IIS ASP.NET (.NET 2.0) web service which reports the DLL versions of components in directories on the system (directories which are not part of the web service). One of those directories contains a Windows Service's EXE and configuration. When the Windows Service is stopped, the EXE replaced and the web service inquired, it sometimes reports the old version. If the web service hasn't been called in a while, it appears that replacing the EXE will be reported correctly on the next inquiry. If the web service is called before the replacement, it reports no change after the replacement, despite the fact that the EXE has been replaced and reports the correct version through Windows Explorer etc.
I noticed that the LoadFile is not accompanied by any sort of unload. Even though the reference to the assembly goes out of scope, I wonder if it stays loaded (and thus not refreshed in a subsequent call, and thus still reporting out of date information) until IIS or whatever expires it out. Is there a way to unload an assembly? Is there a way to get an assembly file's version information without loading it in the first place?
Using System.Reflection.Assembly.LoadFile(FileName) loads the assembly and there is no way to unload the assembly without unloading the AppDomain completely.
In order to inspect an assembly without loading it, the alternative is the small change from this:
var a = System.Reflection.Assembly.LoadFile(f.FullName);
to this:
var a = System.Reflection.AssemblyName.GetAssemblyName(f.FullName);
Shouldn't the use of an Inspect Appdomain solve the issue as well? Or does security disallow creation of new domaion?
using System;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
// Create a new domain for loading extra Assemblies
AppDomain inspectDomain = AppDomain.CreateDomain("Inspect");
// attach handler to show that no assemblies are loaded in the current domain
AppDomain.CurrentDomain.AssemblyLoad +=new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
// another prove
int startcount = AppDomain.CurrentDomain.GetAssemblies().Length;
// now call the inspection method in the inspectionDomain
inspectDomain.DoCallBack(new CrossAppDomainDelegate(Inspect));
// what is the count on assemblies in the curent domain
int endcount = AppDomain.CurrentDomain.GetAssemblies().Length;
// show it
Console.WriteLine("Appdomain {2} # start: {0} and end {1}",
startcount,
endcount,
AppDomain.CurrentDomain.FriendlyName);
// unload our inspectdomain and therefor the loaded assemblies
AppDomain.Unload(inspectDomain);
// pause so we can see
Console.ReadLine();
}
static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
// write a line for a loaded assembly
Console.WriteLine("program domain: {0}", args.LoadedAssembly.FullName);
}
public static void Inspect()
{
Console.WriteLine("Inspecting...");
int startcount = AppDomain.CurrentDomain.GetAssemblies().Length;
var assemblies = new DirectoryInfo(
@"C:\Windows\Microsoft.NET\Framework64\v2.0.50727");
foreach (var f in assemblies.GetFiles("System.Data.*.dll"))
{
var a = System.Reflection.Assembly.LoadFile(f.FullName);
Console.WriteLine("name:{0}, rt:{1}", a.FullName, a.ImageRuntimeVersion);
}
int endcount = AppDomain.CurrentDomain.GetAssemblies().Length;
Console.WriteLine("Appdomain {2} start: {0} and end {1}",
startcount,
endcount,
AppDomain.CurrentDomain.FriendlyName);
Console.WriteLine("Done Inspecting...");
}
}
}
精彩评论