利用反射检查程序集实现自动更新
在.Net下要让程序实现自动维护程序集的版本并且实现自动更新到最新版本的功能,可以使用反射机制。它提供了检查程序集的方法,通过 System.Reflection 中的 Assembly 类我们可以加载程序集,然后检查它的版本号,以此判断是否需要下载或更新。这里我写了一个示例来实现这个功能。但最后发现一旦加载了程序集就占用了需要更新的程序集文件,导致文件替换失败。为了解决这个问题,我参考了Flier's Sky的Assembly.Unload和Wayfarer's Prattle的通过应用程序域AppDomain加载和卸载程序集。下面就是我的代码,由于时间仓促,估计有些异常还没有处理到。请大家指教。
using System;
using System.IO;
using System.Reflection;
using System.Collections.Generic;
using System.Text;
namespace Update
{
// 序列化这个用来传递参数的类
public class AssembliyInf : MarshalByRefObject
{
public string AssemblyName = "";
public string AssemblyFileFullPath = "";
public string Version = "";
public string Revision = "";
public string Major = "";
public string Minor = "";
}
// 由于是远程调用的方式,所以这个类也需要序列化
public class AssemblyLoader : MarshalByRefObject, IDisposable
{
public AssembliyInf GetAssemblyInf(string fileFullName)
{
AssembliyInf assemblyInf = new AssembliyInf();
try
{
Assembly assembly = Assembly.ReflectionOnlyLoadFrom(fileFullName);
assemblyInf.AssemblyName = ((AssemblyName)assembly.GetName()).Name;
assemblyInf.AssemblyFileFullPath = assembly.Location;
assemblyInf.Version = ((AssemblyName)assembly.GetName()).Version.ToString();
assemblyInf.Revision = ((AssemblyName)assembly.GetName()).Version.Revision.ToString();
assemblyInf.Major = ((AssemblyName)assembly.GetName()).Version.Major.ToString();
assemblyInf.Minor = ((AssemblyName)assembly.GetName()).Version.Minor.ToString();
assembly = null; // 释放引用
// 手工调用框架的垃圾收集器
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
System.GC.Collect(0);
}
catch (Exception)
{
}
return assemblyInf;
}
public void Dispose()
{
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine();
string sourceFile, distinationFile;
sourceFile = @"D:\MyApp\Update\myApp.exe"; // 假定准备更新的程序集已经下载到本地
distinationFile = @"D:\MyApp\myApp.exe"; // 这是要维护的目标程序集,发现新版本后就需要替换
// 显示准备更新的程序集信息
AssembliyInf assemblyNew = GetAssemblyInf(sourceFile);
ShowAssembly(assemblyNew);
// 显示当前使用的程序集信息
AssembliyInf assemblyCurrent = GetAssemblyInf(distinationFile);
ShowAssembly(assemblyCurrent);
// 比较两个程序集
if (Compare(assemblyNew, assemblyCurrent))
{
Console.WriteLine("需要更新当前程序集!");
// 开始更新
Update(assemblyNew, assemblyCurrent);
}
else
Console.WriteLine("不需要更新当前程序集!");
Console.ReadKey();
}
// 用新的程序集替换现有的
static void Update(AssembliyInf assemblyNew, AssembliyInf assemblyCurrent)
{
string sourceFile, distinationFile;
sourceFile = assemblyNew.AssemblyFileFullPath;
distinationFile = assemblyCurrent.AssemblyFileFullPath;
// 替换文件
File.Copy(sourceFile, distinationFile, true);
}
// 显示程序集相关信息
static void ShowAssembly(AssembliyInf assembly)
{
Console.WriteLine("Assembly Name: " + assembly.AssemblyName );
Console.WriteLine("Assembly Version.Current: " + assembly.Version);
Console.WriteLine("Assembly Version.Revision: " + assembly.Revision);
Console.WriteLine("Assembly Version.Major: " + assembly.Major);
Console.WriteLine("Assembly Version.Minor: " + assembly.Minor);
Console.WriteLine("Assembly FullName: " + assembly.AssemblyFileFullPath);
Console.WriteLine();
}
// 比较两个程序集判断是否需要更新
static bool Compare(AssembliyInf assemblyNew, AssembliyInf assemblyCurrent)
{
if ((assemblyNew.AssemblyName == assemblyCurrent.AssemblyName)
&& (int.Parse(assemblyNew.Revision) > int.Parse(assemblyCurrent.Revision)))
return true;
else
return false;
}
// 获取程序集的信息
static AssembliyInf GetAssemblyInf(string fileFullName)
{
AssembliyInf assemblyInf = new AssembliyInf();
string dllName = typeof(Program).Assembly.Location;
AppDomain domain = null;
AppDomainSetup setup = new AppDomainSetup();
setup.ShadowCopyFiles = "true";
domain = AppDomain.CreateDomain(dllName, null, setup);
AssemblyLoader al = (AssemblyLoader)domain.CreateInstanceFromAndUnwrap(dllName, "Update.AssemblyLoader");
AssembliyInf tmpAssemblyInf = al.GetAssemblyInf(fileFullName);
// 由于使用了序列化导致传回的对象不能传出这个方法,所以要转换一下
assemblyInf.AssemblyName = tmpAssemblyInf.AssemblyName;
// 又因为是使用了子程序域的方法,实际执行加载的子程序域是一个临时文件。返回值是一个临时文件。
//assemblyInf.AssemblyFileFullPath = tmpAssemblyInf.AssemblyFileFullPath;
assemblyInf.AssemblyFileFullPath = fileFullName;
assemblyInf.Version = tmpAssemblyInf.Version;
assemblyInf.Major = tmpAssemblyInf.Major;
assemblyInf.Minor = tmpAssemblyInf.Minor;
assemblyInf.Revision = tmpAssemblyInf.Revision;
AppDomain.Unload(domain);
return assemblyInf;
}
}
}