Access a specific Inventor.Application object

By Adam Nagy

When running multiple instances of Inventor (or AutoCAD, Microsoft Excel, etc) then even though the ROT (Running Objects Table) lists all the running instances, they will all return the same Inventor.Application object that is returned by GetActiveObject as well – i.e. the first Inventor instance. See comments of this blog post: 
http://adndevblog.typepad.com/autocad/2013/12/accessing-com-applications-from-the-running-object-table.html

The ROT also returns the currently open documents (Inventor.Document object) and those have the correct COM reference. So from those you can find the various Inventor.Application objects.  Of course this only works for the Inventor instances which have documents open in them – and those documents need to be unique. E.g. if in Inventor instance #2 I open "mypart.ipt" then I also open the same file in Inventor instance #3 then that document will only have a single reference in the ROT and that will reference Inventor instance #2. I won't be able to access Inventor instance #3.

If you are trying to access the Inventor instance that you create through CreateProcess then one workaround is to create a copy of a template document with a unique name and open that in Inventor.

using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.InteropServices.ComTypes;  namespace InventorConsoleAppCSharp {   class Program   {      [DllImport("ole32.dll")]     static extern int CreateBindCtx(         uint reserved,         out IBindCtx ppbc);      [DllImport("ole32.dll")]     public static extern void GetRunningObjectTable(         int reserved,         out IRunningObjectTable prot);      // Requires Using System.Runtime.InteropServices.ComTypes     // Get all running instance by querying ROT     private static object GetInventorApplicationWithWindowHandle(IntPtr hwnd)     {       // get Running Object Table ...       IRunningObjectTable Rot = null;       GetRunningObjectTable(0, out Rot);       if (Rot == null)         return null;        // get enumerator for ROT entries       IEnumMoniker monikerEnumerator = null;       Rot.EnumRunning(out monikerEnumerator);        if (monikerEnumerator == null)         return null;        monikerEnumerator.Reset();        IntPtr pNumFetched = new IntPtr();       IMoniker[] monikers = new IMoniker[1];        // go through all entries and identifies app instances       while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0)       {         IBindCtx bindCtx;         CreateBindCtx(0, out bindCtx);         if (bindCtx == null)           continue;          string displayName;         monikers[0].GetDisplayName(bindCtx, null, out displayName);         System.Console.WriteLine(displayName);          object ComObject;         Rot.GetObject(monikers[0], out ComObject);          if (ComObject == null)           continue;          try         {           dynamic invDoc = ComObject;           if (new IntPtr(invDoc.Parent.MainFrameHWND) == hwnd)           {             // The parent of the document is the Application             return invDoc.Parent;           }         }         catch { }       }        return null;     }      static void Main(string[] args)     {       // Create document with unique name      
 string fileName = "C:\temp\" + Guid.NewGuid().ToString() + ".ipt";       System.IO.File.Copy(         "C:\temp\Gusset2013.ipt",          fileName);        // Start Inventor       Process process = Process.Start(         @"C:Program FilesAutodeskInventor 2016BinInventor.exe",          """ + fileName + """);        // Wait until Inventor is fully ready       process.WaitForInputIdle();       while (process.MainWindowTitle == "")       {         process.Refresh();         System.Threading.Thread.Sleep(1000);       }        // Get all the Inventor.Application objects       // registered in the ROT       // ROT = Running Objects Table       Inventor.Application app = null;       for (; app == null; System.Threading.Thread.Sleep(1000))       {         // Find the Inventor.Application object with          // the correct window handle         app = GetInventorApplicationWithWindowHandle(process.MainWindowHandle)            as Inventor.Application;       }        Console.WriteLine("Found Inventor.Application object with title:n" + app.Caption);        // Let's close file and delete it       app.Documents.get_ItemByName(fileName).Close();       System.IO.File.Delete(fileName);        // Wait for user to finish       Console.WriteLine("Press key to exit");       Console.ReadKey();     }   } }

 


Comments

9 responses to “Access a specific Inventor.Application object”

  1. Jamie Johnson Avatar
    Jamie Johnson

    I’m having an issue with the ‘dynamic’ in line
    dynamic invDoc = ComObject;
    VisualStudio is reporting it as system.dynamic which is a namespace not an object type.

  2. Jamie Johnson Avatar
    Jamie Johnson

    Turns out you were closer to the complete solution than you thought, check this bit of VB code out, that I wrote while analyzing your code:
    Private Function GetInventorApplicationWithWindowHandle(hwnd As IntPtr) As Object
    Dim Rot As IRunningObjectTable = Nothing
    GetRunningObjectTable(0, Rot)
    If Rot IsNot Nothing Then
    Dim monikerEnumerator As IEnumMoniker = Nothing
    Rot.EnumRunning(monikerEnumerator)
    If monikerEnumerator IsNot Nothing Then
    monikerEnumerator.Reset()
    Dim pNumFetched As New IntPtr()
    Dim monikers As IMoniker() = New IMoniker(0) {}
    While monikerEnumerator.Next(1, monikers, pNumFetched) = 0
    Dim bindCtx As IBindCtx = Nothing
    CreateBindCtx(0, bindCtx)
    If bindCtx IsNot Nothing Then
    Dim displayName As String = String.Empty
    monikers(0).GetDisplayName(bindCtx, Nothing, displayName)
    ‘Dim guid As New Guid(“{B6B5DC40-96E3-11d2-B774-0060B0F159EF}”)
    If displayName = “!{B6B5DC40-96E3-11D2-B774-0060B0F159EF}” Then
    Dim ComObject As Object = Nothing
    Rot.GetObject(monikers(0), ComObject)
    If ComObject IsNot Nothing Then
    Try
    Dim invApplication As Inventor.Application = TryCast(ComObject, Inventor.Application)
    If invApplication IsNot Nothing Then Return invApplication
    Catch ex As Exception
    Debug.Print(ex.ToString)
    End Try
    End If
    End If
    End If
    End While
    End If
    End If
    Return Nothing
    End Function
    I found that your line for write line display name, was writing the exact com id of Inventor’s application object. So I grabbed exactly that, and it worked. I truly thank you for this blog, its changed my game plan significantly. I will be posting a completed tool in the forums. Thanks.

  3. Glad you like it Jamie! :)
    Yes, you can get back the Application object as well, but as I mentioned, they will return the same Inventor.Application object. You can easily test it by opening a document in the first instance, then open additional Inventor’s and check the Application.Caption for each of the returned objects: they will all contain the opened document’s name in the title because they all give back the first Inventor instance. :-s

  4. Jamie Johnson Avatar
    Jamie Johnson

    Ok so this is not actually doing what I thought. The code above returns 1 app for each reported instance, but each app returned is the same first thread as is verified by the open documents within each app. The problem is none of the items in the ROT are the actual documents either, just apps. But if it shows 2 apps, then I should be able to get each app’s individual thread, somehow. Not giving up on this.

  5. As I also mentioned this is not specific to Inventor, but all COM registered applications. See comment here:
    https://social.technet.microsoft.com/Forums/office/en-US/2179c726-f992-4f29-9e5d-3db8adde98b0/multiple-instances-of-access-2010-running-have-entrees-in-running-object-table-rot-but-all-entrees?forum=officeitproprevious

    >
    Theoretically, you can iterate the ROT for each individual instance, but the Office apps don’t register themselves if another instance is already in the ROT because the moniker for itself is always the same (it couldn’t be distinguished anyway). This means that you can’t attach to any instance except for the first. However, because the Office apps also register their documents in the ROT, you can successfully attach to other instances by iterating the ROT looking for a specific document, attaching to it, then getting the Application object from it.
    <<<<<

  6. Jamie Johnson Avatar
    Jamie Johnson

    The running object table moniker enumerator never passed by a single document object, because… Inventor documents that have not been saved to the file are not listed in the ROT. So unless the document open has been saved to file, this will not work. FYI if you find an Inventor in the ROT that does not have a file based document open (or no documents open) you will see it in the list by its clsID = {B6B5DC40-96E3-11d2-B774-0060B0F159EF}, and it is repeated if you have many instances open. However turning that into a com object is difficult, because they all have the same name and most developers handle the ROT as a hashtable (key, value list of unique items), the multiple instances get mashed into 1 thanks to that hashtable.

  7. Is it the same with e.g. Excel and its documents?
    So maybe the only way to do it properly is if you create an auto-load Inventor addin that exposes all the Inventor objects?

  8. Jamie Johnson Avatar
    Jamie Johnson

    Interesting proposition, how to accomplish would be the question. Would one, force updates to the ROT to exist with different names, or create a separate class apartment that can listen to start up events from itself collecting all the invApps created/destroyed, or some other idea? This does explain a lot of the ‘issues’ with Autodesk add-on apps (such as vault and task manager) that complain about having multiple Inventor’s open.

  9. I guess you could create your own COM server (which would only have a single instance) and that server instance could be accessed from each Inventor application, from inside your addin. When your addin loads it has access to the Application object of the Inventor instance it is running inside in, so it could register Inventor with that COM server. When Inventor is closed down and so the add-in gets unloaded it could unregister that COM instance with the COM server.

Leave a Reply

Discover more from Autodesk Developer Blog

Subscribe now to keep reading and get access to the full archive.

Continue reading