Lesson 3: A First Look at Code
In this lesson, you will take your first close look at the Inventor API. In Lesson 1, you typed code into a code window associated with a Form and saw how it changed the visibility of components in an assembly. In Lesson 2 you created a Windows Forms Application and referenced the Inventor API. In this lesson you will continue working on the project you created in Lesson 2, extending it to use the Inventor API to connect to or start a session of Inventor while taking a close look at how the code actually works, understanding each of the lines of code in turn.
After reviewing the code you will find more detailed information on what is meant by Object-Oriented Programming and the concept of classes in the Additional Topics section here.
Add code to the project you created in Lesson 2
In Lesson 2 you created a project and it referenced the Inventor API via Autodesk.Inventor.Interop.dll. Adding this reference to the Inventor API allows you to start using it in your project. In the following steps you will access the top level object in the API, the Inventor.Application object.
- Open Lesson 2 class library project:
Open the project you created in Lesson 2 and type the following lines of code to the Form1.vb code window (above “Public Class Form1”). You can display the code window by right-clicking on Form1.vb in the Solution Explorer and selecting View Code. To make room for the code, place the cursor to the left of Public Class and press the Enter key a few times to add some blank lines at the top of the code window.
When you look at the code above, one of the first things you’ll notice is the coloring: for readability, Visual Basic Express changes the color of certain words in the code. Words highlighted in blue are known as keywords, which can only be used in certain situations. You would not be able to declare a variable named Imports, for instance, as it is reserved to mean something very specific.
The first few lines of code start with the Imports keyword. When working with different components and APIs in your Visual Basic project, it’s quite common for classes to share the same name - there might be multiple versions of the Point class in different libraries, for instance. To avoid naming conflicts, .NET provides you with the concept of namespaces. A namespace is a way to organize classes by grouping them together in a logical way and to help you identity the class you wish to use.
To give you an example, let’s say there are two boys named John living in the same neighborhood. One possible way of uniquely identifying one of the Johns might be to use his last name. But if both Johns have the same last name, such as Smith, this is no longer enough to uniquely identify them. You now need to use an additional identifier, such as the school at which they study. If this was put into VB.NET syntax, you might use SchoolA.Smith.John for one and SchoolB.Smith.John for the other.
It’s in this way that you’re able to identify the use of specific classes in VB.NET. The Imports keyword gives you direct access to the classes contained in the included namespace, which saves the effort of having to enter the full namespace when typing the names of classes. It also causes the namespace’s various classes to be added to the list presented by the IntelliSense feature.
The Imports keyword is a directive telling the VB.NET compiler to provide access to the contents of a specified namespace from the code in this source file.
The Inventor namespace is available in your project – and for us to Import into the current code file – as we referenced Autodesk.Inventor.Interop.dll.
The following code in Form1 defines a class:
Public Class Form1
This is a standard declaration of a class and was added when the project was created by the Visual Studio Wizard. A class can be thought of as a type that can be used to describe a representation of a thing or an individual object. A class can be considered the template that describes the details of an object and is used to create individual objects. In this way classes can help define the common characteristics of objects of their type: the objects’ attributes (properties) and behaviors (methods). For more details on understanding classes see Additional Topics.
The public keyword states that the class should be accessible from other classes in this assembly as well in other Visual Studio projects which use your application as a component. The name Form1 is the name of the class.
- Add variables:
Let’s now add a couple of variables after the class declaration:
Dim _invApp As Inventor.Application
Dim _started As Boolean = False
A variable is a named location where you place data – usually of a specific type. Variables help you keep track of what’s going on in your application, as well as providing access to commonly used objects.
You’ll start by creating a variable named _invApp. Variables can be named according to your wishes, as long as the name is unique in that code-block and is not a reserved word (such as the “Imports” keyword mentioned earlier). You might also have chosen a more verbose name such as “theInventorApplication” for this variable. The underscore prefixing the variable name is a convention that denotes a class variable: one that can be accessed from all the Sub-routines and Functions in the class.
To declare the variable, you use the Dim keyword, followed by the variable name, _invApp. You specify the type of this variable after the As keyword, in this case Inventor.Application.
After declaring your _invApp variable – which will be used to provide quick access to the Inventor Application object – you’ll declare another variable, named _started, as a Boolean (which means it can either be True or False). Declaring variables reserves an area of memory to be used to store your data: the amount of memory depends on the size of the type or class being stored.
Note: Even though you added the Imports directive you still need to use Inventor.Application: using just Application, would be ambiguous, as the System.Windows.Forms namespace also contains an Application type.
You are now going to create a Sub (routine). A Sub is a block of code that can be executed under circumstances you decide: you might want it to be called when a particular button is clicked in a dialog, for instance.
You will use the keywords Public and Sub - to denote a publically callable sub-routine – and then the name of the Sub. At the end of the Sub you use the keywords End Sub. It’s also possible to add a Sub using the dropdown menus at the top of VB.NET code windows, instead of typing the code in. The Subs you can add using the VB.NET dropdowns are special default Subs associated with the class for which you’re entering code – in your case a Form class. One of these is the Sub New. You want to implement this Sub because it is called automatically when your Form is created (i.e. when your application is started). During this Sub, you will connect to an existing Inventor session or start a new one.
A Sub must be contained within a Class: let’s go ahead and add the “New” Sub to your Form1 class. You’ll first make the code window associated with Form1 active – by right-clicking on Form1.vb in the Solution Explorer and selecting “View Code” – and then selecting Form1 in the dropdown on the left and “New” in the dropdown on the right.
The Sub New procedure should now have been added to the Form1 class: it’s this Sub that gets called when the Windows Form Application is started, as the application’s default behavior is to create an object of type Form1 and display it (this can be controlled via your project’s properties).
Your application needs a session of Inventor to work: while you could choose to connect to one at different times (each time it’s needed, for instance), the approach you’re going to use in this application is to connect to an Inventor session when your application is started.
Public Sub New()
' This call is required by the designer.
' Add any initialization after the InitializeComponent() call.
- Connect to or start an Inventor session in Sub New:
Type this code after the InitializeComponent() call in this Sub. The call to InitializeComponent() was added automatically in the default implementation of Sub New. InitializeComponent helps set up the behavior of a Form. If you’re interested in seeing the code it contains, right-click on the name of the Sub and select Go To Definition. Just be sure not to change the code in the editor window: InitializeComponent’s code will be modified automatically when you add controls to your form, but you should not have to edit it manually.
' Add any initialization after the InitializeComponent() call.
_invApp = Marshal.GetActiveObject("Inventor.Application")
Catch ex As Exception
Dim invAppType As Type = _
_invApp = CreateInstance(invAppType)
_invApp.Visible = True
'Note: if you shut down the Inventor session that was started
'this(way) there is still an Inventor.exe running. We will use
'this Boolean to test whether or not the Inventor App will
'need to be shut down.
_started = True
Catch ex2 As Exception
MsgBox("Unable to get or start Inventor")
Notice how the equals sign is used to assign an object to the _invApp variable.
_invApp = Marshal.GetActiveObject("Inventor.Application")
After this line has run, the variable _invApp contains an object of type Inventor.Application that you can use. Also notice that the Dim statements you added for the Inventor Application and the Boolean are outside of the Sub itself, making them global. This means that you can access these variables from any Sub in the class. You do this to be able to use the variables in the FormClosed Sub event that you will add in the next step.
The GetActiveObject function helps us find a running session of Inventor. The string of characters (known as a ProgID) that you pass into the function, “Inventor.Application”, tells GetActiveObject the type of object you’re trying to find. You surround the call to GetActiveObject with a Try-Catch block, which means your code stays on the lookout for exceptions (a way or returning errors from .NET code). You do this as we know GetActiveObject will “throw” an exception if it doesn’t find a running Inventor session. If this happens your Catch statement gets executed, which gives us the chance to try to launch a new Inventor session.
Respond to the FormClosed event. An event is something you get notified about and can respond to in your application’s code. During your FormClosed event you’re told that Form1 is closing, and that you should clean up after it. If you launched Inventor in your Sub New, above, you should close the session you started. It’s for this reason you set your Boolean variable – _started – to True, if you had to start a new Inventor session: you can check this variable as the form is closing, and close the Inventor session from there. To add the FormClosed event Sub, select “(Form1 Events)” in the dropdown on the left and then select “FormClosed” in the dropdown on the right.
The “Form1_FormClosed” event handler is added to the Form1 class. This Sub will be run when the Form is closed.
Private Sub Form1_FormClosed(ByVal sender As Object, ByVal e As
System.Windows.Forms.FormClosedEventArgs) Handles Me.FormClosed
- Add code: inside form:
Type this code inside the Form1_FormClosed Sub:
' This will shut down the Inventor session started when the form was started
If _started Then
_invApp = Nothing
Here you Quit the Inventor session if _started is set to True (i.e. it was your code that started the session, as one was not already running).
- Run the Windows Form Application:
Run the Windows Form Application by hitting F5. If you do not have a session of Inventor running you should see one start.
Well done – you have made it to the end of a lesson containing a great deal of information!You covered a range of new topics in this lesson, including some basics of object-oriented programming – defining what a class is, what an object is – you looked at some fundamentals of the VB.NET language and also took a first close look at the framework code needed by an Inventor plug-in. You were then introduced to the main classes of the Inventor API and saw them being used in the code you created in Lesson 1.
Object Oriented Programming
In the previous lesson, you learned about how a program is a sequence of instructions that tells the computer how to perform one or more tasks. Simple programs tend to consist of a sequence or list of instructions operating on variables – data representing what’s happening in the program as it executes. But, with increasing complexity, such linear sequences of instructions (an approach also often referred to as procedural programming) can be difficult to manage and ends up not being well-suited for the task for which the code was written.
To help address the complexity problem, Object-Oriented Programming (OOP) was developed to allow source code to be structured differently, enabling componentization and reuse. OOP encourages us to develop discrete, reusable units of programming logic which are aimed at modeling objects from the problem domain and the relationships between them. A good example of this is a map. A map of a city can be considered as a model or a simplified representation of the city itself, providing a useful abstraction of the information we need to get around it.
The key to good object-oriented design is to model the elements of the problem domain as individual objects displaying the relevant behavior of the originals and the relationships between them.
A class can be thought of as a type which can be used to describe a representation of a thing. A class can be considered the template that describes the details of an object and is used to create individual objects. In this way, classes can help define the common characteristics of objects of their type: the objects’ attributes (properties) and behaviors (functions or subs).
An object is an instance of a class. These are the building blocks of Object-Oriented Programming and can be thought of as variables – often quite complex ones – which encapsulate both data and behavior.
To provide an example, the relationship between a class and an object has parallels with Inventor iFeatures. An iFeature could be considered as a class. When you create an instance of this iFeature, that instance can be thought of as an object. The iFeature is the blueprint or template: each instance of the iFeature has the set of parameters defined in the iFeature but has different parameter values. They might have a different length or width, for instance.
Use the Object Model Chart to discover the Inventor API classes
Looking at the Inventor API object model, you will see that many of the classes in the Inventor API are organized to follow the main document types in Inventor – Parts, Assemblies and Drawings. Other parts of the API are used to create user interfaces and other objects not related to a specific document type. You can download a PDF containing a diagram of the Inventor API Object Model from the Inventor Developer Center - http://www.autodesk.com/developinventor (you will find other resources available for the Inventor API there, as well).
The Object Model chart allows us to see graphically how the classes in the Inventor API relate to one another. The top level class is the Application. One of the classes accessible from the Application class is the Documents class. Under Documents you will find PartDocument, AssemblyDocument and DrawingDocument. Here is a screenshot of part of the Object Model chart that shows the relationship between the Application, Documents and DrawingDocument classes. Notice it is a hierarchy showing how classes are referenced by – and accessible from – other classes.
You could think of the Object Model chart as you would a business organization chart. At the top you have the president and under the president you may have several vice presidents. Then each vice president has directors who each has employees under them and so on. So if this business was like the Inventor API, you would need to go through the president (Application), then a vice president (Documents) and down the hierarchy to get to the employee (DrawingDocument) to get something done related to this part of the business (such as saving the DrawingDocument). Another way to look at the Object Model is to see it as a type of containment such as a building. The Application object would be the entire building containing floors. One floor would be the Documents collection and that floor contains rooms such as the various Document objects. You would need to go the building’s front door (Application), to the right floor (Documents) and then into a particular room to access the Document you want to work with.
In the Object Model Chart you see that some of the boxes around the classes are rectangular and some have rounded corners. Boxes with rounded corners are used to denote collections of objects – notice their names typically end in an “s”, as they are intended to contain multiple objects of a particular class. The Documents collection, for example, will contain one or more Document objects. A collection will have methods such as an Add method that will create a new object of that class. (The Documents.Add() method will create a new document.)
The Object Model PDF is also provided with the Inventor SDK (Software Development Kit). The SDK will be in a directory on your local system such as this:
C:\Program Files\Autodesk\Inventor 201x\SDK\DeveloperTools\Docs
While the SDK ships with Inventor, separate msi files need to be run to install it. These are DeveloperTools.msi and UserTools.msi. DeveloperTools.msi installs samples and tools intended to help developers write applications. User Tools.msi installs applications that automate tasks for Inventor users that are not available in the Inventor product. The location of the installer is different for Windows XP and Windows Vista or Windows 7.
For more information on the SDK see this file:
C:\Program Files\Autodesk\Inventor 201x\SDK\SDK_Readme.htm