为什么我的程序不会终止?

时间:2009-10-29 16:43:51

标签: .net compact-framework process windows-ce termination

我有一个可以在三台Windows机器(桌面窗口和两台WinCE机器)上运行的.NET Compact Framework应用程序,在WinCE设备上,即使我调用Application.Exit(),该进程也不会在退出时终止。除了.NET之外,它还使用一个COM组件(它在UI线程上完成所有操作)。如果我在退出后进入调试器,Visual Studio只显示一个线程和一个完全空白的调用堆栈。

什么可能导致这种情况?

更新:我的进程终止于桌面而不是WinCE计算机。我尝试使用以下代码强制进程终止,但它不起作用:

[DllImport("coredll.dll")]
static extern int TerminateProcess(IntPtr hProcess, uint uExitCode);

static public void ExitProcess()
{
    if (Platform.IsWindowsCE)
        TerminateProcess(new IntPtr(-1), 0);
    Application.Exit();
}

还应该有如下的ExitProcess()和GetCurrentProcess()API,但如果我尝试调用它们,我会得到EntryPointNotFoundException。因此我使用TerminateProcess(-1,0),因为桌面版GetCurrentProcess的文档声称它只返回-1。

[DllImport("coredll.dll")]
static extern int ExitProcess(IntPtr hProcess);
[DllImport("coredll.dll")]
static extern IntPtr GetCurrentProcess();

即使抛出未处理的异常也不会这样做。

更新2:导致问题的最简单程序只是创建COM对象。

static void Main()
{
    new FastNavLib.MapControl();
}

使用COM组件的C ++程序不会出现这种行为,因此我的C ++ COM组件必须与.NET框架进行一些奇怪的交互,我将对此进行调查。

5 个答案:

答案 0 :(得分:3)

看起来你的应用程序中仍然有一些线程正在运行。

确保在退出主要线程之前终止每个子线程。

答案 1 :(得分:1)

只是预感 - 确保在退出之前调用CoUninitialize()? 此外,而不是闯入调试器创建崩溃转储和调试。不确定这在CE上是如何工作的,但这就是我在Windows上推荐的内容。

答案 2 :(得分:1)

如果应用程序没有退出,通常意味着仍然有一个句柄仍然打开,无法从用户空间关闭。这意味着有一个错误的驱动程序。

有关详细信息,请参阅this post

答案 3 :(得分:1)

您的COM对象正在后台创建一个线程,该线程没有终止。这很可能是因为COM代码没有在代码中发布。

在退出之前尝试调用Marshal.ReleaseComObject,因此您的简单测试应用程序将如下所示:

static void Main() 
{ 
    // create the COM object
    var obj = new FastNavLib.MapControl(); 

    // simulate doing stuff
    Thread.Sleep(1000);

    // release the COM object
    Marshal.ReleaseComObject(obj);
} 

答案 4 :(得分:0)

我有点想通了。

我的COM对象将自己设置为通过隐藏窗口获取WM_TIMER条消息。基本上是:

// Register window class
WNDCLASS wc;
memset(&wc, 0, sizeof(wc));
wc.lpfnWndProc = &WinCeTimerProc;
wc.lpszClassName = _T("FastNavTimerDummyWindow");
// Create window
gTimerWindow = CreateWindow(wc.lpszClassName, wc.lpszClassName, 
    WS_OVERLAPPED, 0, 0, 100, 100, NULL, NULL, GetModuleHandle(NULL), NULL);
gTimerID = SetTimer(gTimerWindow, 88, gTimerIntervalMs, NULL);

(专家可能会指出我不需要定时器窗口来接收定时器消息 - SetTimer的最后一个参数可以设置为回调函数。实际上,如果我使用回调而不是定时器窗口,问题消失!但是,我不得不使用计时器窗口来解决WinCE中的一个奇怪的错误,其中SetTimer(NULL,...)可能会导致WM_TIMER消息被调用PeekMessage()的人接收。 )

现在,当销毁使用计时器的最后一个COM对象时,计时器和计时器窗口也会被销毁:

KillTimer(gTimerWindow, gTimerID);
DestroyWindow(gTimerWindow);

不幸的是,DestroyWindow()永远不会返回。我假设在DestroyWindow中存在某种死锁,但是当我在Visual Studio中暂停时,为什么调用堆栈为空是不明白的。也许是因为COM对象被自动销毁而不是被Marshal.ReleaseComObject()销毁,它在终结器线程中被销毁,并且无法从WinCE上的终结器线程调用DestroyWindow。像往常一样,微软没有说明其文档中的危险,只说明“不要在一个线程中使用DestroyWindow来销毁由不同线程创建的窗口。”

解决方案很简单:根本不要破坏计时器窗口,因为操作系统会在进程退出时自动销毁它。

有趣的事实:如果我拨打DestroyWindow而不是SetTimer(NULL,...),则不会出现SetTimer(gTimerWindow,...)的问题,但如果我不打电话,会发生完全SetTimer