简体   繁体   中英

Inventor COM object not released when created with Activator.CreateInstance()

My problem is this: If Autodesk Inventor is not running, my app (console app) creates a new instance with Activator.CreateInstance(InventorType); and uses it as a COM object. When my app does not quit Inventor but leaves it open and the user later quits it by hand there is still a process inventor.exe running in TaskManager which can only be killed in TaskManager. Curiously the problem only arises when these two things are combined. Whenever my app quits Inventor with InventorApp.Quit(); it is closed properly and there is no process left open.

If my app starts Inventor with Process.Start(..); or the user starts Inventor before starting the app and then my app grabs Inventor with Marshal.GetActiveObject(ProgId); there is no problem no matter if the app or the user quits Inventor.

If my app starts Inventor with Activator.CreateInstance(InventorType); then leaves Inventor open, the app is closed and then restarted, it grabs Inventor with Marshal.GetActiveObject(..); and then quits Inventor via InventorApp.Quit(); there is no problem.

So, the problem with the left open process only arises in this specific combination:

  1. start Inventor via Activator.CreateInstance(InventorType);
  2. the user quits Inventor by hand

The left open process is not in the Running Object Table anymore so it can't be handled as a COM object anymore and it has no visible UI, which means it can only be killed in TaskManager.

Using the 'bad combination' as described I even tried to call GC.WaitForPendingFinalizers(); GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); several times (I know this is bad but I am just trying everything) in different combinations and before and/or after Marshal.ReleaseComObject(invApp); Marshal.FinalReleaseComObject(invApp); Marshal.ReleaseComObject(invApp); Marshal.FinalReleaseComObject(invApp); . I even tried a minimal app which literally does nothing else. See below for the code.

So, what is Activator.CreateInstance(InventorType); doing that is causing this? Is there any way to prevent this? Or is this a problem specific to Inventor?

Minimal app example:

        Inventor.Application invApp = null;
        string ProgId = "Inventor.Application";
        try
        {
            invApp = (Inventor.Application)Marshal.GetActiveObject(ProgId);
        }
        catch (Exception e1)
        {
            try
            {

                Type InventorType = Type.GetTypeFromProgID(ProgId);
                invApp = (Inventor.Application)Activator.CreateInstance(InventorType);
            }
            catch (Exception e2)
            {
                Console.WriteLine(e1);
                Console.WriteLine(e2);
            }
        }

        invApp = invApp as Inventor.Application;
        invApp.Visible = true;

        Console.Write("Quit Inventor? (y/n) ");
        string quit = Console.ReadLine();
        if (quit == "y")
        {
            invApp.Quit();
        }

        // desperately trying to release the COM object ...
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        if (invApp != null)
        {
            Marshal.ReleaseComObject(invApp);
            Marshal.FinalReleaseComObject(invApp);
        }
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        if (invApp != null)
        {
            Marshal.ReleaseComObject(invApp);
            Marshal.FinalReleaseComObject(invApp);
        }
        GC.WaitForPendingFinalizers();
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();

        invApp = null;

This is not specific for Inventor but for any COM object (Excel for example).

Usually I don't use this COM communication for production, because there are many vulnerabilities and some performance issues. I recommend you to use another workflow when possible. But to your question. You can't release this COM object as you try. I recommend you to wrap Inventor.Application to some IDisposable object and quit them in dispose method, when you create your own instance.

static void Main(string[] args)
{
    InventorTest();

    //Waiting for dispose message
    //Console.ReadKey();
}

private static void InventorTest()
{
    using (var invProvider = new InventorDisposableProvider())
    {

        var invApp = invProvider.InventorApp;

        invApp.Visible = true;

        Console.Write("Quit Inventor? (y/n) ");
        string quit = Console.ReadLine();
        if (quit == "y")
        {
            invApp.Quit();
        }

    }
}

class InventorDisposableProvider : IDisposable
{
    private Application invApp;
    private bool startedByMe = false;

    /// <summary>
    /// Gets running or start new instance of Inventor
    /// </summary>
    public Application InventorApp
    {
        get
        {
            if (invApp == null) GetInventorApp();

            return invApp;
        }
    }

    /// <summary>
    /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
    /// </summary>
    public void Dispose()
    {
        if (startedByMe && invApp != null)
        {
            invApp .Quit();
            Console.WriteLine("Quit");
        }
    }

    private void GetInventorApp()
    {
        string ProgId = "Inventor.Application";
        try
        {
            invApp = (Inventor.Application)Marshal.GetActiveObject(ProgId);
            startedByMe = false;
        }
        catch (Exception e1)
        {
            try
            {
                Type InventorType = Type.GetTypeFromProgID(ProgId);
                invApp = (Inventor.Application)Activator.CreateInstance(InventorType);
                startedByMe = true;
            }
            catch (Exception e2)
            {
                Console.WriteLine(e1);
                Console.WriteLine(e2);
            }
        }
    }
}

I don't know if this is the best solution but it is a good start point.

I found only one issue. When user quit the console application by cross. In this case you can see this article how to solve this case. capture-console-exit-c-sharp

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM