Using a VB ActiveX DLL from Visual LISP or VBA

Published date: 2005-03-11 ID: TS15992

Applies to: AutoCAD® 2005AutoCAD® 2004AutoCAD® 2002

Issue

How can I create a user-interface module in Visual Basic that can also be easily called from Visual LISP or VBA?

Solution

The simplest and most efficient way is to write an in-process Automation client of AutoCAD, such as a VB ActiveX DLL. This DLL can then be loaded from Visual LISP, VBA, Java (via Automation) and ObjectARX. The DLL may be an Automation client of AutoCAD, or any other Automation server, or of multiple servers.

Using Visual Basic6:

1. Start Visual Basic.
2. Select "ActiveX DLL" in the New Project Wizard.
3. Change the project's "name" property to "MyProject".
4. There is a default class module in the project, change its "name" property
to "MyClass1".
5. Add a function or sub-routine to the class module such as:

' This function takes three arguments, and will
' return an array of data to the calling function
Public Function MyFn(ByRef arg1 As Integer, ByRef arg2 As String, ByRef arg3 As Double) As Variant
Dim MyForm1 As New MyForm1
'Populate textboxes with passed-in values
MyForm1.TextBox1.Text = arg1
MyForm1.TextBox2.Text = arg2
MyForm1.TextBox3.Text = arg3
MyForm1.Show vbModal

' Create an array of items to return to the caller
Dim MyArray(2) As Variant
MyArray(0) = CInt(MyForm1.TextBox1.Text)
MyArray(1) = MyForm1.TextBox2.Text
MyArray(2) = CDbl(MyForm1.TextBox3.Text)
MyFn = MyArray
End Function

(Where MyForm1 is a form you are adding to the project. Be aware that this just as well could be a function, which returns a value or a list of values to the calling process).

6. Add a Form to the project. Change its name to "MyForm1".
7. Add three TextBox controls to the Form.
8. Add a Button control which, when clicked, will hide the Form:

Private Sub Command1_Click()
Me.Hide
End Sub

9. Select File -> Make MyProject.dll. This creates the DLL and registers it with COM. (You'll need to ensure that the DLL is registered on any other computers you want to run this on. That's usually handled by creating a setup package for your DLL with Visual Basic.)

10. If you want to use the DLL from Visual LISP, simply create a function such as
the following, and load it into AutoCAD:

(defun showDialog (/ acadApp vbApp retVal retList)
(vl-load-com)
;; get the main AutoCAD application object
(setq acadApp (vlax-get-acad-object))
;; load VB ActiveX DLL into AutoCAD's address space
(setq vbApp (vla-GetInterfaceObject acadApp "MyProject.MyClass1"))
(if (not vbApp)
(princ "\nError loading ActiveX DLL.")
(vlax-invoke vbApp "MyFn"
7 ; arg1, an integer
"Arbitrary string" ; arg2, a string
1.5 ; arg3, a 'double'
)
)
)

To call the exposed function, type (showDialog) at the command line:

This will return the following list to AutoCAD:

(7 "Arbitrary string" 1.5)

You can see that you can pass arguments to the VB dialog function and also process return values back in AutoCAD. This is particularly useful for creating options dialogs, as these settings need to be initialized and the modified values need to be returned.

11. If you want to use this DLL from VBA, add a reference to this DLL (registering it with COM should add it to the list of ActiveX servers available from VBA, otherwise just browse and select MyProject.dll).

12. Use the following mechanism to load the DLL in-process, and call the function (the values are returned in an array):

Sub MyVBAProject()
Dim oMyApp As New MyClass1
Dim vReturn As Variant
Set oMyApp = ThisDrawing.Application.GetInterfaceObject("MyProject.MyClass1")
'Call function w/three arguments
vReturn = oMyApp.MyFn(7, "Arbitrary string", 1.5)
'Display values returned by function
MsgBox "Integer: " & CStr(vReturn(0)) & vbCr _
& "String: " & vReturn(1) & vbCr _
& "Double: " & CStr(vReturn(2)), _
vbOKOnly, "Returned values"
End Sub

If you watch the vReturn variable after the call to MyFn, you will see that it contains the same values as seen in AutoLISP.

Using Visual Studio .Net:

It is also possible to do this with a Visual Basic Class Library, with some minor changes.

1. Start Visual Studio .Net.
2. Select "VisualBasic Projects" and "Class Library" in the New Project Wizard.
3. Change the project's "name" property to "MyProject".
4. There is a default class module in the project, change its "name" property
to "MyClass1".
5. Add a function or sub-routine to the class module such as:

Public Class MyClass1

' This function takes three arguments, and will
' return an array of data to the calling function

' Note: In VB.NET, the function now must return a type Object instead of Variant.

Public Function MyFn(ByRef arg1 As Integer, ByRef arg2 As String, ByRef arg3 As Double) As Object
Dim MyForm1 As New MyForm1()
'Populate textboxes with passed-in values
MyForm1.TextBox1.Text = arg1
MyForm1.TextBox2.Text = arg2
MyForm1.TextBox3.Text = arg3
MyForm1.ShowDialog()

' Create an array of items to return to the caller
Dim MyArray(2) As Object
MyArray.SetValue(CInt(MyForm1.TextBox1.Text), 0)
MyArray.SetValue(MyForm1.TextBox2.Text, 1)
MyArray.SetValue(CDbl(MyForm1.TextBox3.Text), 2)
Return MyArray
End Function

End Class

6. Perform steps 6 through 8 above.

7. In your project's Property Pages, go to Configuration Properties -> Build.
Check the box labeled "Register for COM Interop". Click "OK".

If you want to use this .NETDLL from VBA, add a reference to the .tlb file.
(MyProject.tlb).

Unloading and debugging ActiveX dlls from visual LISP

Published date: 2005-01-27
ID: TS34023

Applies to:
AutoCAD® 2005
AutoCAD® 2004
AutoCAD® 2002

Issue

I am using a Visual Basic ActiveX DLL from Visual LISP, but when I rebuild it,
VB denies permission to do this. I have to close AutoCAD so that I can rebuild
the DLL. I am using (vla-GetInterfaceObject) to load the COM server. WHy does
this occur?

Solution

You are not releasing the DLL from memory. You need to call (vlax-releaseobject)
with the ActiveX object variable to release the DLL. This decrements the
reference count for the COM server, and should allow unloading. To use more
functionality in the DLL, you will have to reload it, of course.

An alternative method to debug ActiveX DLLs is to change the project settings.
Select Project->Properties in VB, then select the Debugging tab. Make sure that
the radio button "Wait for components to be created" is selected.

Now when you select Run->Start in VB, it will wait for a call to be made to load
the DLL (such as (vla-GetInterfaceObject acadApp "MyProject.MyClass")). You will
be able to set breakpoints in your DLL, which will break when the corresponding
methods are called from Visual LISP. You will be able to modify the DLL code
much more freely using this approach.

Note that when you stop execution from VB, it will ask the following question if
Visual LISP still has a reference to the DLL: "Other applications are currently
accessing an object in your program. Ending the program now could cause errors
in those programs. End at this time?" If you choose Yes, then any Visual LISP
variables referring to the DLL will be invalid. In addition, if you are calling
functionality in the DLL from Visual LISP, then Visual LISP will report a
message such as: "; error: MyProject: Application-defined or object-defined
error". You should be able to restart the debugging without having to close
AutoCAD.