最近一直很忙,前段时间也忙着写些公司申请专利相关的文档,并整合通用MIS框架,所以没有太多闲暇写自己的博客。今天又看了看反射的东西,忽然发现Assembly类的内容真是丰富啊!!而且,发现了之前我并没有过多关注的方法,但是,却是非常的实用和强大!我们以一个示例来阐述之。
众所周知,扩展名为EXE的文件在Windows下为二进制可执行文件,一旦这个可执行文件处于运行状态,操作系统会把这个二进制文件映射到内存中去,并将其加以锁定。一般情况下,包括操作系统在内的任何程序都无法将这个处于运行状态的可执行文件移动、更名或删除。不过,在.Net下利用Assembly类,我们对此就有了完美的解决方案。我们可以在内存中装载程序集并运行之,而好处就是我们可以随意移动、更名和删除这个处于运行中的程序或程序集文件。当然,该解决方案仅针对.Net程序或程序集。
示例方案中有两个窗体程序,WindowsFormsApplication1.exe 和 WindowsFormsApplication2.exe,第一个程序的窗体上会有个button,用于在内存中装载第二个程序,当第二个程序的主窗体显示后,我们便可以轻松的将 WindowsFormsApplication2.exe 移动、更名和删除,Windows 根本不会提示什么”文件正在使用,无法….”之类的提示了。
首先,为了动态的在内存中装载程序或程序集,我们以文件流的方式读取二进制文件,并将其以字节的形式保存在数组中,代码如下:
FileStream fs = new FileStream(Application.StartupPath + "\\WindowsFormsApplication2.exe", FileMode.Open);
BinaryReader br = new BinaryReader(fs);
byte[] bin = br.ReadBytes(Convert.ToInt32(fs.Length));
fs.Close();
br.Close();
然后,利用 Assembly 类的 Load 重载方法,以数组的形式加载该程序集。代码如下:
Assembly a = Assembly.Load(bin);
随后我们需要获得程序集的入口点,并将其以 MethodInfo 类型保存下来,代码如下:
method = a.EntryPoint; //获得内存中可执行文件的入口点
C++程序员都清楚,所谓程序入口点其实就是main函数的内存地址,有了程序的main方法的地址,我们只要调用即可了。代码如下:
if (method != null)
{
method.Invoke(null, null);
}
上述代码编译时会100%正确,但是程序运行到 method.Invoke(null, null); 时会出现异常,经过反复测试后发现,我们必须在一个新的线程上来执行对main方法的调用!
System.Threading.Thread thd;
System.Threading.ThreadStart ts; //在线程上执行特定的方法
ts = new System.Threading.ThreadStart(RunNewApp);
thd = new System.Threading.Thread(ts);
thd.Start();
该示例在Windows XP3 + Visual Studio 2008 SP1 下编译调试通过。