应用需求1: 我们cad.net开发都会面临一个问题,加载了的dll无法实现覆盖操作, 就是cad一直打开的状态下,netload两次版本不一样的dll,它只会用第一次载入的....没法做到热插拔....
应用需求2: 我做了一个拖拉加载,不想发送netload到命令栏加载...
在这两个需求之下,已有的资料 明经netloadx 似乎是不二之选...
然而,提出上面的两个需求仅仅是我为了这篇文章想为什么需要这个技术而已....... ( >,< )
真正令我开始研究是因为若海提出的: netloadx 在 a.dll 引用了 b.dll 时候, 为什么不会成功调用...
我首先想到是依赖,于是乎,我试图尝试直接 Assembly.Load(File.ReadAllBytes(path)) 在加载目录的每个文件,并没有报错...
这里打断一下,Assembly.Load(byte)转为byte是为了实现热插拔,所以Assembly.LoadForm()没有byte,也就无法拷贝到内存中去,无法实现热插拔,故此不考虑.
然后出现了一个事情,
能使用单独的命令,却还是不能跨 dll 调用....也就是有运行出错...runtime error
然后我弄了好几天查找了一下那么多的资料..........还翻遍了微软各个函数.............
https://www.cnblogs.com/kongyiyun/archive/2011/08/01/2123459.html
https://www.cnblogs.com/zhuweisky/archive/2005/12/30/308218.html
https://www.cnblogs.com/mengnan/p/4811534.html
https://q.cnblogs.com/q/3530/
https://vimsky.com/examples/detail/csharp-event-system.appdomain.assemblyresolve.html
最终,我还是问了群里的大佬,南胜回答了我....
但是我用他的代码出现了很多问题...所以他的源代码这里贴一个他的链接.....
-----------------------------------------------------------------工程开始-----------------------------------------------------------------
测试代码:
首先,一共有三个项目,
1: 直接netload的项目,也就是cad插件项目.
2: test1.dll
3: test2.dll
test2.dll 引用 test1.dll :
test1:
namespace test1 { public class MyCommands { public static void Test1Hello() { Document doc = Acap.DocumentManager.MdiActiveDocument; Editor ed; if (doc != null) { ed = doc.Editor; ed.WriteMessage("我是被test2调用Test1Hello"); } } [CommandMethod("test1")] public static void Helloaaa() { Document doc = Acap.DocumentManager.MdiActiveDocument; Editor ed; if (doc != null) { ed = doc.Editor; ed.WriteMessage("自带函数test1."); } } } }View Code
test2:
namespace test2 { public class MyCommands { [CommandMethod("test2")] public static void test2() { Document doc = Acap.DocumentManager.MdiActiveDocument; Editor ed; if (doc != null) { ed = doc.Editor; ed.WriteMessage("自带函数test2."); } } [CommandMethod("ttgg")] public void ttgg() { Document doc = Acap.DocumentManager.MdiActiveDocument; Editor ed = doc.Editor; if (doc != null) { ed.WriteMessage("\n\r hhhhhhhhhh"); test1.MyCommands.Test1Hello(); } } } }View Code
cad插件项目.我的代码贴到这里:
namespace JoinBoxCurrency { public class AssemblyLookingDll { /// <summary> /// 加载dll到内存上 /// </summary> /// <param name="path"></param> public AssemblyLookingDll(string path) { //将dll转为byte,再加载进入程序集 Assembly ass = Assembly.Load(File.ReadAllBytes(path)); //这里是有关CLR的Assembly的搜索路径的过程,由于覆盖插件的关系,所以我只会覆盖插件目录,和拖拉的目录 //插件目录 "G:\\K01.惊惊连盒\\net35" string loca = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); //拖拉文件的路径 "D:\\桌面\\若海的\\test2\\bin\\Debug" string dir = Path.GetDirectoryName(path); var assDepe = new List<Assembly>(); //遍历依赖(引用的程序集) foreach (AssemblyName asName in ass.GetReferencedAssemblies()) { var cad = AppDomain.CurrentDomain.GetAssemblies(); //(cad程序集的依赖)和(加载的dll程序集的依赖)不一样,就加载,并加入集合,==0也直接加载 if (cad.Count(c => c.FullName == asName.FullName) == 0) { var asdl = asName.Name + ".dll"; string path1 = Path.Combine(loca, asdl); string path2 = Path.Combine(dir, asdl); //如果插件目录和拖拉目录都有,那么拖拉目录会覆盖前面 if (File.Exists(path1)) { assDepe.Add(Assembly.Load(File.ReadAllBytes(path1))); } if (File.Exists(path2)) { assDepe.Add(Assembly.Load(File.ReadAllBytes(path2))); } } } AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; } } }View Code
而其中最重要的是这个事件:
它会在运行的时候找已经载入内存上面的程序集.
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
//程序域运行事件
private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { //cad的程序集 var cad = AppDomain.CurrentDomain.GetAssemblies(); //获取执行程序集的参数 var ag = args.Name.Split(',')[0]; //获取 匹配符合条件的第一个或者默认的那个 var load = cad.FirstOrDefault(a => a.GetName().FullName.Split(',')[0] == ag); return load; }
备注:
关于动态加载和动态编译是有相通的部分的,而这个部分就是这个事件...
cad2008若没有这个事件,会使动态编译的命令,在执行时候无法引用当前的程序集函数...等于runtime error
netload会把所处的运行域给改到cad自己的,而动态编译不通过 netload,所以要自己去改.
演示,只是会出现pdb占用,我也不知道为什么...多编译几次就过了...可以重复载入之后覆盖掉原有的dll....
完结.