Reference keys is a topic that not many people know about or understand and is an area I’ve seen people struggle with and misuse. Reference keys are not something you’ll use often in your programs but can be very handy in certain cases.
What is a Reference Key?
A reference key is the ID of an object within Inventor. You use the GetReferenceKey method of an object to get its ID, or “key”. You save the ID and at a later time use it to get the same object back. A reference key doesn’t have any time limits, which means you can get the reference key from an object, and the document can go through many changes and versions, and you can still use the reference key to find the same object, even though the object may have changed from when you originally got its reference key. Of course, this assumes the object still exists and hasn’t been deleted or removed through some edit operation.
Below is some sample code that gets the reference key from a selected assembly occurrence. By changing the selection filter the code below will work with any object that supports reference keys except for B-Rep objects (SurfaceBody, FaceShell, Face, Edge, Vertex, EdgeLoop, and EdgeUse) which are discussed later.
Public Sub GetRefKey()
' Get the active document.
Dim doc As Document
Set doc = ThisApplication.ActiveDocument
' Have an object selected.
Dim selection As Object
Set selection = ThisApplication.CommandManager.Pick( _
kAssemblyOccurrenceFilter, _
"Select an occurrence.")
' Get a reference key from the selected entity. This will
' fail if the selected object doesn't support reference keys.
Dim refKey() As Byte
Call selection.GetReferenceKey(refKey)
' Convert the reference key to a string to make saving it easier.
Dim strKey As String
strKey = doc.ReferenceKeyManager.KeyToString(refKey)
' Print out the reference key string.
Debug.Print "RefKey: """ & strKey & """"
End Sub
There are a couple of interesting things to note in the above sample. First, a reference key is an array of bytes. A byte is a number between 0 and 255 so a reference key is just a list of small numbers. Second, Inventor creates the reference key and returns it but it’s up to you to save it somewhere so you can use it later. If you’ll be writing out the reference key to a file it’s easier to deal with a String instead of an array of numbers. The KeyToString method does this for you by converting the array of bytes into a String. Later, when you need to use the key, you can use the StringToKey method to convert the String back into an array of bytes. The sample above just prints the result to the immediate window, which in my test resulted in: “AgEBAAQAAAABAAAA”.
General reference key functionality is exposed through the ReferenceKeyManager object which is obtained from a Document object. Every object that supports reference keys has the GetReferenceKey method.
Now that you have a reference key you can use it at any time, with the same document, to get back the same object. The sample below demonstrates this.
Public Sub BindRefKey()
' Get the active document.
Dim doc As Document
Set doc = ThisApplication.ActiveDocument
' Convert the string back into an array of bytes.
Dim refKey() As Byte
Call doc.ReferenceKeyManager.StringToKey("AgEBAAQAAAABAAAA", _
refKey)
' Bind the key back to the object.
Dim obj As Object
Set obj = doc.ReferenceKeyManager.BindKeyToObject(refKey)
' Select the object to be able to see the found object.
doc.SelectSet.Clear
Call doc.SelectSet.Select(obj)
End Sub
Basic Reference Key Workflow
The concept of a reference key is fairly simple. The steps are:
- Get a reference key from an object.
- Save the reference key.
- Use the reference key at a later time to get back the same object.
Common Problems
There are a few gotchas to be aware of that I’ve seen people struggle with before.
- A reference key is only good with the same document you originally got it from.
- You should ignore the actual value of a reference key. You shouldn’t compare reference keys themselves to check if two reference keys came for the same object. It’s possible that reference keys with different values will bind back to the same object. Because of this you should always bind both reference keys back to their objects and then compare the objects to see if they’re the same.
- You must use a reference key context when working with B-Rep entities. This is discussed below. It’s not needed with other types of objects.
B-Rep Entities and Reference Keys
When using reference keys with B-Rep entities the concepts shown above still apply but there is some additional work that you need to do. B-Rep entities don’t have a simple internal identifier like other objects. It’s much more complicated for Inventor to be able to find a specific B-Rep entity because they can go through significant changes as modeling operations are performed. A way to visualize how Inventor can find a specific B-Rep entity is to think of the reference key as a set of instructions that identify a particular entity. For example a specific edge might be identified as the intersection of two faces and the faces might be identified by which sketch line was used to create that face as part of an extrusion. Because of the large amount of information that could be in these instructions, a reference key to a B-Rep entity can be quite large. In order to minimize the size of B-Rep reference keys and also improve performance, the idea of a reference key context was introduced. You can think of the context as a table where the instructions are saved. Several reference keys might use one or more of the same instructions. Having this information in a table allows individual instructions to be re-used. What this means is that a B-Rep reference key isn’t the full set of instructions but is a set of references into this table. This significantly reduces the size of each reference key.
Here is the process of using reference keys for a B-Rep entity, where the new steps are in bold.
- Create a reference key context.
- Ask an object for its reference key and also providing the key context.
- Continue to get reference keys from other objects, using the same key context.
- Save the reference keys.
- Save the key context.
- Use the reference key and the key context at a later time to get back the same object.
The process is essentially the same as described above for non B-Rep entities except for when you ask an object for a reference key or bind back a reference key to an object you also need to provide the key context. The program below is similar to the previous sample except it works for B-Rep entities.
Public Sub GetBRepRefKey()
' Get the active document.
Dim doc As Document
Set doc = ThisApplication.ActiveDocument
' Have a face and edge selected.
Dim selFace As Face
Set selFace = ThisApplication.CommandManager.Pick( _
kPartFaceFilter, "Select face.")
Dim selEdge As Edge
Set selEdge = ThisApplication.CommandManager.Pick( _
kPartEdgeFilter, "Select edge.")
' Set a reference to the ReferenceKeyManager object.
Dim refKeyMgr As ReferenceKeyManager
Set refKeyMgr = doc.ReferenceKeyManager
' Create a key context.
Dim keyContext As Long
keyContext = refKeyMgr.CreateKeyContext
' Get reference keys from the selected entities.
Dim faceRefKey() As Byte
Call selFace.GetReferenceKey(faceRefKey, keyContext)
Dim edgeRefKey() As Byte
Call selEdge.GetReferenceKey(edgeRefKey, keyContext)
' Get the key context as an array of bytes and
' convert it to a string.
Dim contextArray() As Byte
Call refKeyMgr.SaveContextToArray(keyContext, contextArray)
Dim strContext As String
strContext = refKeyMgr.KeyToString(contextArray)
' Convert the reference keys to strings to make saving it easier.
Dim strFaceKey As String
strFaceKey = doc.ReferenceKeyManager.KeyToString(faceRefKey)
Dim strEdgeKey As String
strEdgeKey = doc.ReferenceKeyManager.KeyToString(edgeRefKey)
' Save the results to a file.
Open "C:\Temp\RefKeys.txt" For Output As #1
Print #1, strContext
Print #1, strEdgeKey
Print #1, strFaceKey
Close #1
End Sub
You may have noticed in the last sample that the variable used for the key context is typed as a Long. Of course, this value isn’t the key context table but is a pointer to the context. Whenever a key context is needed you pass the pointer to the context and when you create a context, a pointer is returned for you to use. When you save the key context along with the reference keys you need to save the full table, not the pointer. The SaveContextToArray method returns an array of bytes that is the full table. This is the data that you need to save, along with any reference keys that were created. The code below binds back to the B-Rep entities.
Public Sub BindBRepRefKey()
' Get the active document.
Dim doc As Document
Set doc = ThisApplication.ActiveDocument
' Get a reference to the reference key manager.
Dim refKeyMgr As ReferenceKeyManager
Set refKeyMgr = doc.ReferenceKeyManager
' Read the reference key strings from the file.
Dim context As String
Dim edgeKey As String
Dim faceKey As String
Open "C:\Temp\RefKeys.txt" For Input As #1
Line Input #1, context
Line Input #1, edgeKey
Line Input #1, faceKey
Close #1
' Convert the string to byte arrays.
Dim edgeRefKey() As Byte
Dim faceRefKey() As Byte
Dim contextArray() As Byte
Call refKeyMgr.StringToKey(edgeKey, edgeRefKey)
Call refKeyMgr.StringToKey(faceKey, faceRefKey)
Call refKeyMgr.StringToKey(context, contextArray)
' Create the context by loading the data.
Dim refKeyContext As Long
refKeyContext = refKeyMgr.LoadContextFromArray(contextArray)
' Bind back the face and edge.
Dim foundFace As Face
Dim foundEdge As Edge
Set foundFace = refKeyMgr.BindKeyToObject(faceRefKey, _
refKeyContext)
Set foundEdge = refKeyMgr.BindKeyToObject(edgeRefKey, _
refKeyContext)
' Highlight the found entities.
doc.SelectSet.Clear
doc.SelectSet.Select foundFace
doc.SelectSet.Select foundEdge
End Sub
Differences with B-Rep Entities when Binding Back
There’s a difference when binding back B-Rep entities from other types of entities. The BindKeyToObject method can return an ObjectCollection in the case where the original B-Rep entity has been split into two or more pieces. The picture below shows the progression of a model and the changes that can take place to a face. In the last step face 2 is split into two pieces by an extrude feature. When binding back, both faces will be returned in an ObjectCollection.
Below is some modified code from the sample above that handles this case. When multiples are returned the first one in the collection is considered the “best” match with no priority for the rest of the entities.
' Bind back the face and edge.
Dim foundFaces As Object
Dim foundEdges As Object
Set foundFaces = refKeyMgr.BindKeyToObject(faceRefKey, refKeyContext)
Set foundEdges = refKeyMgr.BindKeyToObject(edgeRefKey, refKeyContext)
' Highlight the found entities, special casing for the case where
' more than one might have been returned
doc.SelectSet.Clear
If TypeOf foundFaces Is ObjectCollection Then
Dim obj As Object
For Each obj In foundFaces
doc.SelectSet.Select obj
Next
Else
doc.SelectSet.Select foundFaces
End If
Important Points
Here are a few points that I want to emphasize when using reference keys with B-Rep entities.
- A single context can be used, and is intended to be used, for multiple reference keys. That’s the reason that the concept of the reference key context was created; so that multiple keys can share the same set of instructions for finding specific entities.
- A reference key context and reference keys will only work with the same document they were originally obtained from.
- Reference keys will only work with the same reference key context they were originally created with.
- When working with B-Rep entities, write code that can handle the return of either a single object or an ObjectCollection.
- The BindKeyToObject method can fail if the entity is not available. The CanBindKeyToObject method can be used to first check to see if the key can bind back.
Why Use Reference Keys?
An expected question at this point might be why would anyone want to use reference keys? They’re a generic tool that can be used in many ways but here are some examples.
Support associativity in an external read-only application
This is one of the more common uses of reference keys. Applications are able to use Apprentice to read B-Rep data and display the model in their application. When the user selects geometry in their applications they’re able to get the corresponding geometry in Apprentice and get the reference key. For example, the geometry might used for a tool path or to position a load or constraint for analysis. Getting reference keys from objects does not require write access to the model. When the model is edited in Inventor they can read in the new geometry and use the reference keys to find the geometry and update their data if the geometry has changed.
Maintaining a reference over a re-compute
This is one of the more common general uses of the functionality. An example of this is if you want to write a program that finds all of the circular planar faces in a model and places a hole feature at the center of each one. The first part of your programs finds all of the circular planar faces and then it begins placing holes on each one. The problem is that any references to B-Rep entities are no longer valid after any change has been made to the model. Creating the first hole invalidated all of the references you have to the circular faces. You don’t want to do the analysis again to find circular faces because you’ve potentially added new circular faces by placing the new feature.
Reference keys can help you solve this. Instead of maintaining a reference to a Face object you can get the reference key of each circular planar face. Reference keys are still valid after a compute, so after you create the first hole you can find the next circular planar face by using the saved reference key.
Attributes
Attributes can be used in some cases to solve the same issues that reference keys do by providing a way of adding information to an object to find it later. Attributes have the advantage that they’re self-contained and you don’t have to save any information. They also have the advantage of supporting a more powerful search mechanism. They have the disadvantage that they require write access to the file and will require the document to be saved. They also have the disadvantage that they don’t return multiple objects in the case where B-Rep entities have been split.
What Are Transient Keys?
There’s one more thing that I want to quickly touch on, which is
transient keys. Transient key functionality is only available with B-Rep entities. Each of the B-Rep entities supports the TransientKey property which returns a Long value. This is a simple identifier for that specific B-Rep entity. You can bind back to the B-Rep entity with the transient key using the BindTransientKeyToObject method on the SurfaceBody object. This is much simpler than reference keys because it’s just a single Long and you don’t need a key context. However, there’s a BIG limitation with transient keys. They’re only good as long as the model has not been modified. They’re similar to a live reference to a B-Rep entity in that it doesn’t survive through a model compute.
The purpose of transient keys is to provide the ability to match up B-Rep entities when copying bodies. For example, the AlternateBody property of the SurfaceBody returns a copy of the body it was called on. The new body may even be slightly different than the original body, (for example, some faces may have been split into two). The new body has transient keys that you can get from each B-Rep entity. You can use the BindTransientKeyToObject method to find the equivalent B-Rep entity on the original body. It’s also possible to write out a body as SAT where the transient keys are also output as attributes on the SAT data. This again allows you to match up the B-Rep entities from this output model to the original Inventor model, as long as the model has not been edited since the copy was created.
-Brian