使用反射检查程序集,实现自动更新
使用反射检查程序集,实现自动更新
在.Net下要让程序实现自动维护程序集的版本并且实现自动更新到最新版本的功能,可以使用反射机制。它提供了检查程序集的方法,通过 System.Reflection 中的 Assembly 类我们可以加载程序集,然后检查它的版本号,以此判断是否需要下载或更新。这里我写了一个示例来实现这个功能。但最后发现一旦加载了程序集就占用了需要更新的程序集文件,导致文件替换失败。为了解决这个问题,我参考了Flier's Sky的Assembly.Unload和Wayfarer's Prattle的通过应用程序域AppDomain加载和卸载程序集。下面就是我的代码,由于时间仓促,估计有些异常还没有处理到。请大家指教。
在.Net下要让程序实现自动维护程序集的版本并且实现自动更新到最新版本的功能,可以使用反射机制。它提供了检查程序集的方法,通过 System.Reflection 中的 Assembly 类我们可以加载程序集,然后检查它的版本号,以此判断是否需要下载或更新。这里我写了一个示例来实现这个功能。但最后发现一旦加载了程序集就占用了需要更新的程序集文件,导致文件替换失败。为了解决这个问题,我参考了Flier's Sky的Assembly.Unload和Wayfarer's Prattle的通过应用程序域AppDomain加载和卸载程序集。下面就是我的代码,由于时间仓促,估计有些异常还没有处理到。请大家指教。
1using System;
2using System.IO;
3using System.Reflection;
4using System.Collections.Generic;
5using System.Text;
6
7namespace Update
8{
9 // 序列化这个用来传递参数的类
10 public class AssembliyInf : MarshalByRefObject
11 {
12 public string AssemblyName = "";
13 public string AssemblyFileFullPath = "";
14 public string Version = "";
15 public string Revision = "";
16 public string Major = "";
17 public string Minor = "";
18 }
19
20 // 由于是远程调用的方式,所以这个类也需要序列化
21 public class AssemblyLoader : MarshalByRefObject, IDisposable
22 {
23 public AssembliyInf GetAssemblyInf(string fileFullName)
24 {
25 AssembliyInf assemblyInf = new AssembliyInf();
26
27 try
28 {
29 Assembly assembly = Assembly.ReflectionOnlyLoadFrom(fileFullName);
30
31 assemblyInf.AssemblyName = ((AssemblyName)assembly.GetName()).Name;
32 assemblyInf.AssemblyFileFullPath = assembly.Location;
33 assemblyInf.Version = ((AssemblyName)assembly.GetName()).Version.ToString();
34 assemblyInf.Revision = ((AssemblyName)assembly.GetName()).Version.Revision.ToString();
35 assemblyInf.Major = ((AssemblyName)assembly.GetName()).Version.Major.ToString();
36 assemblyInf.Minor = ((AssemblyName)assembly.GetName()).Version.Minor.ToString();
37
38 assembly = null; // 释放引用
39
40 // 手工调用框架的垃圾收集器
41 System.GC.Collect();
42 System.GC.WaitForPendingFinalizers();
43 System.GC.Collect(0);
44 }
45 catch (Exception)
46 {
47 }
48 return assemblyInf;
49 }
50
51 public void Dispose()
52 {
53 }
54 }
55
56
57
58 class Program
59 {
60 static void Main(string[] args)
61 {
62 Console.WriteLine();
63 string sourceFile, distinationFile;
64
65 sourceFile = @"D:\MyApp\Update\myApp.exe"; // 假定准备更新的程序集已经下载到本地
66 distinationFile = @"D:\MyApp\myApp.exe"; // 这是要维护的目标程序集,发现新版本后就需要替换
67
68 // 显示准备更新的程序集信息
69 AssembliyInf assemblyNew = GetAssemblyInf(sourceFile);
70 ShowAssembly(assemblyNew);
71
72 // 显示当前使用的程序集信息
73 AssembliyInf assemblyCurrent = GetAssemblyInf(distinationFile);
74 ShowAssembly(assemblyCurrent);
75
76 // 比较两个程序集
77 if (Compare(assemblyNew, assemblyCurrent))
78 {
79 Console.WriteLine("需要更新当前程序集!");
80 // 开始更新
81 Update(assemblyNew, assemblyCurrent);
82 }
83 else
84 Console.WriteLine("不需要更新当前程序集!");
85
86 Console.ReadKey();
87 }
88
89 // 用新的程序集替换现有的
90 static void Update(AssembliyInf assemblyNew, AssembliyInf assemblyCurrent)
91 {
92 string sourceFile, distinationFile;
93
94 sourceFile = assemblyNew.AssemblyFileFullPath;
95 distinationFile = assemblyCurrent.AssemblyFileFullPath;
96
97 // 替换文件
98 File.Copy(sourceFile, distinationFile, true);
99
100 }
101
102 // 显示程序集相关信息
103 static void ShowAssembly(AssembliyInf assembly)
104 {
105 Console.WriteLine("Assembly Name: " + assembly.AssemblyName );
106 Console.WriteLine("Assembly Version.Current: " + assembly.Version);
107 Console.WriteLine("Assembly Version.Revision: " + assembly.Revision);
108 Console.WriteLine("Assembly Version.Major: " + assembly.Major);
109 Console.WriteLine("Assembly Version.Minor: " + assembly.Minor);
110 Console.WriteLine("Assembly FullName: " + assembly.AssemblyFileFullPath);
111 Console.WriteLine();
112 }
113
114 // 比较两个程序集判断是否需要更新
115 static bool Compare(AssembliyInf assemblyNew, AssembliyInf assemblyCurrent)
116 {
117 if ((assemblyNew.AssemblyName == assemblyCurrent.AssemblyName)
118 && (int.Parse(assemblyNew.Revision) > int.Parse(assemblyCurrent.Revision)))
119 return true;
120 else
121 return false;
122 }
123
124 // 获取程序集的信息
125 static AssembliyInf GetAssemblyInf(string fileFullName)
126 {
127 AssembliyInf assemblyInf = new AssembliyInf();
128
129 string dllName = typeof(Program).Assembly.Location;
130 AppDomain domain = null;
131 AppDomainSetup setup = new AppDomainSetup();
132 setup.ShadowCopyFiles = "true";
133 domain = AppDomain.CreateDomain(dllName, null, setup);
134 AssemblyLoader al = (AssemblyLoader)domain.CreateInstanceFromAndUnwrap(dllName, "Update.AssemblyLoader");
135 AssembliyInf tmpAssemblyInf = al.GetAssemblyInf(fileFullName);
136
137 // 由于使用了序列化导致传回的对象不能传出这个方法,所以要转换一下
138 assemblyInf.AssemblyName = tmpAssemblyInf.AssemblyName;
139 // 又因为是使用了子程序域的方法,实际执行加载的子程序域是一个临时文件。返回值是一个临时文件。
140 //assemblyInf.AssemblyFileFullPath = tmpAssemblyInf.AssemblyFileFullPath;
141 assemblyInf.AssemblyFileFullPath = fileFullName;
142 assemblyInf.Version = tmpAssemblyInf.Version;
143 assemblyInf.Major = tmpAssemblyInf.Major;
144 assemblyInf.Minor = tmpAssemblyInf.Minor;
145 assemblyInf.Revision = tmpAssemblyInf.Revision;
146
147 AppDomain.Unload(domain);
148
149 return assemblyInf;
150 }
151 }
152
153}
154
2using System.IO;
3using System.Reflection;
4using System.Collections.Generic;
5using System.Text;
6
7namespace Update
8{
9 // 序列化这个用来传递参数的类
10 public class AssembliyInf : MarshalByRefObject
11 {
12 public string AssemblyName = "";
13 public string AssemblyFileFullPath = "";
14 public string Version = "";
15 public string Revision = "";
16 public string Major = "";
17 public string Minor = "";
18 }
19
20 // 由于是远程调用的方式,所以这个类也需要序列化
21 public class AssemblyLoader : MarshalByRefObject, IDisposable
22 {
23 public AssembliyInf GetAssemblyInf(string fileFullName)
24 {
25 AssembliyInf assemblyInf = new AssembliyInf();
26
27 try
28 {
29 Assembly assembly = Assembly.ReflectionOnlyLoadFrom(fileFullName);
30
31 assemblyInf.AssemblyName = ((AssemblyName)assembly.GetName()).Name;
32 assemblyInf.AssemblyFileFullPath = assembly.Location;
33 assemblyInf.Version = ((AssemblyName)assembly.GetName()).Version.ToString();
34 assemblyInf.Revision = ((AssemblyName)assembly.GetName()).Version.Revision.ToString();
35 assemblyInf.Major = ((AssemblyName)assembly.GetName()).Version.Major.ToString();
36 assemblyInf.Minor = ((AssemblyName)assembly.GetName()).Version.Minor.ToString();
37
38 assembly = null; // 释放引用
39
40 // 手工调用框架的垃圾收集器
41 System.GC.Collect();
42 System.GC.WaitForPendingFinalizers();
43 System.GC.Collect(0);
44 }
45 catch (Exception)
46 {
47 }
48 return assemblyInf;
49 }
50
51 public void Dispose()
52 {
53 }
54 }
55
56
57
58 class Program
59 {
60 static void Main(string[] args)
61 {
62 Console.WriteLine();
63 string sourceFile, distinationFile;
64
65 sourceFile = @"D:\MyApp\Update\myApp.exe"; // 假定准备更新的程序集已经下载到本地
66 distinationFile = @"D:\MyApp\myApp.exe"; // 这是要维护的目标程序集,发现新版本后就需要替换
67
68 // 显示准备更新的程序集信息
69 AssembliyInf assemblyNew = GetAssemblyInf(sourceFile);
70 ShowAssembly(assemblyNew);
71
72 // 显示当前使用的程序集信息
73 AssembliyInf assemblyCurrent = GetAssemblyInf(distinationFile);
74 ShowAssembly(assemblyCurrent);
75
76 // 比较两个程序集
77 if (Compare(assemblyNew, assemblyCurrent))
78 {
79 Console.WriteLine("需要更新当前程序集!");
80 // 开始更新
81 Update(assemblyNew, assemblyCurrent);
82 }
83 else
84 Console.WriteLine("不需要更新当前程序集!");
85
86 Console.ReadKey();
87 }
88
89 // 用新的程序集替换现有的
90 static void Update(AssembliyInf assemblyNew, AssembliyInf assemblyCurrent)
91 {
92 string sourceFile, distinationFile;
93
94 sourceFile = assemblyNew.AssemblyFileFullPath;
95 distinationFile = assemblyCurrent.AssemblyFileFullPath;
96
97 // 替换文件
98 File.Copy(sourceFile, distinationFile, true);
99
100 }
101
102 // 显示程序集相关信息
103 static void ShowAssembly(AssembliyInf assembly)
104 {
105 Console.WriteLine("Assembly Name: " + assembly.AssemblyName );
106 Console.WriteLine("Assembly Version.Current: " + assembly.Version);
107 Console.WriteLine("Assembly Version.Revision: " + assembly.Revision);
108 Console.WriteLine("Assembly Version.Major: " + assembly.Major);
109 Console.WriteLine("Assembly Version.Minor: " + assembly.Minor);
110 Console.WriteLine("Assembly FullName: " + assembly.AssemblyFileFullPath);
111 Console.WriteLine();
112 }
113
114 // 比较两个程序集判断是否需要更新
115 static bool Compare(AssembliyInf assemblyNew, AssembliyInf assemblyCurrent)
116 {
117 if ((assemblyNew.AssemblyName == assemblyCurrent.AssemblyName)
118 && (int.Parse(assemblyNew.Revision) > int.Parse(assemblyCurrent.Revision)))
119 return true;
120 else
121 return false;
122 }
123
124 // 获取程序集的信息
125 static AssembliyInf GetAssemblyInf(string fileFullName)
126 {
127 AssembliyInf assemblyInf = new AssembliyInf();
128
129 string dllName = typeof(Program).Assembly.Location;
130 AppDomain domain = null;
131 AppDomainSetup setup = new AppDomainSetup();
132 setup.ShadowCopyFiles = "true";
133 domain = AppDomain.CreateDomain(dllName, null, setup);
134 AssemblyLoader al = (AssemblyLoader)domain.CreateInstanceFromAndUnwrap(dllName, "Update.AssemblyLoader");
135 AssembliyInf tmpAssemblyInf = al.GetAssemblyInf(fileFullName);
136
137 // 由于使用了序列化导致传回的对象不能传出这个方法,所以要转换一下
138 assemblyInf.AssemblyName = tmpAssemblyInf.AssemblyName;
139 // 又因为是使用了子程序域的方法,实际执行加载的子程序域是一个临时文件。返回值是一个临时文件。
140 //assemblyInf.AssemblyFileFullPath = tmpAssemblyInf.AssemblyFileFullPath;
141 assemblyInf.AssemblyFileFullPath = fileFullName;
142 assemblyInf.Version = tmpAssemblyInf.Version;
143 assemblyInf.Major = tmpAssemblyInf.Major;
144 assemblyInf.Minor = tmpAssemblyInf.Minor;
145 assemblyInf.Revision = tmpAssemblyInf.Revision;
146
147 AppDomain.Unload(domain);
148
149 return assemblyInf;
150 }
151 }
152
153}
154