2

我有一个 .NET Compact Framework 应用程序,它可以在三台 Windows 机器(桌面窗口和两台 WinCE 机器)和 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 框架有一些奇怪的交互。

4

5 回答 5

3

看起来您的应用程序中仍在运行一些线程。

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

于 2009-11-02T11:43:29.913 回答
1

Your COM object is creating a thread in the background and that thread is not terminating. It's likely that this is because the COM object is not getting released in your code.

Try calling Marshal.ReleaseComObject before exiting, so your simple test app would look like this:

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);
} 
于 2010-06-07T16:56:12.483 回答
1

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

有关详细信息,请参阅此帖子

于 2009-10-29T21:17:48.913 回答
1

只是预感 - 确保CoUninitialize()在退出之前被调用?此外,不要闯入调试器,而是创建故障转储并对其进行调试。不确定这在 CE 上是如何工作的,但这就是我在 Windows 上所推荐的。

于 2009-10-29T16:47:06.490 回答
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 对象是自动销毁的,而不是 by Marshal.ReleaseComObject(),所以在终结器线程中被销毁,DestroyWindow在 WinCE 上无法从终结器线程调用。像往常一样,Microsoft 没有在其文档中说明危险,仅说明“不要在一个线程中使用 DestroyWindow 来破坏由不同线程创建的窗口”。

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

有趣的事实:DestroyWindow如果我打电话SetTimer(NULL,...)而不是,则不会SetTimer(gTimerWindow,...)出现问题,但如果我根本不打电话,就会出现问题。SetTimer

于 2010-06-07T21:12:10.383 回答