雪山飞狐_lzh 发表于 2004-5-11 10:14:00

AutoCAD开发常见问题

版本老了点,是R14的不过有些还有用


张朝阳 <BR>                                       Autodesk China <BR>                                       问:有什么方式可以在AutoCAD启动的时候自动加载VBA工程? <BR>                                       答:当VBA加载时会在AutoCAD目录下寻找一个名叫acad.dvb的工程。如果找到,就会自动加载它。如果你想VBA和缺省工程在AutoCAD启动的时候每次都加载,你需要在acad.rx文件中生成一个入?。VBA被设计成命令加载方式(在没有激活一个VBA命令之前不占用任何内存和进程)。为了总是加载VBA和acad.dvb工程,请在acad.rx文件中包含下列一行:


                                       acadvba.arx <BR>                                       在AutoCAD <BR>                                       2000中支持嵌入工程,这样当你打开拥有嵌入工程的图形文件时该工程就会自动加载。所以定义嵌入工程也是一个很好的自动加载VBA工程的办法。 <BR>                                       问:有办法可以在AutoCAD启动时自动运行一个VBA例程或宏吗?


                                       答:当然。你可以从AutoCAD的acad.lsp文件的启动功能中通过VBARUN的命令行版本运行一个工程中的宏。首先,你需要准备acad.dvb文件以便自动加载。以drawline.dvb作为例子,用VBALOAD命令弹出VBA <BR>                                       IDE对话框。然后用VBA <BR>                                       IDE保存为菜单命令保存此工程为新的名字acad.dvb。下一步,激活notepad.exe并且建立或添加下列行到acad.lsp文件中: <BR>                                       (defun S::STARTUP() (command "_-vbarun" "drawline")) <BR>                                       问: AutoCAD支持VBA多工程的打开吗? <BR>                                       答:在2000中支持。但在14版中尚不支持。 <BR>                                       问:我可以加密我的VBA模块代码吗? <BR>                                       答:虽然VBA不支持可执行文件的创建,但是它确实在一个工程的基础上提供了口令保护工程窗体、类和模块可见性的功能。你可以发现这个工程保护功能在VBA <BR>                                       IDE菜单中。选择 工具 &gt; 工程属性 &gt; 保护。如下图所示:


                                       问:在VBA中如何在命令行上提示? <BR>                                       答:有一些方法允许在命令行提示输入以获取数据。这些方法以“GET”开头。你可以发现决大多数的方法(如果不是全部的话)通过对象浏览器搜索AutoCAD类型库。启动VBA <BR>                                       IDE。击F2功能键或者从视图菜单中选择对象浏览器。?对象浏览器窗口中使用库范围下拉列表把缺省的&lt;所有库&gt;改成AutoCAD。库列表下面的编辑列表框是用来搜索的。输入GET到那个编辑列表框中并且敲回车键。包含“GET”的类和成员函数就会显示在滚动列表中。AcadUtility的一些成员函数可以?命令行上提示用户输入。如下图所示:

雪山飞狐_lzh 发表于 2004-5-11 10:15:00

问:我怎样从AutoCAD菜单中激活一个VBA例程? <BR>                                       答:你需要编辑菜单和添加VBARUN模块子例程。首先确认正确的VBA工程已经加载。 <BR>                                       问:我怎样从AutoCAD菜单中启动一个VBA对话框(窗体)? <BR>                                       答:除了创建一个VBA窗体,你还需要创建一个VBA子例程来显示这个窗体。而这个子例程需要用VBARUN命令激活。1. 启动VBA IDE;2. <BR>                                       从菜单中选择 插入 &gt; 用户窗体(缺省名称为UserForm1);3. 添加合适的控件到你的窗体中; 4. 下一步,从菜单中选择 插入 &gt; <BR>                                       模块;5. 输入以下的代码到缺省的Module1模块中:Sub Foo() UserForm1.showEnd Sub6. <BR>                                       最后,编辑AutoCAD菜单调用以下命令来激活对话框: -VBARUN Module1.foo. <BR>                                       问:我已经写了一个VBA例程用ThisDrawing.Utility.GetPoint获取一个点。当我从一个VBA对话框中启动这个例程时,我可以在命令行上看到提示。但是我不能在AutoCAD屏幕上点取一个点。我作错了什么?


                                       答:首先需要关闭对话框,然后才能从AutoCAD获取数据。添加以下的代码到ThisDrawing.Utility.GetPoint之前的任何地方以关闭对话框,然后你就可以获得一个点了。


                                       Me.Hide <BR>                                       问:有方法加载一个VBA工程而在加载后不显示VBA IDE吗?


                                       答:如果你设置AutoCAD系统变量FILEDIA为0,或者在你加载一个工程的时候把&lt;打开VBA编辑器&gt;的开关关闭,你就不会?加载之后看到VBA <BR>                                       IDE了。这是一个标准的AutoCAD转换机制。 <BR>                                       问:VBA和AutoLISP有何不同,两者如何转换? <BR>                                       答:如果你已经熟知AutoLISP并且想学习VBA,通过把VBA和AutoLISP表达式联系起来的方法就可以容易地做到。以下就是一个关于两种语言的比较。由于大量的AutoLISP代码可以利用,转换一个已有的AutoLISP代码段为VBA远比重新编制VBA代码来得容易。如果两者的变量名字保持一致,那么比较两者的代码也就变得容易了。


                                       点: <BR>                                       在AutoLISP中,你用(setq)函数来设置一个点。下面这一行把点(0,0,0)分配给PT1变量。(0,0,0)代表这个点位置的x,y,z坐标。


                                       (setq PT1 '(0 0 0)) <BR>                                       在VBA中,这个操作需要更多的代码行来完成: <BR>                                       Dim PT1(0 To 2) As Double <BR>                                       PT1(0) = 0# <BR>                                       PT1(1) = 0# <BR>                                       PT1(2) = 0# <BR>                                       在VBA中,你使用Dim语句来声明变量。点总是保存在一个三维双精度值数组中。一个数组也就是一个变量列表。在数组中的变量都拥有相同的名字。数组中的第一个元素使用索引值0,即PT1(0)。第二个元素使用索引值1,即PT1(1)。依次类推。


                                       VBA使用#号作为双精度值类型的后缀字符。在这个数组中,PT1(0)保存x坐标值,PT1(1)保存y坐标值,PT1(2)保存z坐标值。 <BR>                                       既然你已经知道如何保存一个点,我们就可以写一个VBA宏来在两点之间画一条线。 <BR>                                       在AutoLISP中,你使用(defun)来定义一个名叫Line1的T诤智暗腃: <BR>                                       意味着拥护可以直接从AutoCAD命令行上执行这个函数。下一步,分配两个点,然后用AutoCAD的LINE命令?两点之间画一条线了。 <BR>                                       在VBA中,一个子例程创建一个与AutoLISP相当的函数。下面是AutoLISP和VBA的源代码。 <BR>                                       AutoLISP code <BR>                                                       <BR>(defun C:LINE1 ()<BR>(setq PT1 '(0 0 0)) <BR>(setq PT2 '(4 4 0))<BR>(command "._line" PT1 PT2 "")<BR>)


                                       VBA code <BR>                                                       <BR>Sub Line1()<BR>Dim NewLine As Object<BR>Dim PT1(0 To 2) As Double<BR>Dim PT2(0 To 2) As Double<BR>PT1(0) = 0#<BR>PT1(1) = 0#<BR>PT1(2) = 0#<BR>PT2(0) = 4#<BR>PT2(1) = 4#<BR>PT2(2) = 0#<BR>Set NewLine = ModelSpace.AddLine(PT1, PT2)<BR>End Sub


                                       子例程的第一行声明变量NewLine为一个对象。对象是一个应用程序的一个元素。线,视图和层都是对象的例子。下一步,点数组声明了两个点值。最后,AutoCAD从(0,0,0)到(4,4,0)画一条线。添加一条线到模型空间中,你需要使用ModelSpace对象集合中的AddLine方法和一条线的起始和终止点。


                                       获取点: <BR>                                       用AutoLISP,你经常需要提示拥护选择一个点。你可以在VBA中用Utility对象的GetPoint方法达到这个目的。你需要声明一个额外的VarRet变量来保存返回值。以下是Line2的AutoLISP和VBA的代码对照:

雪山飞狐_lzh 发表于 2004-5-11 10:15:00

AutoLISP code <BR>                                                       <BR>(defun C:LINE2 ()<BR>(setq PT1 (getpoint "\nStart Point: ")) <BR>(setq PT2 (getpoint PT1 "\nEnd Point: "))<BR>(command "._line" PT1 PT2 "")<BR>)


                                       VBA code <BR>                                                       <BR>Sub Line2()<BR>Dim VarRet As Variant<BR>Dim NewLine As Object<BR>Dim PT1(0 To 2) As Double<BR>Dim PT2(0 To 2) As Double<BR>VarRet = Utility.GetPoint(, "Start Point: ")<BR>PT1(0) = VarRet(0)<BR>PT1(1) = VarRet(1)<BR>PT1(2) = VarRet(2)<BR>VarRet = Utility.GetPoint(PT1, "End Point: ")<BR>PT2(0) = VarRet(0)<BR>PT2(1) = VarRet(1)<BR>PT2(2) = VarRet(2)<BR>Set NewLine = ModelSpace.AddLine(PT1, PT2)<BR>End Sub


                                       这一次,GetPoint方法返回一个Variant数据类型。这种数据类型可以保存任何类型的数据。为了使用你选择的并且保存在变量VarRet之中的开始和终止点,你必须转换Variant数据为一个双精度值的数组并且把他们付值给PT1和PT2。


                                       现在两个VBA子例程已经建立起来了,如何运行它们呢? <BR>                                                       启动AutoCAD,打开一个新图; <BR>                                                       加载工程; <BR>                                                       选择 工具|宏|运行宏; <BR>                                                       选择宏位置(缺省为所有活动图形和工程); <BR>                                       5. 从窗口中选择宏名称; <BR>                                       6. 点击运行按钮。

雪山飞狐_lzh 发表于 2004-5-11 10:18:00

AutoCAD开发常见问题(二)


                                       张朝阳                       <BR>                                       Autodesk China <BR>        <BR>                                       问:能够给AutoCAD的VBA宏传递参数吗? <BR>                                       答:从AutoCAD直接传递参数到一个VBA宏中是不可能的。但是你可以用VBA的GetString 方法和LISP的(command)函数来传递信息。


                                       举例: <BR>                                       首先定义一个VBA宏: <BR>                                       Sub testparams() <BR>                                       Dim str, str2 As String <BR>                                       str=ThisDrawing.Utility.GetString(False) <BR>                                       str2=ThisDrawing.Utility.GetString(False) <BR>                                       MsgBox str <BR>                                       MsgBox str2 <BR>                                       End Sub <BR>                                       然后用以下LISP语句调用此宏: <BR>                                       (command "-VBARUN" "testparams" "param1" "param2") <BR>                                         <BR>                                       问:.MNU和.LSP文件的调用顺序是怎样的? <BR>                                       答:他们在AutoCAD 2000的调用顺序是这样的: <BR>                                       当AutoCAD启动时: <BR>                                       - acadr2000.lsp <BR>                                       - acad.lsp <BR>                                       对于每个新打开的文档: <BR>                                       - acad.lsp, 如果在AutoCAD的配置对话框中的系统标签下做了相应的选项设定。 <BR>                                       - acaddoc2000.lsp <BR>                                       - acaddoc.lsp <BR>                                       - .MNU菜单文件和它们的关联.MNL文件。 <BR>

雪山飞狐_lzh 发表于 2004-5-11 10:19:00

问:我已经写了一个VisualLisp函数和一个VBA宏。这个VBA宏显示一个对话框,并且有一些按钮允许有用户输入(例如:输入一个字符串)。现在我从VisualLisp用(vl-vbarun)调用这个VBA宏。当AutoCAD执行到这一行时,LISP停在这一行。但是一旦我在此宏中使用了一个用户输入函数,LISP就不等VBA对话框出现,而继续执行下面的LISP函数。有些时候甚至首先执行LISP的输入函数,然后再执行VBA宏中的输入语句。怎样才能使我的LISP函数和VBA宏同步?


                                       答:现在只有一个解决办法。在你的LISP函数中(vl-vbarun) <BR>                                       函数必须最后调用。当关闭VBA对话框或者从VBA宏中返回时,你得调用另一个你想继续执行的LISP函数。你可以象下面这样做。 <BR>                                       首先:你需要一个LISP函数调用这个VBA宏: <BR>                                       (defun c:test () <BR>                                       ;; Call a VBA macro which displays <BR>                                       ;; a dialog and writes a file. <BR>                                       (vl-vbarun "test") <BR>                                       ;;If you uncomment the following line, then AutoCAD will prompt you <BR>                                       ;;to select an entity first, input a string second. <BR>                                       ;;(entsel) <BR>                                       (princ) <BR>                                       ) <BR>                                       然后你需要第二个LISP函数以供你的VBA宏返回时调用: <BR>                                       (defun c:test_cont () <BR>                                       ;; Here you can continue. <BR>                                       (print “ I am continuing.”) <BR>                                       (princ) <BR>                                       ) <BR>                                       以下是这个VBA宏的示例: <BR>                                       Sub test() <BR>                                       ' Show a dialog which uses some <BR>                                       ' user input functions. <BR>                                       UserForm1.Show <BR>                                       ' Call the lisp function which should <BR>                                       ' be now executed. <BR>                                       ' You can use the SendCommand method: <BR>                                       ' ThisDrawing.SendCommand ("test_cont ") <BR>                                       ' or use the ActiveX interface of VisualLisp: <BR>                                       ' In order to use it, please remember to reference vl.tlb file which is in <BR>                                       Acad2000 folder <BR>                                       ' into your VBA project and execute (vl-load-com) on the AutoCAD command <BR>                                       line first. <BR>                                       Dim vl As Object <BR>                                       Set vl = CreateObject("VL.Application.1") <BR>                                       vl.ActiveDocument.functions.Item("c:test_cont").funcall <BR>                                       End Sub <BR>                                         <BR>                                       问:怎样用Visual Basic生成一个用户接口模块以供Visual LISP方便地调用? <BR>                                       答:最简单和最有效的方法是为AutoCAD写一个内过程(in-process)的自动客户(Automation Client)。例如:一个VB <BR>                                       ActiveX DLL。这个DLL 以后可以从 Visual LISP, VBA, Java <BR>                                       (通过Automation)和ObjectARX加载。它可以是一个AutoCAD的自动客户,也可以是一个任何的自动服务器( Automation <BR>                                       server),或者多重服务器。 <BR>                                       1. 启动Visual Basic 5 or 6; <BR>                                       2. 在New Project Wizard中选择ActiveX DLL; <BR>                                       3. 把工程名改为"MyProject"; <BR>                                       4. 在工程中有一个缺省的类模块,把它的名称改为"MyClass"; <BR>                                       5. 添加一个函数或者子例程到类模块中。例如: <BR>                                       ' This function takes two arguments, and will return a list of data to the <BR>                                       calling function <BR>                                       Public Function MyFn(ByRef arg1 as Integer, ByRef arg2 as Double) As <BR>                                       Variant <BR>                                       myForm.Show vbModal <BR>                                       ' Create a list of items to return to the caller (the items are in this <BR>                                       case purely arbitrary) <BR>                                       MyFn = Array(1.0,"Arbitrary string",2) <BR>                                       End Function <BR>                                       (这里,myForm是一个你必须添加到工程中的表格。同时切记MyFn是一个函数,它将返回一个值或者一组值给调用例程。) <BR>                                       6. 点取File -&gt; Make <BR>                                       MyProject.dll。这就会生成一个DLL并且把它注册为COM。(如果你想在其他机器上运行此DLL,你需要首先确认在所有的机器上安装并注册了这个DLL。这通常需要你用Visual <BR>                                       Basic生成一个安装包。) <BR>                                       7. 如果你想从Visual LISP中使用此DLL,你需要定义一个简单的函数,并且把他加载到AutoCAD中: <BR>                                       (defun showDialog (/ acadApp vbApp retVal retList) <BR>                                       ;; required in AutoCAD 2000, not R14 <BR>                                       (if (car (atoms-family 1 '("vl-load-com"))) (vl-load-com)) <BR>                                       ;; get the main AutoCAD application object <BR>                                       (setq acadApp (vlax-get-acad-object)) <BR>                                       ;; load VB ActiveX DLL into AutoCAD's address space (either line will <BR>                                       work) <BR>                                       ;;(setq vbApp (vlax-invoke acadApp "GetInterfaceObject" <BR>                                       "MyProject.MyClass") <BR>                                       (setq vbApp (vla-GetInterfaceObject acadApp "MyProject.MyClass")) <BR>                                       (if (not vbApp) <BR>                                       (princ "\nError loading ActiveX DLL.") <BR>                                       (vlax-invoke vbApp "MyFn" <BR>                                       7 ; arg1, an integer <BR>                                       1.5 ; arg2, a 'double' <BR>                                       ) <BR>                                       ) <BR>                                       ) <BR>                                       为了调用已经暴露出的ActiveX方法,在命令行上输入: <BR>                                       (showDialog) <BR>                                       将把下列内容返回给AutoCAD: <BR>                                       (1.0 "Arbitrary string" 2) <BR>                                       你会发现你可以给VB对话框传递参数并且在AutoCAD中处理返回值。这对于生成选项对话框非常有用,因为有些参数需要初始化并且修改后的值需要返回给AutoCAD。


                                       7. 如果你想从VBA使用这个DLL,你需要把此DLL添加到引用中。(用COM注册它,就会把它添加到ActiveX <BR>                                       服务器的列表中。然后它就可以被VBA引用,不然就请浏览并且选择MyProject.dll。) <BR>                                       8. 然后你就可以用下面的机制加载这个内过程 ActiveX DLL,并且调用其中的函数: <BR>                                       Sub MyVBAProject() <BR>                                       Dim oMyApp as Object <BR>                                       dim vReturn as Variant <BR>                                       set oMyApp = ThisDrawing.Application.GetInterfaceObject( <BR>                                       "MyProject.MyClass" <BR>                                       ) <BR>                                       vReturn = oMyApp.MyFn(7,1.5) <BR>                                       End Sub <BR>                                         <BR>                                       问:可以在VBA中获得带有预览图象的AutoCAD "Open File Dialog"对话框吗?       <BR>                                       答:在VBA没有直接的方法这样做。但是,你可以通过LISP和AutoCAD之间的通讯来完成。在LISP中有一个名叫getfiled的函数,它可以预览DWG并且与AutoCAD的"Open <BR>                                       File Dialog"表现一样。 <BR>                                       首先,通过SendCommand方法发送getfiled表达式给AutoCAD命令行并且定义一个系统变量USERS1以保存文件名。然后,你可以用GetVariable方法获得这个系统变量。最后,象使用其它任何变量一样使用它。


                                       Public Sub OpenDialog() <BR>                                       Dim fileName As String <BR>                                       ThisDrawing.SendCommand "(setvar " &amp; """users1""" &amp; "(getfiled " &amp; <BR>                                       """Select a DWG File""" &amp; """c:/program files/acad2000/""" &amp; """dwg""" &amp; <BR>                                       "8)) " <BR>                                       fileName = ThisDrawing.GetVariable("users1") <BR>                                       MsgBox "You have selected " &amp; fileName &amp; "!!!", , "File Message" <BR>                                       End Sub <BR>

雪山飞狐_lzh 发表于 2004-5-11 10:19:00

问: <BR>                                       如何在AutoLISP调用完一个VBA宏后,卸载包含它的VBA工程?我想首先加载这个工程,调用一个宏,然后卸载它。下面是我使用的代码,但是VBAUNLOAD在Test宏结束之前被发送到命令行。


                                       (command "-VBARUN" "Test") <BR>                                       (command "vbaunload") <BR>                                       答:主要的问题是你的宏当中有要求用户输入的语句,这时LISP试图执行下一条命令,即VBAUNLOAD。我们需要等待LISP语句执行完毕,或者让LISP在调用VBARUN命令后什么也不做,而让VBA卸载它自己。下面是这两种方法:


                                       1) 在LISP中等待。 <BR>                                       (defun c:RunMacro( / oldFileDia oldCmdEcho) <BR>                                       (setq oldFileDia (getvar "FILEDIA") <BR>                                       oldCmdEcho (getvar "CMDECHO") <BR>                                       ) <BR>                                       (setvar "FILEDIA" 0) <BR>                                       (setvar "CMDECHO" 0) <BR>                                       (command "_VBALOAD" "c:\\test.dvb") <BR>                                       (command "_-VBARUN" "Test") <BR>                                       (while (= "-VBARUN" (getvar "CMDNAMES")) <BR>                                       (command pause) <BR>                                       ) <BR>                                       (command "_VBAUNLOAD") <BR>                                       (setvar "FILEDIA" oldFileDia) <BR>                                       (setvar "CMDECHO" oldCmdEcho) <BR>                                       (princ) <BR>                                       ) <BR>                                       2) VBA中自动卸载。 <BR>                                       你可以使VBA代码强制卸载它自己。在你的宏的最后(例如:UserForm_Terminate()),使用下列代码可以达到此目的: <BR>                                       AppActivate ThisDrawing.Application.Caption <BR>                                       SendKeys "_VBAUNLOAD" + Chr$(13) <BR>                                       以上的代码在AutoCAD R14.01中工作良好,但是AutoCAD <BR>                                       2000允许加载多个工程。相应地,VBAUNLOAD增加了一个额外参数。有两个新的函数可以加载和运行VBA <BR>                                       代码:(vl-vbaload)和(vl-vbarun)。你可以通过在宏名中指定工程名字把这两种操作联合在一起。 <BR>                                       (defun c:RunMacro2000( / oldFileDia oldCmdEcho) <BR>                                       (setq oldFileDia (getvar "FILEDIA") <BR>                                       oldCmdEcho (getvar "CMDECHO") <BR>                                       ) <BR>                                       (setvar "FILEDIA" 0) <BR>                                       (setvar "CMDECHO" 0) <BR>                                       (vl-vbarun "c:\\test.dvb!Test") <BR>                                       (while (= "-VBARUN" (getvar "CMDNAMES")) <BR>                                       (command pause) <BR>                                       ) <BR>                                       (command "VBAUNLOAD" "c:\\test.dvb") <BR>                                       (setvar "FILEDIA" oldFileDia) <BR>                                       (setvar "CMDECHO" oldCmdEcho) <BR>                                       (princ) <BR>                                       ) <BR>                                       第二种方法也需要做一些小的改动。我们可以通过自动接口向AutoCAD发送命令,并且这比SendKeys更安全: <BR>                                       ThisDrawing.SendCommand "_VBAUNLOAD C:\TEST.DVB" + Chr$(13) <BR>                                       如果我们卸载另一个工程,我们可以使用自动接口 (ThisDrawing.Application.UnloadDVB "test.dvb" <BR>                                       )。但是在这种情况下我们需要使用SendKeys或SendCommand来卸载其本身。 <BR>                                         <BR>                                       问:在R13中,我可以通过检查DWK文件知道DWG文件是否被锁定了。在R14中,没有一个ARX函数来检查文件锁定状态。有什么其它办法吗? <BR>                                       答:因为我们现在使用了操作系统支持的文件锁定方式,你可以通过用_fsopen()打开图形文件来检查文件是否锁定。使用模式"r+"和共享标志SH_DENYWR,如果失败,这个图形就已经被打开并且是写的状态。如果成功,千万不要忘记关闭此文件,然后再让AutoCAD打开它。


                                       同时你也需要检查.DWK文件,因为这个文件也可能被另外的R13例程所打开。 <BR>                                       2) 用LISP运行一些VBA代码打开这个文件,通过一个系统变量返回一个成功或者失败标志给LISP。例如:下面的VBA代码可以做这件事情。 <BR>                                       Sub Test_File_Data() <BR>                                       Dim FileNum As Integer <BR>                                       Dim MyFileName As String <BR>                                       Dim sysVarName As String <BR>                                       Dim sysVarData As Variant <BR>                                       On Error GoTo ErrHandler <BR>                                       FileNum = 1 <BR>                                       MyFileName = "D:\PROGRAM FILES\AUTOCAD R14\ARCTEXT.DWG" <BR>                                       sysVarName = "USERI1" <BR>                                       sysVarData = 0 <BR>                                       ThisDrawing.SetVariable sysVarName, sysVarData <BR>                                       Open MyFileName For Binary Access Read Lock Read Write As FileNum <BR>                                       Close 1 <BR>                                       sysVarData = 1 <BR>                                       ThisDrawing.SetVariable sysVarName, sysVarData <BR>                                       Exit Sub <BR>                                       ErrHandler: <BR>                                       MsgBox "Error Number = " &amp; Err.Number <BR>                                       MsgBox "Error Description = " &amp; Err.Description <BR>                                       Err.Clear <BR>                                       sysVarData = 0 <BR>                                       ThisDrawing.SetVariable sysVarName, sysVarData <BR>                                       End Sub <BR>

雪山飞狐_lzh 发表于 2004-5-11 10:20:00

问:有什么方法可以检测一个VBA宏是否加载? <BR>                                       答:在AutoCAD <BR>                                       2000中,你可以获取一个属性名叫VBE的ActiveX应用程序对象。它使你可以访问VBAIDE扩展对象。这个对象拥有方法和属性允许你判断一个工程是否已经加载。


                                       如果你想深入学习,就请启动VBAIDE,添加一个对"Microsoft Visual Basic for Applications <BR>                                       Extensibility"的引用,然后你就可以在你的ObjectBrowser (F2)浏览此对象了。 <BR>                                       以下是四个例子。第一个"loadMyProcedure"检测所有已经加载的工程名。如果没有发现"TestMenuEcho",就加载它。 <BR>                                       第二个例子"testMacros"在每一个加载的模块中搜索每一行并且在立即窗口中 (cntrl + <BR>                                       G)打印true或者false。目的在于寻找文本"test4"(宏的名字)。 <BR>                                       第三个例子"displayLoadedProjects"在信息窗口中显示所有已加载的工程。 <BR>                                       第四个例子是一个LISP例程。它使用相同的ActiveX对象判断一个工程是否已经加载,如果没有,就加载它。 <BR>                                       Public Sub loadMyProcedure() <BR>                                       Dim int1 As Integer <BR>                                       Dim bProjectLoaded As Boolean <BR>                                       ' Iterate through all the projects <BR>                                       For int1 = 1 To Application.VBE.VBProjects.Count <BR>                                       ' Make the test boolean variable to true if the Project I want to load <BR>                                       ' is already loaded, Change TestMenuEcho to the name of your project <BR>                                       If Application.VBE.VBProjects(int1).Name = "TestMenuEcho" Then <BR>                                       ' Debug.Print Application.VBE.VBProjects(int1).Name <BR>                                       bProjectLoaded = True <BR>                                       End If <BR>                                       Next int1 <BR>                                       ' Display a message if my the project is already loaded <BR>                                       ' if it isn't then load it, change the directory and <BR>                                       ' name to that of your project <BR>                                       If bProjectLoaded = True Then <BR>                                       MsgBox "TestMenuEcho is already loaded" <BR>                                       Else <BR>                                       MsgBox "Going to load the project TestMenuEcho" <BR>                                       Application.LoadDVB "D:\Vba-apps\a-2000\menuecho in menu.dvb" <BR>                                       End If <BR>                                       End Sub <BR>                                         <BR>                                       Public Sub testMacros() <BR>                                       Dim int1 As Integer <BR>                                       Dim int2 As Integer <BR>                                       ' Iterate through all the loaded projects <BR>                                       For int1 = 1 To Application.VBE.VBProjects.Count <BR>                                       ' Name of loaded project <BR>                                       Debug.Print Application.VBE.VBProjects(int1).Name <BR>                                       ' Iterate through the text of each module looking for "test4" <BR>                                       For int2 = 1 To Application.VBE.VBProjects(int1).VBComponents.Count <BR>                                       ' Get the number of lines in each module - use in the <BR>                                       ' find method below <BR>                                       Dim L As Long <BR>                                       L = Application.VBE.VBProjects(int1).VBComponents(int2) _ <BR>                                       .CodeModule.CountOfLines <BR>                                       Debug.Print Application.VBE.VBProjects(int1).VBComponents(int2) _ <BR>                                       .CodeModule.Find("test4", 1, 1, L, 1, False, False) <BR>                                       Next int2 <BR>                                       Next int1 <BR>                                       End Sub <BR>                                         <BR>                                       Public Sub displayLoadedProjects() <BR>                                       Dim strProjectNames <BR>                                       Dim int1 As Integer <BR>                                       strProjectNames = "Names of loaded projects " &amp; vbCrLf <BR>                                       For int1 = 1 To Application.VBE.VBProjects.Count <BR>                                       strProjectNames = strProjectNames + Application.VBE.VBProjects(int1).Name <BR>                                       &amp; vbCrLf <BR>                                       Next int1 <BR>                                       MsgBox strProjectNames <BR>                                       End Sub <BR>                                       (defun c:loadMyProject () <BR>                                       ;; This routine will load a project if it is not already loaded <BR>                                       ;; the VBE (VB extensibility) ActiveX object is used to reference <BR>                                       ;; the loaded projects <BR>                                       ;; Load ActiveX       <BR>                                       (vl-load-com) <BR>                                       ;; Get the VBE extisibility object       <BR>                                       (setq acadObject (vlax-get-acad-object)) <BR>                                       (setq acadVbe (vla-get-vbe acadObject)) <BR>                                       (setq acadVbeProjects (vlax-get-property acadVbe 'VBProjects)) <BR>                                       ;; Get the number of loaded VBA projects <BR>                                       (setq int1 (vlax-get-property acadVbeProjects 'count)) <BR>                                       ;; Counter and test variable named loaded       <BR>                                       (setq int2 1) <BR>                                       (setq loaded "False") <BR>                                       ;; Repeat for each project       <BR>                                       (repeat int1 <BR>                                       ;; Itereate through the projects, getting the name of <BR>                                       ;; next project, each time through       <BR>                                       (setq Item (vlax-invoke-method acadVbeProjects 'Item int2)) <BR>                                       (setq pName (vlax-get-property Item 'Name)) <BR>                                         <BR>                                       ;; Test the name for the name of the project I want to load <BR>                                       ;; If it is already loaded the set the test variable to True <BR>                                       (if (= pName "my_test_project") <BR>                                       (progn <BR>                                       (prompt "\nmy_test_project is already loaded\n") <BR>                                       (Setq Loaded "True") <BR>                                       ) <BR>                                       ) <BR>                                       ;; Increment the number used to get the next Project <BR>                                       (setq int2 (+ int2 1)) <BR>                                       ) <BR>                                       ;; Load project if it is not already loaded by testing <BR>                                       ;; the Loaded variable <BR>                                       (if (= Loaded "False") <BR>                                       (progn <BR>                                       (princ "\nLoading my_test_project") <BR>                                       (command "-VBALOAD" <BR>                                       "D:/vba-apps/a-2000/already loaded test.dvb" <BR>                                       ) <BR>                                       ) <BR>                                         <BR>                                       附:我已将所有本文中的VBA和VLISP代码及此文打包成一个ZIP压缩文件。你可以从以下的网址免费下载。 <BR>                                       <A href="http://china.autodesk.com/support/china.html" target="_blank" >http://china.autodesk.com/support/china.html</A>的‘文件下载’ <BR>

雪山飞狐_lzh 发表于 2004-5-11 10:21:00

AutoCAD开发常见问题AutoCAD开发常见问题(三) <BR>张朝阳 (Bill Zhang) <BR>  <BR>问:我想对一个圆弧进行操作。但是当我运行下面的VBA宏时,出现一个编号为451的运行错误。它的意思是‘对象不是一个集合’。这是一个关于变体型数组(Variant <BR>Array)的常见VBA问题吗?有什么方法可以解决? <BR>Sub showArcPoints() <BR>Dim oEnt As Object <BR>For Each oEnt In ThisDrawing.ModelSpace <BR>If (TypeOf oEnt Is AcadArc) Then <BR>' This causes Runtime error 451 - Object Not a Collection <BR>' if oEnt is not late bound it works ok <BR>MsgBox "StartPoint(0) is =" &amp; oEnt.StartPoint(0) <BR>End If <BR>Next <BR>End Sub <BR>答:当VBA从一个一般对象返回变体型数组的时候,如果这个对象是晚束缚(late <BR>bound)的话,VBA不能通过这个对象的属性返回数组的一个元素。如果这个对象是早束缚(early bound)的话,那么就不受限制。 <BR>有两种办法可以解决这个问题。其一、用AcadArc 来对此对象进行早束缚。修改好的代码如下: <BR>Sub showArcPoints() <BR>Dim oEnt As Object <BR>Dim oArc As AcadArc <BR>For Each oEnt In ThisDrawing.ModelSpace <BR>If (TypeOf oEnt Is AcadArc) Then <BR>'This works well since it is early bound. <BR>Set oArc = oEnt <BR>MsgBox "StartPoint(0) = " &amp; oArc.StartPoint(0) <BR>End If <BR>Next <BR>End Sub <BR>其二、声明一个临时的变体型变量并且把它设置为需要操作的对象属性。修改的代码如下: <BR>Sub showArcPoints() <BR>Dim oEnt As Object <BR>For Each oEnt In ThisDrawing.ModelSpace <BR>If (TypeOf oEnt Is AcadArc) Then <BR>'This also works well. <BR>Dim pv_temp As Variant <BR>pv_temp = oEnt.StartPoint <BR>MsgBox "StartPoint(0) = " &amp; pv_temp(0) <BR>End If <BR>Next <BR>End Sub <BR>

雪山飞狐_lzh 发表于 2004-5-11 10:21:00

问:我想在VBA中使用Microsoft的DAO。如何使用? <BR>答:请注意DAO并不是随VBA提供的。所以首先你必须确认DAO在所有需要它运行的机器上正确安装。并且你得使用相应的Microsoft方法来获得DAO的使用许可,然后才可以使用。你会发现位于Microsoft网站上的这篇文档也许有用:


<A href="http://msdn.microsoft.com/library/techart/msdn_redistmdac.htm" target="_blank" >http://msdn.microsoft.com/library/techart/msdn_redistmdac.htm</A> <BR>根据不同版本的DAO安装在不同版本的AutoCAD上,安装程序将覆盖一个MFC <BR>DLL文件MFC42.DLL。这可能引起AutoCAD或者其他的应用程序的一些问题。所以请小心对待。如果你已经正确安装了DAO,下列的VBA代码应该运行良好。这是一个用VBA在AutoCAD中访问Microsoft <BR>Access MDB数据库的例子。 <BR>Private Sub CommandButton1_Click() <BR>Dim daoWs As Workspace <BR>Dim daoDb As Database <BR>Dim daoRs As Recordset <BR>Dim daoFields As Fields <BR>Dim recordValue As String <BR>' Create a DAO workspace object <BR>Set daoWs = CreateWorkspace("", "admin", "", dbUseJet) <BR>' Open a MDB file (and get a database object) <BR>Set daoDb = daoWs.OpenDatabase("") <BR>' Get a recordset from the database <BR>Set daoRs = daoDb.OpenRecordset("") <BR>' Move to the first record in this set <BR>daoRs.MoveFirst <BR>' Get all fields from the first record <BR>Set daoFields = daoRs.Fields <BR>' Get the value from one field. <BR>' For the index you can use an integer <BR>' or the name of the table field. <BR>recordValue = daoFields("") <BR>MsgBox ("Value: " &amp; recordValue) <BR>' <BR>' Close every DAO object <BR>' <BR>' Close the recordset object <BR>daoRs.Close <BR>' Close the database object <BR>daoDb.Close <BR>' Close the workspace object <BR>daoWs.Close <BR>End Sub <BR>问:我怎样在AutoCAD中调用具有参数的VBA函数?我知道VB/A宏没有参数。Microsoft就是这样设计的。然而,VB/A函数可以带参数。我想知道是否存在命令行或者LISP的一种途径来调用VBA函数以便从AutoCAD传递必需的参数。


答:在AutoCAD R14中, 这是不可能的。但是在AutoCAD <BR>2000利用VBASTMT命令可以完成这项任务。这种方法仅当所调用的函数(f_test)定义在一个VBA模块中,而不是在ThisDrawing对象中有效。以下的例子说明如何具体去做。这个函数需要一个字符串参数。


Function f_test(ByVal sCommand As String) <BR>MsgBox "This was sent from the AutoCAD Command Line: " &amp; sCommand <BR>End Function <BR>在AutoCAD命令行,用VBASTMT命令。 <BR>Command: vbastmt <BR>Expression: f_test "line 1,1 5,5 " <BR>或者用以下的LISP代码: <BR>(command "vbastmt" "f_test \"line 1,1 5,5 \"") <BR>问:块(Block)对象的AddDimAligned方法创建不正确的标注(Dimension)对象。我怎样才能正确地添加一个标注(Dimension)实体到一个块(Block)对象中?


答:块对象的AddDimAligned方法和其它的AddDimxxx方法导致添加的标注实体不正确。这是已知的一个AutoCAD缺陷。然而,下面的方法可以克服这个缺陷。首先,在模型空间中生成一个标注实体,拷贝这个实体到所需块对象中,最后,删掉原始的标注实体。以下的事例代码生成一个块"test",然后添加了一个DimRotated实体到块中,最后把这个块插入到模型空间中。


Sub f_SolAddDiminBlocks() <BR>'Work around for Adding dimensions to block AutoCAD 2000 <BR>Dim po_rotDim As AcadDimAligned <BR>Dim po_block As AcadBlock <BR>Dim pd_ext1(0 To 2) As Double <BR>Dim pd_ext2(0 To 2) As Double <BR>Dim pd_lineLoc(0 To 2) As Double <BR>Dim po_array(0) As Object <BR>pd_ext1(0) = 3: pd_ext1(1) = 3: pd_ext1(2) = 0 <BR>pd_ext2(0) = 10: pd_ext2(1) = 3: pd_ext2(2) = 0 <BR>pd_lineLoc(0) = 5: pd_lineLoc(1) = 4: pd_lineLoc(2) = 0 <BR>'create dimeionsion object <BR>Set po_rotDim = ThisDrawing.ModelSpace.AddDimAligned(pd_ext1, pd_ext2, <BR>pd_lineLoc) <BR>'create a new block by name test <BR>Set po_block = ThisDrawing.Blocks.Add(pd_ext1, "test") <BR>'insert a block reference <BR>ThisDrawing.ModelSpace.InsertBlock pd_ext1, "test", 1, 1, 1, 0 <BR>'copy dimension object <BR>Set po_array(0) = po_rotDim <BR>ThisDrawing.CopyObjects po_array, po_block <BR>po_rotDim.Delete <BR>'release the references <BR>Set po_block = Nothing <BR>Set po_rotDim = Nothing <BR>End Sub <BR>问:怎样才能获得当前配置的AutoCAD绘图仪列表? <BR>答:下面的VBA函数列举出所有的PC3的绘图仪。你可以在你的应用程序中使用它。 <BR>Public Function GetPlotters() As Collection <BR>Set GetPlotters = New Collection <BR>Dim strPlotter As String <BR>strPlotter = Dir(Application.Preferences.Files.PrinterConfigPath + "\*.pc3") <BR>While Not strPlotter = "" <BR>GetPlotters.Add strPlotter <BR>strPlotter = Dir <BR>Wend <BR>End Function <BR>有一个方法用来枚举所有的系统打印机。这些打印机在AutoCAD的绘图对话框中也可以使用。请参看Microsoft知识库文档: Q166008 在以下的网页: <BR>

YAOSHIWEI 发表于 2013-12-29 21:19:05

有什么方式可以
页: [1]
查看完整版本: AutoCAD开发常见问题