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

发布于 2024-08-09 03:51:34 字数 1139 浏览 6 评论 0原文

我有一个 .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 框架有一些奇怪的交互,我将对此进行研究。

I have a .NET Compact Framework app that can runs on three windows machines (Desktop windows and two WinCE machines) and on the WinCE devices, the process never terminates on exit, even if I call Application.Exit(). Besides .NET, it uses one COM component (which does everything on the UI thread). If I break into the debugger after exitting, Visual Studio shows only one thread and a completely blank call stack.

What could possibly cause this?

Update: My process is terminating on the desktop but not the WinCE machines. I tried to force the process to terminate with the following code, but it doesn't work:

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

There are also supposed to be ExitProcess() and GetCurrentProcess() APIs like the following, but if I try to call them, I get EntryPointNotFoundException. Therefore I am using TerminateProcess(-1, 0) because the documentation for the desktop version of GetCurrentProcess claims that it simply returns -1.

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

Even throwing an unhandled exception won't do it.

Update 2: the simplest program that causes the problem merely creates the COM object.

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

C++ programs that use the COM component do not exhibit this behavior, so my C++ COM component must have some bizarre interaction with the .NET framework which I will investigate.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

远山浅 2024-08-16 03:51:34

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

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

look like you have some threads still running in your application.

Make sure you have terminate every child thread before exiting the main one.

箹锭⒈辈孓 2024-08-16 03:51:34

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

Just a hunch - make sure CoUninitialize() is called before exit?
Also, instead of breaking into a debugger create a crash dump and debug that. Not sure how this works on CE, but that's what'd I'd recommend on Windows.

黒涩兲箜 2024-08-16 03:51:34

如果应用程序没有退出,通常意味着有一个句柄仍然打开,无法从用户空间关闭。这意味着有一个越野车司机。

有关详细信息,请参阅这篇文章

If an app doesn't exit that usually means that there's one handle still open which can't be closed from userspace. And that means there's a buggy driver.

See this post about details.

梦纸 2024-08-16 03:51:34

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

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);
} 
你如我软肋 2024-08-16 03:51:34

我大概明白了。

我的 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 code> 消息由调用 PeekMessage() 的人接收。)

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

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

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

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

有趣的事实:如果我调用 SetTimer(NULL,...) 而不是 SetTimer(gTimerWindow,...),则不会出现 DestroyWindow 的问题。 code>,但如果我根本不调用 SetTimer,就会发生这种情况。

I sort-of figured it out.

My COM object sets itself up to get WM_TIMER messages through a hidden window. Basically:

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

(Experts might point out I don't need a timer window to receive timer messages--the last parameter of SetTimer can be set to a callback function. Indeed, if I use a callback instead of a timer window, the problem disappears! However, I had to use a timer window to work around a strange bug in WinCE in which SetTimer(NULL,...) can cause WM_TIMER messages to received by somebody that calls PeekMessage().)

Now, when the last COM object that uses the timer is destroyed, the timer and timer window are also destroyed:

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

Unfortunately, DestroyWindow() never returns. I assume there is some kind of deadlock in DestroyWindow, though it is not clear why the call stack is blank when I pause in Visual Studio. Perhaps because the COM object is destroyed automatically instead of by Marshal.ReleaseComObject(), it is destroyed in the finalizer thread, and DestroyWindow cannot be called from the finalizer thread on WinCE. As usual Microsoft doesn't spell out the danger in its documentation, stating only "Do not use DestroyWindow in one thread to destroy a window created by a different thread."

The solution was simple: don't destroy the timer window at all, as the OS destroys it automatically when the process exits.

Fun fact: the problem with DestroyWindow does not occur if I call SetTimer(NULL,...) instead of SetTimer(gTimerWindow,...), but it does occur if I don't call SetTimer at all.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文