在.Net下要让程序实现自动维护程序集的版本并且实现自动更新到最新版本的功能,可以使用反射机制。它提供了检查程序集的方法,通过 System.Reflection 中的 Assembly 类我们可以加载程序集,然后检查它的版本号,以此判断是否需要下载或更新。这里我写了一个示例来实现这个功能。但最后发现一旦加载了程序集就占用了需要更新的程序集文件,导致文件替换失败。为了解决这个问题,我参考了Flier's Sky的Assembly.Unload和Wayfarer's Prattle的通过应用程序域AppDomain加载和卸载程序集。下面就是我的代码,由于时间仓促,估计有些异常还没有处理到。请大家指教。
1
using System;
2
using System.IO;
3
using System.Reflection;
4
using System.Collections.Generic;
5
using System.Text;
6
7
namespace 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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154
