明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 2145|回复: 6

.Net CAD调试不重启CAD(源码交流)

  [复制链接]
发表于 2022-9-11 17:20:50 | 显示全部楼层 |阅读模式
本帖最后由 yupeng_dyp 于 2022-9-11 18:12 编辑

由于 .Net CAD 开发调试一直很烦,经常要重启!在网上查了很多的方法,现经测试优化整理出一个可行的方法,不过只能对无依赖的 Dll 文件可以成功,在这里与大家分享交流;而有依赖的暂时还没有找到好的方法,望各位道友共研交流分享。

说明:
TestApp.dll 为要调试的类库
TestApp.ClassMain 为类库中对应的空间名与类名
TestCmd 为要调试执行的方法
上面对应的名称可根据自己的修改就行,把下面的源码单独编译成一个 Dll 然后在 CAD 中加载,另把要调试的 Dll 放到与此 Dll 一个目录就可以了,Json 文件主要用于在不重新编译此 Dll 时,方便修改要调试的 Dll 相应的名称(使用前先安装好 Json 包)

源码如下:
第一次上传,using Autodesk.AutoCAD.Runtime 中好像始终有个自动加的链接,也不知道咋去掉!!!
  1. using System;
  2. using System.IO;
  3. using System.Reflection;

  4. using Autodesk.AutoCAD.Runtime;

  5. using Newtonsoft.Json;
  6. using Newtonsoft.Json.Linq;

  7. namespace Acad.Reload;

  8. public class Reload
  9. {
  10.     private Action cmd;

  11.     [CommandMethod("RL", CommandFlags.UsePickSet)]       //简写命令
  12.     public void RL() { ReloadCmd(); }

  13.     [CommandMethod("Reload", CommandFlags.UsePickSet)]   //全写命令
  14.     public void ReloadCmd() {
  15.         var cmdInfo = new CmdInfo("TestApp.dll", "TestApp.ClassMain", "TestCmd");           //目标类库、空间.类、方法的名称
  16.         var dllPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);      //当前类库文件所在目录
  17.         var jsonFile = dllPath + "\\Reload.json";                                           //定义 Json 文件全路径

  18.         //如果 Json 文件存在就读取对应数据
  19.         if (File.Exists(jsonFile)) {
  20.             using var file = File.OpenText(jsonFile);
  21.             using var reader = new JsonTextReader(file);
  22.             var obj = (JObject)JToken.ReadFrom(reader);
  23.             cmdInfo.DllName = obj["DllName"].ToString();
  24.             cmdInfo.ClassName = obj["ClassName"].ToString();
  25.             cmdInfo.MethodName = obj["MethodName"].ToString();
  26.         }

  27.         var adapterFileInfo = new FileInfo(Assembly.GetExecutingAssembly().Location);       //获取当前类库文件全路径
  28.         var targetFilePath = Path.Combine(adapterFileInfo.DirectoryName, cmdInfo.DllName);  //获取目标类库文件全路径
  29.         var targetAssembly = Assembly.Load(File.ReadAllBytes(targetFilePath));              //将目标类库以二进制形式加载到程序

  30.         try {
  31.             var targetType = targetAssembly.GetType(cmdInfo.ClassName);                     //定位目标类
  32.             var targetMethod = targetType.GetMethod(cmdInfo.MethodName);                    //定位目标方法
  33.             var targetObject = Activator.CreateInstance(targetType);                        //创建目标类

  34.             cmd = () => targetMethod.Invoke(targetObject, null);                            //cmd指向类中的方法
  35.             cmd?.Invoke();                                                                  //执行方法
  36.         }
  37.         catch (System.Exception ex) {
  38.             System.Windows.Forms.MessageBox.Show(ex.Message, "提示");
  39.         }
  40.     }
  41. }

  42. public class CmdInfo
  43. {
  44.     public string DllName { get; set; }     //类库文件名
  45.     public string ClassName { get; set; }   //空间名.类名
  46.     public string MethodName { get; set; }  //方法名

  47.     public CmdInfo(string dllName, string className, string methodName) {
  48.         DllName = dllName;
  49.         ClassName = className;
  50.         MethodName = methodName;
  51.     }
  52. }

Json 文件内容参考:
  1. {
  2.     "DllName":"TestApp.dll",
  3.     "ClassName":"TestApp.ClassMain",
  4.     "MethodName":"TestCmd"
  5. }
复制代码

下面是一个用于自动加载 Dll 的 Lisp 程序用于交流分享,默认文件名为 LoadApp.lsp ,默认加载同目录中 net35 或 net48 目录下的 Reload.dll 文件,可根据自己需要修改,使用时把此程序设为自动加载即可,源码如下:
  1. ;; 通过 appload 命令加载获取最后一个加载的 Lisp 文件目录(已设为自动加载的不可用)
  2. ;; 如想要获取当前文件的目录,就必需在 appload 加载的时候直接调用执行,而非通过命令再调用
  3. (defun get-cur-path (/)
  4.   (setq last_lisp_path
  5.     (vl-registry-read
  6.       (strcat
  7.         "HKEY_CURRENT_USER\\" (vlax-product-key) "\\Profiles\\"
  8.         (vla-get-activeprofile (vla-get-profiles (vla-get-preferences (vlax-get-acad-object))))
  9.         "\\Dialogs\\Appload"
  10.       )
  11.       "MainDialog"
  12.     )
  13.   )
  14. )


  15. ;; 获取已设为自动加载程序的文件目录(附加三个文件或目录查找的可选参数增强判断准确性,不加就传 nil 即可)
  16. (defun get-folder-path (autoloadapp_fullname fileorfolder1 fileorfolder2 fileorfolder3
  17.                         / i acad_temp app_count reg_path list_path reg_file_name return_path)  
  18.   (setq acad_temp (getenv "ACAD"))  ;; 暂存系统原支持搜索路径
  19.   
  20.   ;; 设置注册表 appload 程序列表路径
  21.   (setq reg_path
  22.     (strcat
  23.       "HKEY_CURRENT_USER\\" (vlax-product-key) "\\Profiles\\"
  24.       (vla-get-activeprofile (vla-get-profiles (vla-get-preferences (vlax-get-acad-object))))
  25.       "\\Dialogs\\Appload\\Startup"
  26.     )
  27.   )
  28.   
  29.   (setq app_count (atoi (vl-registry-read reg_path "NumStartup")))  ;; 获取列表中程序数量
  30.   
  31.   (setq i 1)
  32.   (while (<= i app_count) ;; 逐个读取列表进行查找判断
  33.     (setq list_path (fnsplitl (vl-registry-read reg_path (strcat (itoa i) "Startup"))))
  34.     (setq reg_file_name (strcat (cadr list_path) (caddr list_path)))
  35.     (if (= (strcase reg_file_name) (strcase autoloadapp_fullname))
  36.       (progn
  37.         (setenv "ACAD" (strcat acad_temp ";" (car list_path)))  ;; 将目录加入系统支持搜索路径
  38.         
  39.         (if (and (/= fileorfolder1 nil) (/= fileorfolder2 nil) (/= fileorfolder3 nil)
  40.               (findfile fileorfolder1) (findfile fileorfolder2) (findfile fileorfolder3)
  41.             )
  42.           (progn
  43.             (setq i (+ app_count 1))  ;; 用于结束循环
  44.             (setenv "ACAD" acad_temp) ;; 还原系统原支持搜索路径
  45.             (setq return_path (car list_path))  ;; 返回文件目录
  46.           )
  47.           (if (and (/= fileorfolder1 nil) (/= fileorfolder2 nil)
  48.                 (findfile fileorfolder1) (findfile fileorfolder2)
  49.               )
  50.             (progn
  51.               (setq i (+ app_count 1))  ;; 用于结束循环
  52.               (setenv "ACAD" acad_temp) ;; 还原系统原支持搜索路径
  53.               (setq return_path (car list_path))  ;; 返回文件目录
  54.             )
  55.             (if (and (/= fileorfolder1 nil) (findfile fileorfolder1))
  56.               (progn
  57.                 (setq i (+ app_count 1))  ;; 用于结束循环
  58.                 (setenv "ACAD" acad_temp) ;; 还原系统原支持搜索路径
  59.                 (setq return_path (car list_path))  ;; 返回文件目录
  60.               )
  61.               (progn
  62.                 (setq i (+ i 1))
  63.                 (setq return_path nil)  ;; 返回空
  64.               )
  65.             )
  66.           )
  67.         )
  68.       )
  69.       (progn
  70.         (setq i (+ i 1))
  71.         (setq return_path nil)  ;; 返回空
  72.       )
  73.     )
  74.   )
  75. )


  76. ;; 加载应用程序
  77. (defun loadapp (/ old_CMDECHO load_path)
  78.   (setq old_CMDECHO (getvar "CMDECHO"))  ;; 获取命令回显变量初始值
  79.   (setvar "CMDECHO" 0)  ;; 关闭命令回显
  80.   
  81.   (cond
  82.     ;; 读取自动加载列表并通过 loadapp.lsp 文件与 net35、net48 两个目录来判断是否为指定路径
  83.     ((not (setq load_path (get-folder-path "loadapp.lsp" "net35" "net48" nil)))
  84.     ;; 获取通过 appload 加载时的目录(未设为自动加载程序时)
  85.     (setq load_path (get-cur-path)))
  86.   )
  87.   
  88.   (if (>= (atof (getvar "acadver")) 18.0) ;; 要求 CAD2010 及以上版本
  89.     (if (>= (atof (getvar "acadver")) 19.0)
  90.       (progn
  91.         ;; CAD版本2013(版本号:19.0)及以上
  92.         (if (>= (atof (getvar "acadver")) 19.1) ;; CAD 2014 及以上版本
  93.           ;; 添加受信任位置且避免重复添加
  94.           (progn  ;;下面通过在查寻与被查寻的字串前后加分号来去除查寻路径的子路径如:";D:\\ABC;" 与 ";D:\\ABC\\EFG;"
  95.             (if (= (vl-string-search (strcat ";" load_path ";") (strcat ";" (getvar "TRUSTEDPATHS") ";")) nil)
  96.               (setvar "TRUSTEDPATHS" (strcat (getvar "TRUSTEDPATHS") ";" load_path))
  97.             )
  98.             (if (= (vl-string-search (strcat ";" load_path "net48\\" ";") (strcat ";" (getvar "TRUSTEDPATHS") ";")) nil)
  99.               (setvar "TRUSTEDPATHS" (strcat (getvar "TRUSTEDPATHS") ";" load_path "net48\\"))
  100.             )
  101.           )
  102.         )
  103.         
  104.         (command "netload" (strcat load_path "net48\\Reload.dll"))
  105.         (princ "\n\n程序 Reload 加载完成,版本 Net48\n\n")
  106.       )
  107.       (progn
  108.         ;; CAD版本2010(版本号:18.0)至2012(版本号:18.2)
  109.         (command "netload" (strcat load_path "net35\\Reload.dll"))
  110.         (princ "\n\n程序 Reload 加载完成,版本 Net35\n\n")
  111.       )
  112.     )
  113.     (princ "\n\n程序未能加载, 要求 CAD 版本不低于 2010\n\n")
  114.   )
  115.   
  116.   (setvar "CMDECHO" old_CMDECHO)  ;; 还原命令回显变量初始值
  117. )

  118. (loadapp) ;; 用函数加载程序(由于要用临时变量存储系统变量值,固用函数来限定为局部变量)







发表于 2022-9-11 22:35:58 | 显示全部楼层
支持楼主,这个方法方便多了
发表于 2022-9-12 18:05:35 | 显示全部楼层
感谢分享,过几天有时间折腾一下
发表于 2022-10-3 13:16:48 | 显示全部楼层
有VB.net的源码?
 楼主| 发表于 2022-10-3 15:07:01 | 显示全部楼层

没有,不过你可以根据原理用VB重新写一下就行了嘛
发表于 2022-10-25 14:25:50 | 显示全部楼层
用反射的方式也能做到热加载

  1. using System.Reflection;  //反射


  1. Assembly assy = Assembly.LoadFrom(@"路径\插件.dll"); //加载插件.dll
  2. Type CommandType = assy.GetType("Namespace.Class"); // 创建类实例对象


//用反射调用属性方法
发表于 2022-11-1 11:39:24 | 显示全部楼层
1. 可以改造下,用反射的方法来直接获取 [CommandClassAttribute] 的类,获取 Init() 函数,使用默认的类和接口

2. 读取二进制文件加载,会不会丢失相对信息,比如相对路径资源什么的?我没有具体测试。

如果是高版本,可以试试隔壁的热加载
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|CAD论坛|CAD教程|CAD下载|联系我们|关于明经|明经通道 ( 粤ICP备05003914号 )  
©2000-2023 明经通道 版权所有 本站代码,在未取得本站及作者授权的情况下,不得用于商业用途

GMT+8, 2024-12-23 13:53 , Processed in 0.165118 second(s), 26 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表