简体   繁体   English

Inventor COM object 使用 Activator.CreateInstance() 创建时未发布

[英]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);我的问题是:如果 Autodesk Inventor 没有运行,我的应用程序(控制台应用程序)使用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.并将其用作 COM object。当我的应用程序没有退出 Inventor 而是保持打开状态并且用户稍后手动退出时,仍然有一个进程inventor.exe在 TaskManager 中运行,它只能在 TaskManager 中被杀死。 Curiously the problem only arises when these two things are combined.奇怪的是,只有当这两件事结合在一起时,问题才会出现。 Whenever my app quits Inventor with InventorApp.Quit();每当我的应用程序使用InventorApp.Quit(); it is closed properly and there is no process left open.它已正确关闭,并且没有任何进程处于打开状态。

If my app starts Inventor with Process.Start(..);如果我的应用程序使用Process.Start(..); or the user starts Inventor before starting the app and then my app grabs Inventor with Marshal.GetActiveObject(ProgId);或者用户在启动应用程序之前启动 Inventor,然后我的应用程序使用Marshal.GetActiveObject(ProgId); there is no problem no matter if the app or the user quits Inventor.无论应用程序还是用户退出 Inventor 都没有问题。

If my app starts Inventor with Activator.CreateInstance(InventorType);如果我的应用使用Activator.CreateInstance(InventorType); then leaves Inventor open, the app is closed and then restarted, it grabs Inventor with Marshal.GetActiveObject(..);然后让 Inventor 打开,应用程序关闭然后重新启动,它使用Marshal.GetActiveObject(..); and then quits Inventor via InventorApp.Quit();然后通过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);通过Activator.CreateInstance(InventorType);
  2. the user quits Inventor by hand用户手动退出 Inventor

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.左打开的进程不再在 Running Object Table 中,因此它不能再作为 COM object 处理,并且它没有可见的 UI,这意味着它只能在 TaskManager 中被杀死。

Using the 'bad combination' as described I even tried to call GC.WaitForPendingFinalizers(); GC.Collect();使用描述的“错误组合”我什至尝试调用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); 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);那么,什么是Activator.CreateInstance(InventorType); doing that is causing this?这样做会导致这个吗? Is there any way to prevent this?有什么办法可以防止这种情况发生吗? Or is this a problem specific to Inventor?或者这是 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).这不是特定于 Inventor,而是特定于任何 COM object(例如 Excel)。

Usually I don't use this COM communication for production, because there are many vulnerabilities and some performance issues.通常我不会将这个 COM 通信用于生产,因为存在很多漏洞和一些性能问题。 I recommend you to use another workflow when possible.我建议您尽可能使用其他工作流程。 But to your question.但是对于你的问题。 You can't release this COM object as you try.你不能随便释放这个COM object。 I recommend you to wrap Inventor.Application to some IDisposable object and quit them in dispose method, when you create your own instance.我建议您在创建自己的实例时将Inventor.Application包装到一些IDisposable object 并在 dispose 方法中退出它们。

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 捕获控制台退出-c-sharp

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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