明经CAD社区

 找回密码
 注册

QQ登录

只需一步,快速开始

搜索
查看: 13006|回复: 14

[讨论]LISP调用DLL动态Win32Api的实现

  [复制链接]
发表于 2007-6-10 19:54:00 | 显示全部楼层 |阅读模式
本帖最后由 作者 于 2007-6-10 20:31:20 编辑

aroom
   发帖实现Quicktool Win32Api For Lisp编程接口
各位高手来共同来讨论讨论,它是如何实现的呢?
在网上找了一段动态调用外部函数的VB代码。
  1. Option Explicit
  2. Public Declare Function LoadLibrary Lib "kernel32" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
  3. Public Declare Function GetProcAddress Lib "kernel32" (ByVal hModule As Long, ByVal lpProcName As String) As Long
  4. Public Declare Function CallWindowProc Lib "User32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  5. Public Declare Function FreeLibrary Lib "kernel32" (ByVal hLibModule As Long) As Long
  6. Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (lpDest As Any, lpSource As Any, ByVal cBytes As Long)
  7. Private m_opIndex As Long '写入位置
  8. Private m_OpCode() As Byte  'Assembly 的OPCODE
  9. Public Function RunDll32(ByVal isUnload As Boolean, ByVal strLibFileName As String, strProcName As String, ParamArray Params()) As Long
  10.     Dim hProc As Long
  11.     Dim hModule As Long
  12.     ReDim m_OpCode(400 + 6 * UBound(Params)) '保留用来写m_OpCode
  13.     '读取API库
  14.     hModule = LoadLibrary(ByVal strLibFileName)
  15.     If hModule = 0 Then
  16.         MsgBox "函数库Library:" + Chr(13) + strLibFileName + Chr(13) + "读取失败!"
  17.         RunDll32 = 0
  18.         Exit Function
  19.     End If
  20.    
  21.     '取得函数地址
  22.     hProc = GetProcAddress(hModule, ByVal strProcName)
  23.     'MsgBox "参数2:" + strLibFileName + Chr(13) + "参数3:" + strProcName
  24.     If hProc = 0 Then
  25.        MsgBox "提示:" + Chr(13) + strProcName + Chr(13) + "函数读取失败!", vbCritical
  26.        FreeLibrary hModule
  27.        RunDll32 = 0
  28.        Exit Function
  29.     End If
  30.     '执行Assembly Code部分
  31.     RunDll32 = CallWindowProc(GetCodeStart(hProc, Params), 0, 1, 2, 3)
  32.     '一些函数需要驻留这就先不释放
  33.     If isUnload Then FreeLibrary hModule  '释放空间
  34. End Function
  35. 'Public Function RunDll32(LibFileName As String, ProcName As String, ParamArray Params()) As Long
  36. '    Dim hProc As Long
  37. '    Dim hModule As Long
  38. '
  39. '    ReDim m_OpCode(400 + 6 * UBound(Params)) '保留用来写m_OpCode
  40. '    '读取API库
  41. '    hModule = LoadLibrary(ByVal LibFileName)
  42. '    If hModule = 0 Then
  43. '        MsgBox "Library读取失败!"
  44. '        Exit Function
  45. '    End If
  46. '
  47. '    '取得函数地址
  48. '    hProc = GetProcAddress(hModule, ByVal ProcName)
  49. '    If hProc = 0 Then
  50. '       MsgBox "函数读取失败!", vbCritical
  51. '       FreeLibrary hModule
  52. '       Exit Function
  53. '    End If
  54. '
  55. '
  56. '    '执行Assembly Code部分
  57. '    RunDll32 = CallWindowProc(GetCodeStart(hProc, Params), 0, 1, 2, 3)
  58. '
  59. '    FreeLibrary hModule '释放空间
  60. 'End Function
  61. Private Function GetCodeStart(ByVal lngProc As Long, ByVal arrParams As Variant) As Long
  62. '---以下为Assembly部分--
  63. '作用:将函数的参数压入堆栈
  64.    
  65.     Dim lngIndex As Long, lngCodeStart As Long
  66.    
  67.     '程序起始位址必须是16的倍数
  68.     'VarPtr函数是用来取得变量的地址
  69.     lngCodeStart = (VarPtr(m_OpCode(0)) Or &HF) + 1
  70.    
  71.     m_opIndex = lngCodeStart - VarPtr(m_OpCode(0)) '程序开始的元素的位置
  72.    
  73.     '前面部分以中断点添满
  74.     For lngIndex = 0 To m_opIndex - 1
  75.         m_OpCode(lngIndex) = &HCC 'int 3
  76.     Next lngIndex
  77.    
  78.     '--------以下开始放入所需的程序----------
  79.    
  80.     '将参数push到堆栈
  81.     '由于是STDCall CALL 参数由最后一个开始放到堆栈
  82.     For lngIndex = UBound(arrParams) To 0 Step -1
  83.        AddByteToCode &H68 'push的机器码为H68
  84.        AddLongToCode CLng(arrParams(lngIndex))  '参数地址
  85.     Next lngIndex
  86.    
  87.     'call hProc
  88.     AddByteToCode &HE8 'call的机器码为HE8
  89.     AddLongToCode lngProc - VarPtr(m_OpCode(m_opIndex)) - 4 '函数地址 用call的定址
  90.    
  91.     '-----------结束所需的程序--------------
  92.    
  93.     '返回呼叫函數
  94.     AddByteToCode &HC2 'ret 10h
  95.     AddByteToCode &H10
  96.     AddByteToCode &H0
  97.    
  98.     GetCodeStart = lngCodeStart
  99. End Function
  100. Private Sub AddLongToCode(lData As Long)
  101. '将Long类型的参数写到m_OpCode中
  102.     CopyMemory m_OpCode(m_opIndex), lData, 4
  103.     m_opIndex = m_opIndex + 4
  104. End Sub
  105. Private Sub AddIntToCode(iData As Byte)
  106. '将Integer类型的参数写道m_OpCode中
  107.     CopyMemory m_OpCode(m_opIndex), iData, 2
  108.     m_opIndex = m_opIndex + 2
  109. End Sub
  110. Private Sub AddByteToCode(bData As Byte)
  111.     '将Byte类型的参数写道m_OpCode中
  112.     m_OpCode(m_opIndex) = bData
  113.     m_opIndex = m_opIndex + 1
  114. End Sub
在VB中新建DLL工程命令如:nbtlibs,然后新建一个模块命令如:CallAPIbyName,将上面代码输入。然后再新建一类模块命令如:Win32Api,并输入:
  1. Public Function test1(hWnd As Long, ByVal s1 As String, ByVal s2 As String) As Long
  2.   s1 = StrConv(s1, vbFromUnicode)
  3.   s2 = StrConv(s2, vbFromUnicode)
  4.   test1 = RunDll32(True, "user32", "MessageBoxA", hWnd, StrPtr(s1), StrPtr(s2), 0&)
  5. End Function
在VB中最后生成DLL文件,将其复制到你需要的目录,如D:\
然后在windows的命令运行窗口(Ctrl+R)中运行:Regsvr32 d:\nbtlibs.dll,这样DLL就在windows中注册成功了。
然后在CAD的VLISP中
  1. (defun test1 (/ obj ret hwnd errorMsg)
  2.   (setq hwnd 153)
  3.   (setq obj (vlax-get-or-create-object "NBTLIBS.Win32Api"))
  4.   (setq errorMsg (VL-CATCH-ALL-APPLY 'vlax-invoke-method
  5.                                      (list obj "test1" 0 "内容 " "标题")
  6.                  ) ;_ 结束VL-CATCH-ALL-APPLY
  7.   ) ;_ 结束setq
  8.   (vlax-release-object obj)
  9.   (if (VL-CATCH-ALL-ERROR-P errorMsg)
  10.     (princ (STRCAT "发生下列错误: "
  11.                    (VL-CATCH-ALL-ERROR-MESSAGE errorMsg)
  12.            ) ;_ 结束STRCAT
  13.     )
  14.   ) ;_ 结束if
  15.   errormsg
  16. ) ;_ 结束defun
然后运行(test1),就能实现对话框了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?注册

x
"觉得好,就打赏"
还没有人打赏,支持一下
发表于 2017-11-8 20:38:26 | 显示全部楼层
回帖是一种美德!感谢楼主的无私分享 谢谢
 楼主| 发表于 2007-6-10 20:11:00 | 显示全部楼层
但是在LISP中动态调用方面却总是因为参数类型问题无法调用成功
在VB中Win32Api类模块中输入:
Public Function Win32Api(ByVal LibFileName As String, ByVal ProcName As String, ParamArray Params1()) As Long
'Public Function Win32Api(ByVal LibFileName As String, ByVal ProcName As String, ByVal Params1 As Variant) As Long
'处理字符串变量
Dim lngIndex As Long
    For lngIndex = 0 To UBound(Params1)
      If VarType(Params1(lngIndex)) = vbString Then
        Params1(lngIndex) = StrPtr(StrConv(Params1(lngIndex), vbFromUnicode))
      End If
    Next lngIndex
  Win32Api = RunDll32(True, LibFileName, ProcName, Params1)
  '("user32", "MessageBoxA", hWnd, VarPtr(s1(0)), VarPtr(s2(0)), 0&)
End Function
然后在LISP中
  1. (defun test1 (/ obj ret hwnd errorMsg)
  2.   (setq hwnd 153)
  3.   (setq obj (vlax-get-or-create-object "NBTLIBS.Win32Api"))
  4.   (setq errorMsg (VL-CATCH-ALL-APPLY 'vlax-invoke-method
  5.                                      (list obj "Win32Api" "user32" "MessageBoxA" 0 "测试内容" "标题" 0)
  6.                                      ) ;_ 结束VL-CATCH-ALL-APPLY
  7.   ) ;_ 结束setq
  8.   (vlax-release-object obj)
  9.   (if (VL-CATCH-ALL-ERROR-P errorMsg)
  10.     (princ (STRCAT "发生下列错误: "
  11.                    (VL-CATCH-ALL-ERROR-MESSAGE errorMsg)
  12.            ) ;_ 结束STRCAT
  13.     )
  14.   ) ;_ 结束if
  15.   errormsg
  16. ) ;_ 结束defun
调用却不成功!
出错信息:发生下列错误: Automation 错误。 类型不匹配
 楼主| 发表于 2007-6-10 20:30:00 | 显示全部楼层

得解决参数传递问题呀!
各位快来讨论讨论!!

发表于 2007-6-11 20:48:00 | 显示全部楼层

我曾做过试验,但基于单变量,多变量时可用Variant解决,当时的实验总结如下,或许对你有帮助。

1、在VB中,选择创建activex dll,在右上方将修改工程名为testdll,修改类名为test1
在代码区输入下列代码:
Public Function vvvaa(ByVal a As double) As double
    vvvaa = a
End Function
用文件菜单编译成testdll.dll
于是创建了一个testdll.dll文件,其中包含一个test1类,test1类中有含Public Function vvvaa定义。

2、在autolisp中,用
(setq vvvv (vlax-create-object "testdll.test1"))
(vlax-invoke-method vvvv "vvvaa" 3)
可获得返回值3(与输入值一致)

或用

(defun vvvv ( a / vbcls out)  
   (setq vbcls
    (vlax-invoke-method
       (vlax-get-acad-object)
       "getInterfaceObject"
       "testdll.test1"
    )
   )
   (setq out
    (vlax-invoke-method vbcls "vvvaa" a)
   )
   (vlax-release-object vbcls)  
   out
)
(vvvv 30)
;;可得到返回值30(与输入值一致)


;;注意事项:
;;一、函数定义时的问题
;; 1) 函数定义时应采用Public。
;; 2) 输入参数时必须采用byval,否则输入参数传不进dll。
;; 3) 如果输入参数是一个表,可将参数定义为Variant类型。
;; 4) 如果返回参数是一个表,可将函数定义为Variant类型,返回值用array构造。
;;二、autolisp中对返回参数的处理
;;1)函数定义返回简单参数时,可直接得到结果
;;2)函数定义返回variant,且结果为简单参数时,用vlax-variant-value得到结果
;;3)函数定义返回variant,且结果为表时,
;;   用(mapcar 'vlax-variant-value (vlax-safearray->list (vlax-variant-value得到表结果
;;三、autolisp中对输入参数的处理
;;1)函数定义输入为简单参数时,可直接填入数据
;;2)函数定义输入为variant时,如输入简单参数,可直接填入数据也可用vlax-make-variant输入
;;3)函数定义输入为variant时,如输入表时,用如下示例方法:
;;   (setq aa0 (vlax-make-safearray vlax-vbVariant '(0 . 2)))
;;   (vlax-safearray-fill aa0 '(9 2 "3"))
;;   (setq aa1 (vlax-make-variant aa0 ))

发表于 2007-10-7 02:20:00 | 显示全部楼层
非常感谢分享经验.这可是书本上学不到的东西~~~~~
发表于 2007-10-11 18:50:00 | 显示全部楼层
收下先,说不定会用到
发表于 2007-10-11 23:13:00 | 显示全部楼层
感谢NetBee和liu_kunlun两高手在此提供了宝贵的经验!
发表于 2007-11-7 19:54:00 | 显示全部楼层

学习了

发表于 2008-12-10 12:18:00 | 显示全部楼层
好文章 学习中
发表于 2010-12-2 10:11:00 | 显示全部楼层
呵呵,好啊,我也做个一个,但是参数传递还没弄过,应该也没问题,哈哈,时隔三年了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-11-22 18:39 , Processed in 0.187957 second(s), 28 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

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