c#启动的应用程序在主线程关闭之前一直作为运行进程运行

本文关键字:运行 一直 进程 启动 应用程序 线程 | 更新日期: 2023-09-27 18:16:19

我的目标是使用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只终止时,主控制台应用程序终止,而不是当它达到一个单独的线程结束?

c#启动的应用程序在主线程关闭之前一直作为运行进程运行

即使在调用mfl.Quit()之后,当有突出的COM引用它时,第三方程序可能仍然存活。这有点奇怪,因为"Quit()"方法的正常语义是强制进程退出(有序)并使任何COM引用无效。

根据设计,当持有引用的变量(好吧,持有RCW或持有应用程序的COM引用的运行时可调用包装器对象的引用的变量)超出作用域时,. net不会在COM对象上调用Release(),因为。net是垃圾收集的。

Release()将只在COM对象上被调用,当你的进程(技术上是AppDomain)结束时,或者当垃圾收集器决定收集你的RCW对象时,这在一个短暂的控制台应用程序上将是'never'。

我本来期望RCW对象能够Dispose(),但它们不是。也许是技术上的原因,也许在。net 1.0的开发过程中没有引入dispose模式来改变RCW的行为已经太晚了。

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

  Marshal.ReleaseComObject(mfl);

在调用Quit()方法之后尝试一下,看看它是否改变了第三方程序的行为。

我认为有些东西没有在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);
  });