Often times when coding in VBA, you may get an error message that says, “Function or interface marked as restricted, or the function uses an Automation type not supported in Visual Basic.” In this article, I will explain the reason for this error and present a good strategy to deal with it.
When does the error occur?
First off, you will only see this error when you work in VBA or another programming language/environment where you are able to early bind data types. You will not see this error in VBScript programs because all variables are always late bound. If you are not familiar with the concept of binding, early binding simply means that you have declared a variable in your program as a specific type and the compiler associates that type information to the variable before the program begins executing. On the other hand, late binding means that the specific data type of the variable is discovered as the program runs. If you browse the automation API help documentation, you might notice that all CATIA automation objects support the IUnknown interface which is what makes this discovery possible at runtime.
You will get this error any time you early bind on a CATIA automation object and then call a method that has an array argument. For example, one of the most common situations is when programming an interactive selection in your program. There are several different methods available to handle selections and they all require an array to define a filter of what types of objects can be selected. Below I will show the selectElement2 method:
'Prompt the user to select a body or geometrical set Dim varFilter(1) As Variant Dim objSel As Selection Dim strReturn As String Dim strMsg as String varFilter(0) = "Body" 'Body represents a solid body varFilter(1) = "HybridBody" 'HybridBody represents a geometrical set Set objSel = CATIA.ActiveDocument.Selection strMsg = "Select a body or geometrical set…" objSel.Clear strReturn = objSel.SelectElement2(varFilter, strMsg, false) 'Display the name of the selected item Msgbox objSel.Item2(1).Value.Name
You can see that on line 3, I declared the objSel variable as a Selection object (this is the early binding). Then on line 12, I try to call the SelectElement2 method which takes an array as an argument. This will generate the error.
Another common offender is the Measurable object which, you guessed it, is used to perform various types of measurements. This object has many different methods that take an array as an argument.
'Measure the x,y,z coordinates of a point Dim varCoords(2) As Variant Dim objPart As Part Dim objPoint As Point Dim objRef As Reference Dim objSPAWorkbench As Workbench Dim objMeasurable As Measurable 'Retrieve Point.1 from the first geometrical set in the active part Set objPart = CATIA.ActiveDocument.Part Set objPoint = objPart.HybridBodies.Item(1).HybridShapes.Item("Point.1") Set objRef = objPart.CreateReferenceFromObject(objPoint) 'Measure the coordinates of the point Set objSPAWorkbench = CATIA.ActiveDocument.GetWorkbench("SPAWorkbench") Set objMeasurable = objSPAWorkbench.GetMeasurable(objRef) objMeasurable.GetPoint varCoords 'Display the coordinates Msgbox Join(varCoords, ", ")
You can see on line 7, the Measurable object was early bound and finally on line 17 the GetPoint method was called which will generate the error.
How to best deal with this situation
The CATIA help documentation recommends that you simply do not declare the type when you plan to call one of these methods. This is definitely the easiest technique, and it is a good stategy in some situations. However, I really try to fully declare all of my variables as much as possible so that I can take advantage of the compiler’s ability to find all kinds of different mistakes in my code. This strategy can save a ton of time and headaches down the line when you are trying to track down and fix bugs in your program. So what strategy is best?
- If you only plan to make a single call to the object, just skip the type declaration as recommended above or declare the variable as Object or As Variant. This will allow the object to be late bound and avoid the error. You really don’t have any other choice here.
- If you will make more than one call to the object (as in the earlier selection example) you should declare 2 separate variables to work with. The first one should be fully declared with the type and the second one will omit a specific type. I then make both variables refer to the same object in memory then I use the untyped one to make the calls requiring an array and the typed one for all other calls. This way, I can still take the fullest advantage of the compiler to catch mistakes. Below, I have modified the earlier mentioned selection example to follow this strategy.
'Prompt the user to select a body or geometrical set Dim varFilter(1) As Variant Dim objSel As Selection Dim objSelLB As Object ' <--- notice declared as object Dim strReturn As String Dim strMsg as String varFilter(0) = "Body" 'Body represents a solid body varFilter(1) = "HybridBody" 'HybridBody represents a geometrical set Set objSel = CATIA.ActiveDocument.Selection Set objSelLB = objSel strMsg = "Select a body or geometrical set…" objSel.Clear strReturn = objSelLB.SelectElement2(varFilter, strMsg, false) If strReturn = "Normal" Then If objSel.Count2 > 0 Then Msgbox objSel.Item2(1).Value.Name End If End If
In this example, you can see that on lines 3 and 4 I declared two different variables for the selection. Then on line 10, I assign the selection object for the active document to the objSel variable then on line 11 assign that same object to the objSelLB variable. Realize that in memory there is only one Selection object but you are allowed to have many different variables point at that same memory address which is what I am doing here. By pointing two different variables at the same object, that same object can be accessed by different variables that are typed differently.
In the remaining code, you can see that I make a total of four calls to the selection object. Three of them were handled from the objSel variable which was fully typed. This allows the compiler to catch any coding mistakes I might have made in three different places that would have been overlooked if I had just used a single variable whose type was not declared. Then the untyped variable is used to make the call that requires an array argument which avoids the restricted interface error. Here, gaining three places where the compiler can help us find errors might seem insignificant but in a larger program that could easily grow to 50 places or more.
In this article, I explained why this common error occurs. There are hundreds of places in the CATIA automation API where you can potentially encounter this issue so it is important to understand what causes it and how to deal with it. The strategy I presented at the end of the article covers some interesting programming concepts related to variables and memory as well as debugging. I hope it all made sense, but if not just post a comment and I will try to explain a bit more.