由C#启动的应用程序保持为正在运行的进程,直到主线程关闭

时间:2013-05-31 11:29:45

标签: c# com process

我的目标是使用C#控制第三方应用程序。第三方应用程序可以由COM引用控制。

我在简单的控制台应用程序中添加了引用,我可以看到类和方法没问题。

以下行触发第三方应用程序启动。它做的。你可以在Taskmanager / Processes(mfl32.exe)中快乐地看到它:

MFL32.Application mfl = new MFL32.Application();

当我的控制台应用程序退出时,mfl32.exe仍然在进程列表中 - 这是预期的,因为我没有调用关闭它。然后我添加了这一行:

mfl.Quit();

现在,当我的控制台应用程序运行时,它会触发mfl32.exe启动,当我的控制台应用程序关闭时,它会终止mfl32.exe进程。

出现的问题是我的代码需要在单独的线程中调用此应用程序。 mfl32.exe进程不会在线程结束时终止,只会在我的控制台应用程序关闭时自行删除。当前代码如下所示:

namespace lt
{

class threadtest
{
    public void LaserTest()
    {

        Console.WriteLine("Worker thread started...");
        MFL32.Application mfl = new MFL32.Application();
        int i = 0;
        while (i < 50000)
        {
            i++;
        }
        mfl.Quit();
        Console.WriteLine("Worker thread now finished!");
    }

    void laser_AppQuit() // Quit event handler triggered
    {
        Console.WriteLine("The QUIT method has been caught. It should kill the lfm32.exe process");
    }
}



class Program
{
    static void Main(string[] args)
    {
        threadtest workerObject = new threadtest();
        Thread workerThread = new Thread(workerObject.LaserTest);
        workerThread.Start();
        Console.WriteLine("End of main thread reached");
        Console.ReadKey();
    }
}

}

为什么触发exe只会在主控制台应用程序终止时终止而不是到达单独线程结束时才会终止?

2 个答案:

答案 0 :(得分:1)

即使在调用mfl.Quit()之后,即使有未完成的COM引用,第三方程序也可能保持活动状态。这有点奇怪,因为“Quit()”方法的正常语义是强制进程退出(有序)并使任何COM引用无效。

按照设计,当拥有引用的变量时,.Net不会在COM对象上调用Release()(好的,该变量持有对包含应用程序的COM引用的RCW or Runtime-Callable Wrapper对象的引用)超出范围,因为.Net是垃圾收集。

只有当你的进程(技术上是AppDomain)结束,或者当垃圾收集器决定收集你的RCW对象时才会在COM对象上调用

Release(),这些对象在一个短暂的控制台应用程序上将是'永远不会”。

我原本预计RCW对象会Dispose()能够,但事实并非如此。也许有一个技术原因,也许在.Net 1.0的开发过程中为了改变RCW的行为为时已晚,才会引入配置模式。

在任何情况下,要强制RCW在其COM引用上调用Release(),请致电:

  Marshal.ReleaseComObject(mfl);

在调用Quit()方法后尝试检查它是否会改变第三方程序的行为。

答案 1 :(得分:0)

我会假设某些内容未在COM dll中正确发布。我会尝试在单独的应用程序域中运行LaserTest。在.NET中加载程序集后,除非应用程序域被销毁,否则它将保持加载状态。或者,您可以创建单独的“启动程序”程序集,在您的线程中运行启动程序程序集,并在完成COM内容后强制退出启动程序集程序。至于在单独的应用程序域中运行:

  var domain = AppDomain.CreateDomain("LaserDomain");
  domain.DoCallBack(() => {
      // dummy wait - run laser test here
      Thread.Sleep(5000);
      AppDomain.Unload(AppDomain.CurrentDomain);
  });