.net 为什么赢了';我的程序不会终止吗?
我有一个.NET Compact Framework应用程序,可以在三台windows机器(桌面windows和两台WinCE机器)上运行,在WinCE设备上,即使调用Application.exit(),进程也不会在退出时终止。除了.NET之外,它还使用一个COM组件(在UI线程上执行所有操作)。如果我在退出后进入调试器,VisualStudio只显示一个线程和一个完全空白的调用堆栈 这可能是什么原因造成的 更新:我的进程在桌面上终止,而不是在WinCE机器上终止。我尝试使用以下代码强制进程终止,但无效:.net 为什么赢了';我的程序不会终止吗?,.net,compact-framework,process,windows-ce,termination,.net,Compact Framework,Process,Windows Ce,Termination,我有一个.NET Compact Framework应用程序,可以在三台windows机器(桌面windows和两台WinCE机器)上运行,在WinCE设备上,即使调用Application.exit(),进程也不会在退出时终止。除了.NET之外,它还使用一个COM组件(在UI线程上执行所有操作)。如果我在退出后进入调试器,VisualStudio只显示一个线程和一个完全空白的调用堆栈 这可能是什么原因造成的 更新:我的进程在桌面上终止,而不是在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组件必须与我将调查的.NETFramework有一些奇怪的交互。 < P>只是一个预感-请确保在退出之前调用<代码> CudiialAlgIsId()//Cux>
另外,不要打断调试器,而是创建一个崩溃转储并调试它。不确定这在CE上是如何工作的,但这是我在Windows上的建议。如果应用程序没有退出,通常意味着有一个句柄仍然打开,无法从用户空间关闭。这意味着有一个马车司机
有关详细信息,请参阅。看起来应用程序中仍有一些线程在运行
在退出主线程之前,请确保已终止每个子线程。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);
}
我有点明白了 我的COM对象将自身设置为通过隐藏窗口获取
WM\u 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,…)
会导致调用peek message()
的人接收到WM\u TIMER
消息
现在,当使用计时器的最后一个COM对象被销毁时,计时器和计时器窗口也被销毁:
KillTimer(gTimerWindow, gTimerID);
DestroyWindow(gTimerWindow);
不幸的是,DestroyWindow()
从未返回。我假设销毁窗口
中存在某种死锁,但不清楚在Visual Studio中暂停时调用堆栈为何为空。可能是因为COM对象是自动销毁的,而不是由Marshal.ReleaseComObject()
,所以它在终结器线程中销毁,并且不能从WinCE上的终结器线程调用DestroyWindow
。像往常一样,微软没有在文档中详细说明这种危险,只是声明“不要在一个线程中使用DestroyWindow来破坏由另一个线程创建的窗口。”
解决方案很简单:根本不要破坏计时器窗口,因为当进程退出时,操作系统会自动破坏它
有趣的事实:如果我调用<代码> SETIMER(null,…)<代码>,而不是<代码> SETIMER(GTimeLeWindows,…)>代码>,则不会发生<代码>销毁窗口< /代码>,但是如果我不调用<代码> StimeT/<代码>,则<>强> >
。这可能是当我的C++应用程序不终止(经常发生恼人的事情)时要尝试的东西。但是.NET应用程序不应该调用它。如何进行崩溃转储并对其进行调试,为什么这比使用VS调试器更好?正如我提到的,我的是单线程应用程序。我写了应用程序中的所有代码,包括COM组件,所以我应该知道。由于进程在桌面上成功终止,WinXP和WinCE上的“进程终止规则”可能有些不同?不,没有不同的规则。COM组件正在创建一个线程,而该线程没有被终止。应用程序使用的唯一特殊设备是COM端口,即使我没有打开COM端口,进程也不会终止。哦,等等,我还使用waveOutOpen等播放声音。我想我可以将其作为一个原因。不,对waveOutOpen电话的评论没有帮助。根据他的帖子,这不太可能是司机的问题。这是一个线程问题,在CF中并不少见。我的COM对象不创建任何线程。我还使用ATL方法FinalRelease()末尾的OutputDebugString确认COM对象已正常释放。我现在了解到这个问题是由于创建了一个窗口来接收WM_定时器消息引起的,所以我将发布一个关于这个问题的答案。