最近在做项目的时候,遇到一个需求,需要通过选择的文件,先判断是C#dll还是C++dll,再判断dll是否是我们需要的特定dll,还是别的随便一个dll。
经过研究发现,我们可以通过反射来加载dll,如果用C#的反射机制可以正确加载dll,那么就可以认为该dll是C#类型的,如果不能正确加载,我们再通过C++的方法来加载进行判断。
判断是否特定dll的话,只需在判断完dll类型后,对dll中的特定类进行判断即可。
但是这里,存在一个问题,通过反射加载完C#dll后,该dll即被占用,无法进行修改(比如,我判断完该dll后,发现该dll版本选错了,那么此时,是没办法进行替换的,因为已经被程序加载了),此时,我们即需要对反射加载的该类进行卸载。
尴尬的是,C#并没有对Assembly定义释放方法,这是因为,反射加载完的对象是通过CLR进行托管的,只有等到对象无用时,才会通过GC进行回收。但这明显不能满足我们的需求。
其实,这个问题是可以通过应用程序域来解决的。
要对dll进行判断时,我们可以创建一个应用程序域,通过应用程序域的方法来动态加载反射,并且,其提供的UnLoad方法,可以将加载的dll给动态的卸载掉。
如,我这里是这么处理的。
AppDomain appDomain = AppDomain.CreateDomain("CheckDllType"); try { //判断文件存在不存在 if (!File.Exists(dllPath)) return res; //用C#加载DLL的方法进行加载dll尝试 object ob_CAN = appDomain.CreateComInstanceFrom(dllPath, "Unify.Bootloader"); if (ob_CAN!=null) { res = 1; //释放对象 AppDomain.Unload(appDomain); appDomain = null; ob_CAN = null; } } catch (Exception ex)//若异常,则判断其他类型 { //.... }
注意,这里会把新创建域中的所有对象都给回收。
另外一个应用场景就是,当我们需要做一个反编译工具时,也需要用到这样的处理,因为不能每次dll更新后都重启反编译软件。
同时,这里创建域是有一组不同的方法的,并且每个方法都有多个重载,包括
CreateComInstanceFrom CreateInstance CreateInstanceAndUnwrap CreateInstanceFrom CreateInstanceFromAndUnwrap
有需要的大家可以自行学习,注意其间的区别。