0

我的问题是:如果 Autodesk Inventor 没有运行,我的应用程序(控制台应用程序)会创建一个新实例Activator.CreateInstance(InventorType);并将其用作 COM 对象。当我的应用程序没有退出 Inventor 而是让它保持打开状态并且用户稍后手动退出它时,TaskManager 中仍然有一个进程inventor.exe正在运行,它只能在 TaskManager 中被杀死。奇怪的是,只有将这两件事结合起来时,才会出现问题。每当我的应用程序退出 Inventor 并InventorApp.Quit();正确关闭并且没有任何进程处于打开状态时。

如果我的应用程序使用 Inventor 启动Process.Start(..);或用户在启动应用程序之前启动 Inventor,然后我的应用程序使用 Inventor 抓取,Marshal.GetActiveObject(ProgId);则无论应用程序或用户退出 Inventor 都没有问题。

如果我的应用程序使用 Inventor 启动Activator.CreateInstance(InventorType);然后保持 Inventor 打开,应用程序关闭然后重新启动,它使用 Inventor 抓取Marshal.GetActiveObject(..);然后退出 Inventor,InventorApp.Quit();没有问题。

所以,左开进程的问题只出现在这个特定的组合中:

  1. 通过以下方式启动 InventorActivator.CreateInstance(InventorType);
  2. 用户手动退出 Inventor

左打开进程不再在运行对象表中,因此它不能再作为 COM 对象处理,并且它没有可见的 UI,这意味着它只能在 TaskManager 中被杀死。

使用所描述的“错误组合”,我什至尝试以GC.WaitForPendingFinalizers(); GC.Collect();不同的组合以及 before 和/或 after 多次调用(我知道这很糟糕,但我只是在尝试一切)Marshal.ReleaseComObject(invApp); Marshal.FinalReleaseComObject(invApp);。我什至尝试了一个最小的应用程序,它实际上什么都不做。请参阅下面的代码。

那么,是Activator.CreateInstance(InventorType);什么导致了这种情况?有什么办法可以防止这种情况发生吗?或者这是 Inventor 特有的问题?

最小的应用程序示例:

        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;
4

1 回答 1

1

这不是特定于 Inventor,而是针对任何 COM 对象(例如 Excel)。

通常我不会将这种COM通信用于生产,因为存在很多漏洞和一些性能问题。我建议您尽可能使用其他工作流程。但是对于你的问题。尝试时无法释放此 COM 对象。我建议您在创建自己的实例时包装Inventor.Application到某个对象并在 dispose 方法中退出它们。IDisposable

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);
            }
        }
    }
}

我不知道这是否是最好的解决方案,但这是一个很好的起点。

我只发现了一个问题。当用户通过交叉退出控制台应用程序时。在这种情况下,您可以查看这篇文章如何解决这种情况。 捕获-控制台-退出-c-sharp

于 2021-10-11T15:39:00.483 回答