Issue
How do we use Inventor ReferenceKeys to bind back to Inventor objects between Inventor sessions?
How do we create and store ReferenceKeys, and how do we then obtain our original objects in new Inventor sessions using ReferenceKeys?
Many Inventor objects have a unique ID called a 'Reference Key', and these Reference keys are persistent across Inventor sessions. The Inventor API allows you to obtain these Reference keys through the ReferenceKeyManager in the Document object. Through this Reference key it is possible to bind back to the original object at any time.
One of the most common application of Reference Keys is to save the Reference Key associated with individual objects and later bind back to these objects through those Keys. For example we can iterate through all Faces in the SurfaceBody object, and store Reference Keys into some persistent storage. Later on we need not iterate through all the faces of the SurfaceBody again, we can simply read the keys from the persistent storage and obtain the original faces using the Reference Keys.
The Reference Keys also have a 'context' associated with them. The context is used to reduce the size of the Reference Key when binding to BRep entities. At present it can be ignored for other entity types. The context can be thought of as a table of Inventor objects, with a Reference Key being an index into this table.
For saving and restoring the keys we need to follow some basic guidelines as outlined below.
Saving the Reference Keys:
1. Get the ReferenceKeyManager from the Document object.
2. Create the context using ReferenceKeyManager.
3. Obtain the Reference Key from desired objects.
4. Store the Reference Keys into persistent storage (e.g. text file on disk).
5. Save the context into array (ReferenceKeyManager.SaveContextToArray).
6. Store the Context Array into persistent storage
Reading the Reference Keys and binding back to the original object -
1. Read the Reference Keys.
2. Read context array.
3. Create the context from context array (using ReferenceKeyManager.LoadContextFromArray) .
4. Bind back to the original objects is now available using Reference Keys and context (via ReferenceKeyManager.BindKeyToObject).
Note: similar tasks often can be solved using Inventor attributes.
Here are VB.NET and C# standalone exe samples that connect to Inventor from out of process and demonstrate the use of Reference keys.
VB .NET
Imports System.Runtime.InteropServices
Imports System.IO
Imports Inventor
Public Class Form1
' Name of the file for saving reference keys
Private _Filename As String = "c:\temp\keydata.txt"
Private _oApp As Inventor.Application
Private Sub Form1_Load(sender As System.Object, _
e As System.EventArgs) _
Handles MyBase.Load
Try
_oApp = Marshal.GetActiveObject("Inventor.Application")
Catch ex As Exception
Beep()
MsgBox("Can't get Inventor")
End
End Try
End Sub 'Load
Private Sub btn_GetKeys_Click(sender As System.Object, _
e As System.EventArgs) _
Handles btn_GetKeys.Click
Call GetKeys()
End Sub
Private Sub btn_UseKeys_Click(sender As System.Object, _
e As System.EventArgs) _
Handles btn_UseKeys.Click
Call ReadFaceKeys()
End Sub
Private Sub GetKeys()
If _oApp.Documents.Count = 0 Then
MsgBox("Open Part Document")
Exit Sub
End If
If _oApp.ActiveDocument.DocumentType <> _
DocumentTypeEnum.kPartDocumentObject Then
MsgBox("Open or Activate Part Document")
Exit Sub
End If
Dim oDoc As PartDocument = _oApp.ActiveDocument
Dim oDef As PartComponentDefinition = oDoc.ComponentDefinition
Dim oRefKeyMgr As ReferenceKeyManager = oDoc.ReferenceKeyManager
' The keys will be written to binary file for later access
Dim binWriter As New BinaryWriter( _
System.IO.File.Open(_Filename, FileMode.Create))
Try
Dim lContext As Long
Dim byContext() As Byte = New Byte() {} ' initilization
' The conext should be created only once
lContext = oRefKeyMgr.CreateKeyContext
' Dump the number of faces in this model
binWriter.Write(oDef.SurfaceBodies(1).Faces.Count())
' Iterate through all faces and dump key
Dim oFace As Face
For Each oFace In oDef.SurfaceBodies(1).Faces
' get key from oFace
Dim byKey() As Byte = New Byte() {}
oFace.GetReferenceKey(byKey, lContext)
' save the key size and key in the file
binWriter.Write(byKey.Length)
binWriter.Write(byKey)
Next
' We have finished creating all the keys,
' now save the long context as byte array
' Context should be saved after creating all the keys
oRefKeyMgr.SaveContextToArray(lContext, byContext)
' Dump the size and context array into file
binWriter.Write(byContext.Length)
binWriter.Write(byContext)
Catch ex As SystemException
MsgBox(ex.ToString())
Finally
binWriter.Close()
MsgBox("Reference Keys are Saved in the file:" & vbNewLine & _Filename)
End Try
oRefKeyMgr = Nothing
oDoc = Nothing
End Sub 'GetKeys
Sub ReadFaceKeys()
If _oApp.Documents.Count = 0 Then
MsgBox("Open Part Document")
Exit Sub
End If
If _oApp.ActiveDocument.DocumentType <> _
DocumentTypeEnum.kPartDocumentObject Then
MsgBox("Open or Activate Part Document")
Exit Sub
End If
Dim oDoc As PartDocument = _oApp.ActiveDocument
Dim oDef As PartComponentDefinition = oDoc.ComponentDefinition
Dim oRefKeyMgr As ReferenceKeyManager = oDoc.ReferenceKeyManager
Dim lContext As Long
Dim byContext() As Byte = New Byte() {} ' initilization
' Open the File to read the Face keys
Dim binReader As New BinaryReader( _
System.IO.File.Open(_Filename, FileMode.Open))
' Reset the position in the stream to zero.
binReader.BaseStream.Seek(0, SeekOrigin.Begin)
Dim nFaces As Long
' Create collection object to hold all keys
Dim oKeysColl As New Collection
Try
' Read the number of faces
nFaces = binReader.ReadInt32
'Read All keys
Dim nSize As Int32
For ii As Int16 = 1 To nFaces
Dim byKey() As Byte = New Byte() {}
' Read the key Size
nSize = binReader.ReadInt32
' Read the key
byKey = binReader.ReadBytes(nSize)
oKeysColl.Add(byKey)
Next
' Read the size of context Array
nSize = binReader.ReadInt32()
byContext = binReader.ReadBytes(nSize)
Finally
binReader.Close()
End Try
' Now create the long context from context array
lContext = oRefKeyMgr.LoadContextFromArray(byContext)
'visualize faces associated with reference keys
Dim oHSet As HighlightSet
oHSet = oDoc.HighlightSets.Add
oHSet.SetColor(0, 255, 0)
Dim FoundFaces As Integer = 0
For i As Integer = 1 To nFaces
Dim oFace As Face = Nothing
Try
Dim oKey() As Byte = CType(oKeysColl.Item(i), Byte())
oFace = oRefKeyMgr.BindKeyToObject(oKey, lContext)
oHSet.AddItem(oFace)
FoundFaces += 1
Catch
MsgBox("Unable to to Bind Key to Object")
Exit Sub
End Try
Next
If FoundFaces = nFaces Then
MsgBox("All faces bound back successfully")
oHSet.Clear()
Else
MsgBox("Couldn't find " & nFaces - FoundFaces & " faces")
End If
End Sub 'ReadFaceKeys
End Class 'Form1
C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Inventor;
using System.Runtime.InteropServices;
using System.IO;
namespace RefKeys_CSharp_2
{
public partial class Form1 : Form
{
// Reference to Inventor application
Inventor.Application oApp = null;
private string _Title = "Reference Keys Demo";
private string _Filename = @"c:\temp\keydata.txt";
public Form1()
{
InitializeComponent();
try
{
oApp = (Inventor.Application)Marshal
.GetActiveObject("Inventor.Application");
}
catch
{
MessageBox.Show("Can't get Inventor session.",
"Reference Keys Demo");
}
}
private void btn_GetKeys_Click(object sender, EventArgs e)
{
SaveFaceKeys();
}
private void btn_FindFaces_Click(object sender, EventArgs e)
{
FindFacesByKeys();
}
/////////////////////////////////////////////////////////////
// Extract reference keys from faces and save them on disk
/////////////////////////////////////////////////////////////
void SaveFaceKeys()
{
// verify if part document is active
if (oApp.Documents.Count == 0)
{
MessageBox.Show("Open Part Document", _Title);
}
if (oApp.ActiveDocument.DocumentType !=
DocumentTypeEnum.kPartDocumentObject)
{
MessageBox.Show("Open Part Document", _Title);
}
PartDocument oDoc = (PartDocument)oApp.ActiveDocument;
PartComponentDefinition oDef = oDoc.ComponentDefinition;
ReferenceKeyManager oRefKeyMgr = oDoc.ReferenceKeyManager;
// The keys will be written to binary file for later access
BinaryWriter binWriter = new BinaryWriter(
System.IO.File.Open(_Filename, FileMode.Create));
try
{
// The conext should be created only once
int lContext = oRefKeyMgr.CreateKeyContext();
byte[] byContext = new byte[] {}; // initilization
// Dump the number of faces in this model
binWriter.Write(oDef.SurfaceBodies[1].Faces.Count);
// Iterate through all faces and dump key
foreach (Face oFace in oDef.SurfaceBodies[1].Faces)
{
// get key from oFace
byte[] byKey = new byte[] { };
oFace.GetReferenceKey(ref byKey, lContext);
// Dump the key size and key in file
binWriter.Write(byKey.Length);
binWriter.Write(byKey);
}
// We have finished creating all the keys,
// now save the long context as byte array
// Context should be saved after creating all the keys
oRefKeyMgr.SaveContextToArray(lContext, ref byContext);
// Dump the size and context array into file
binWriter.Write(byContext.Length);
binWriter.Write(byContext);
}
catch (Exception ex)
{
// show error message
MessageBox.Show(ex.ToString(), _Title);
}
finally
{
binWriter.Close();
MessageBox.Show("Reference Keys are Saved in the file: \n"
+ _Filename, _Title);
}
oRefKeyMgr = null;
oDoc = null;
} //SaveFaceKeys
/////////////////////////////////////////////////////////////
// Read reference keys from file and find associated faces
/////////////////////////////////////////////////////////////
private void FindFacesByKeys()
{
// verify if part document is active
if (oApp.Documents.Count == 0)
{
MessageBox.Show("Open Part Document", _Title);
}
if (oApp.ActiveDocument.DocumentType !=
DocumentTypeEnum.kPartDocumentObject)
{
MessageBox.Show("Open Part Document", _Title);
}
PartDocument oDoc = (PartDocument)oApp.ActiveDocument;
PartComponentDefinition oDef = oDoc.ComponentDefinition;
ReferenceKeyManager oRefKeyMgr = oDoc.ReferenceKeyManager;
int lContext;
byte[] byContext = new byte[] { }; // initilization
// Open the File to read the Face keys
BinaryReader binReader = new BinaryReader(
System.IO.File.Open(_Filename, FileMode.Open));
// Reset the position in the stream to zero.
binReader.BaseStream.Seek(0, SeekOrigin.Begin);
int nFaces = 0;
// Create collection object to hold all keys
var oKeysColl = new List<byte[]> { };
try
{
// Read the number of faces
nFaces = binReader.ReadInt32();
// Read All keys
int nSize;
for (int i = 1; i <= nFaces; i++)
{
byte[] byKey = new byte[] { };
// Read the key Size
nSize = binReader.ReadInt32();
// Read the key
byKey = binReader.ReadBytes(nSize);
oKeysColl.Add(byKey);
}
// Read the size of context array
nSize = binReader.ReadInt32();
// Read the context array
byContext = binReader.ReadBytes(nSize);
}
finally
{
binReader.Close();
}
// Now create the long context from context array
lContext = oRefKeyMgr.LoadContextFromArray(byContext);
// visualize faces
HighlightSet oHSet = oDoc.HighlightSets.Add();
oHSet.SetColor(0, 255, 0);
for (int i = 1; i <= nFaces; i++)
{
Face oFace = null;
try
{
byte[] oRefKey = (byte[])oKeysColl[i-1];
object MatchType = null;
oFace = oDoc.ReferenceKeyManager.BindKeyToObject(
ref oRefKey, lContext, out MatchType) as Face;
oHSet.AddItem(oFace);
}
catch
{
MessageBox.Show("Unable to to Bind Key to Object", _Title);
return;
}
}
MessageBox.Show("All faces found back successfully", _Title);
oHSet.Clear();
oRefKeyMgr = null;
oDoc = null;
} //FindFacesByKeys
} //Form1
}