LISP与VBA内存数据共享方案

在 AutoCAD 环境中,LISP (Visual LISP) 与 VBA 无法直接共享内存变量(如 LISP 的 setq 变量或 VBA 的 Public 变量),因为它们运行在不同的运行时环境且进程隔离。实现"内存级"数据交换的核心方案是利用 Windows注册表 (Registry) 作为高速中间存储,或通过 临时文件/剪贴板 进行序列化传输。其中,注册表方式最接近"内存读写"的体验,速度极快且无需管理临时文件生命周期 。

核心方案对比

特性 Windows 注册表 (推荐) 临时文本文件 剪贴板 (Clipboard)
速度 极快 (毫秒级,近似内存) 中等 (涉及磁盘 I/O) 快 (但受格式限制)
数据类型 字符串 (需序列化复杂数据) 任意文本/CSV/JSON 文本为主
稳定性 高 (系统原生支持) 中 (需处理文件锁/权限) 低 (易被用户操作干扰)
实现难度 中 (需调用 ActiveX) 低 (标准文件流) 低 (标准命令)
适用场景 高频、小数据量交互 大数据量、复杂结构 简单文本传递

注:VBA 擅长通过 COM 接口操作 Excel 和 Office 对象,而 LISP 擅长 CAD 图形逻辑,两者结合通常用于将 Excel 数据导入 CAD 绘图或反之 。


方案一:基于注册表的高速数据交换(类内存操作)

此方法利用 WScript.Shell 组件在注册表当前用户目录下读写键值。对于 LISP 和 VBA 而言,这相当于读写一个全局字典,速度远快于文件 IO。

  1. VBA 端:写入数据到注册表

VBA 通过创建 WScript.Shell 对象,将数据序列化为字符串写入指定路径。

vba 复制代码
' 在 VBA 编辑器中 (Alt+F11) 插入模块
Sub WriteDataToRegistry()
    Dim wsh As Object
    Dim regPath As String
    Dim dataValue As String ' 初始化对象 Set wsh = CreateObject("WScript.Shell")    ' 定义注册表路径 (建议在 CurrentUser下建立专用文件夹)
    regPath = "HKEY_CURRENT_USER\Software\AutoCAD_LISP_VBA_Share\"    ' 模拟数据:坐标点列表 "x1,y1;x2,y2"
    dataValue = "100.5,200.3;150.0,250.0;300.2,100.8"
    
    ' 写入注册表,键名为 "CoordData" ' RegWrite 方法自动处理字符串类型
    wsh.RegWrite regPath & "CoordData", dataValue, "REG_SZ"
    
    ' 写入状态标志,通知 LISP 数据已就绪
    wsh.RegWrite regPath & "Status", "Ready", "REG_SZ"
    
    MsgBox "数据已写入注册表内存区", vbInformation
End Sub
  1. LISP 端:从注册表读取数据

LISP 通过 vlax-create-object 调用相同的 COM 接口读取数据,并解析为 LISP 表结构。

lisp 复制代码
;; 加载 ActiveX 支持(vl-load-com)

(defun c:ReadDataFromRegistry ()
  (setq wsh (vlax-create-object "WScript.Shell"))
  (setq regPath "HKEY_CURRENT_USER\\Software\\AutoCAD_LISP_VBA_Share\\")
  (setq dataKey (strcat regPath "CoordData"))
  (setq statusKey (strcat regPath "Status"))
  
  ;; 错误捕获,防止键不存在时报错
  (vl-catch-all-apply '(lambda ()
 (setq rawData (vlax-invoke-method wsh 'RegRead dataKey))
       (setq status (vlax-invoke-method wsh 'RegRead statusKey))
       
       (if (= status "Ready") (progn
           ;; 解析字符串 "x,y;x,y" 为 LISP 点对列表 ((x . y) (x . y))
           (setq pointList nil)
           (foreach seg (vl-string-split rawData ";")
 (setq coords (vl-string-split seg ","))
             (if (= (length coords) 2)
               (setq pointList 
                     (append pointList 
                             (list (cons (atof (nth 0 coords)) (atof (nth 1 coords))))))
             )
           )
           (princ "
成功从注册表读取数据:")
           (princ pointList)
           ;; 此处可调用绘图函数,如 (command "_point" ...)
         )
 (princ "
数据未就绪或状态标志错误。")
 )
     ) )
  
  ;; 释放对象
  (vlax-release-object wsh)
  (princ)
)

;; 辅助函数:分割字符串 (若低版本 CAD 无 vl-string-split)
(defun vl-string-split (str delimiter / pos result) (while (setq pos (vl-string-search delimiter str))
    (setq result (append result (list (substr str 1 pos)))
          str (substr str (+ pos 1 (strlen delimiter))))
  )
  (append result (list str))
)

方案二:基于临时文件的批量数据交换

当数据量极大(如数万行 Excel 数据)时,注册表可能受限,此时应采用内存映射文件或高速临时文件。VBA 负责从 Excel 提取数据写入文件,LISP读取并绘制 。

  1. VBA 端:导出 Excel 数据到临时文件

利用 VBA 强大的 Excel 对象模型,将单元格范围快速输出为 CSV 格式。

vba 复制代码
Sub ExportExcelToTempFile()
    Dim fs As Object
    Dim ts As Object
    Dim rng As Range
    Dim cell As Range
    Dim filePath As String
    Dim lineText As String
    Dim colText As String
    
    ' 获取系统临时目录
    filePath = Environ$("TEMP") & "\cad_data_exchange.csv"
    
    Set fs = CreateObject("Scripting.FileSystemObject") Set ts = fs.CreateTextFile(filePath, True) ' True 表示覆盖现有文件
    
    ' 假设数据在 Sheet1 的 A1:C100 区域
    Set rng = ThisWorkbook.Sheets("Sheet1").Range("A1:C100")
    
    ' 遍历行
    For Each rowCell In rng.Rows
        lineText = ""
        ' 遍历列
        For Each cell In rowCell.Cells
            colText = CStr(cell.Value) If lineText = "" Then
                lineText = colText
            Else
                lineText = lineText & "," & colText
            End If
        Next cell
        ts.WriteLine lineText Next rowCell
    
    ts.Close
    MsgBox "数据已导出至临时文件:" & filePath
End Sub
  1. LISP 端:读取文件并处理

LISP 使用标准文件流操作读取 CSV,适合批量生成图形实体。

lisp 复制代码
(defun c:ImportDataFromFile ()
 (setq filePath (strcat (getenv "TEMP") "\\cad_data_exchange.csv"))
  (setq fileHandle (open filePath "r"))
  
  (if fileHandle
    (progn
      (setq dataList nil)
      ;; 逐行读取      (while (setq lineStr (read-line fileHandle))
        ;; 简单解析 CSV: 假设格式为 X,Y,Z
        (setq parts (vl-string-split lineStr ","))
        (if (>= (length parts) 3)
          (setq dataList 
                (append dataList 
                        (list (list (atof (nth 0 parts)) 
                                    (atof (nth 1 parts)) 
                                    (atof (nth 2 parts))))))
        )
      )
      (close fileHandle)
      
      (princ (strcat "
成功读取 " (itoa (length dataList)) " 条数据。"))
      
      ;; 示例:根据数据绘制三维多段线
      (if (> (length dataList) 0)
        (progn
          (command "_3DPOLY")
          (foreach pt dataList
            (command pt)
          )
          (command "") ; 结束命令
        )
      )
    )
    (princ "
错误:无法打开临时文件。")
  )
  (princ)
)

关键技术要点说明

  1. 数据序列化:由于注册表和文本文件主要存储字符串,复杂数据结构(如嵌套表、对象属性)必须在写入前序列化为特定格式(如 JSON、XML 或自定义分隔符字符串),读取后再反序列化 。
  2. 同步机制:在异步操作场景下(如 VBA 后台运行),建议像方案一那样在注册表中设立"状态标志位"(Status Key),LISP 轮询该标志位确认数据完整性后再读取,避免读到半写数据。
  3. 环境依赖 :上述代码均依赖 vl-load-com 加载 ActiveX 接口,这是 Visual LISP 与外部程序(包括 VBA 背后的 COM 库)交互的基础 。
  4. 选型建议 :
    • 若需频繁交互少量配置参数或坐标点,注册表法最优,体验最接近内存共享。
    • 若涉及 Excel 大量行列数据导入 CAD 绘图,临时文件法更稳健,且便于调试查看中间数据 。

参考来源

相关推荐
吴可可1232 小时前
Lisp操作注册表及数据类型详解
lisp
吴可可1232 天前
IF与PROGN在LISP中的组合应用
lisp
吴可可1237 天前
Lisp函数定义与括号语法解析
lisp
吴可可1238 天前
AutoLISP轻松移动多段线顶点
lisp
吴可可12310 天前
多段线整体弯曲LISP实现
lisp
吴可可12310 天前
查找最近多段线的LISP实现
lisp
吴可可12311 天前
Lisp函数定义四法详解
lisp
aluluka11 天前
《elisp 每日一练》——Emacs的启动加速
emacs·lisp·elisp
吴可可12312 天前
LISP实现多段线精准三段打断
lisp