升级到VS2010后不久,我的应用程序就不会干净地关闭。如果我关闭应用程序然后在IDE中点击暂停,我会看到:
问题是,没有背景。调用堆栈只是说[外部代码],这没什么用处。
这是我迄今为止试图缩小问题范围的工作:
虽然我可以执行下一个强力步骤,即将我的代码回滚到没有发生的点,然后查看所有更改日志,但这不是非常有效率。鉴于调试器提供的信息显着缺乏,任何人都可以推荐一种更好的方法来解决这个问题吗?
我能想到的唯一其他事情包括:
更新
也许这些信息会有用。我决定使用WinDbg并附加到我的应用程序。然后我关闭它,并切换到线程0并转储堆栈内容。这就是我所拥有的:
ThreadCount: 6
UnstartedThread: 0
BackgroundThread: 1
PendingThread: 0
DeadThread: 4
Hosted Runtime: no
PreEmptive GC Alloc Lock
ID OSID ThreadOBJ State GC Context Domain Count APT Exception
0 1 1c70 005a65c8 6020 Enabled 02dac6e0:02dad7f8 005a03c0 0 STA
2 2 1b20 005b1980 b220 Enabled 00000000:00000000 005a03c0 0 MTA (Finalizer)
XXXX 3 08504048 19820 Enabled 00000000:00000000 005a03c0 0 Ukn
XXXX 4 08504540 19820 Enabled 00000000:00000000 005a03c0 0 Ukn
XXXX 5 08516a90 19820 Enabled 00000000:00000000 005a03c0 0 Ukn
XXXX 6 08517260 19820 Enabled 00000000:00000000 005a03c0 0 Ukn
0:008> ~0s
eax=c0674960 ebx=00000000 ecx=00000000 edx=00000000 esi=0040f320 edi=005a65c8
eip=76c37e47 esp=0040f23c ebp=0040f258 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
USER32!NtUserGetMessage+0x15:
76c37e47 83c404 add esp,4
0:000> !clrstack
OS Thread Id: 0x1c70 (0)
Child SP IP Call Site
0040f274 76c37e47 [InlinedCallFrame: 0040f274]
0040f270 6baa8976 DomainBoundILStubClass.IL_STUB_PInvoke(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\WindowsBase\d17606e813f01376bd0def23726ecc62\WindowsBase.ni.dll
0040f274 6ba924c5 [InlinedCallFrame: 0040f274] MS.Win32.UnsafeNativeMethods.IntGetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2c4 6ba924c5 MS.Win32.UnsafeNativeMethods.GetMessageW(System.Windows.Interop.MSG ByRef, System.Runtime.InteropServices.HandleRef, Int32, Int32)
0040f2dc 6ba8e5f8 System.Windows.Threading.Dispatcher.GetMessage(System.Windows.Interop.MSG ByRef, IntPtr, Int32, Int32)
0040f318 6ba8d579 System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame)
0040f368 6ba8d2a1 System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame)
0040f374 6ba7fba0 System.Windows.Threading.Dispatcher.Run()
0040f380 62e6ccbb System.Windows.Application.RunDispatcher(System.Object)*** WARNING: Unable to verify checksum for C:\Windows\assembly\NativeImages_v4.0.30319_32\PresentationFramewo#\7f91eecda3ff7ce478146b6458580c98\PresentationFramework.ni.dll
0040f38c 62e6c8ff System.Windows.Application.RunInternal(System.Windows.Window)
0040f3b0 62e6c682 System.Windows.Application.Run(System.Windows.Window)
0040f3c0 62e6c30b System.Windows.Application.Run()
0040f3cc 001f00bc MyApplication.App.Main() [C:\code\trunk\MyApplication\obj\Debug\GeneratedInternalTypeHelper.g.cs @ 24]
0040f608 66c421db [GCFrame: 0040f608]
编辑 - 不确定这是否有帮助,但主线程的调用堆栈如下所示:
[Managed to Native Transition]
> WindowsBase.dll!MS.Win32.UnsafeNativeMethods.GetMessageW(ref System.Windows.Interop.MSG msg, System.Runtime.InteropServices.HandleRef hWnd, int uMsgFilterMin, int uMsgFilterMax) + 0x15 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.GetMessage(ref System.Windows.Interop.MSG msg, System.IntPtr hwnd, int minMessage, int maxMessage) + 0x48 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame = {System.Windows.Threading.DispatcherFrame}) + 0x85 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes
PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes
PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes
我对它进行了搜索,发现了一些与WPF GUI挂起的帖子,也许这会给我一些更多的线索。
答案 0 :(得分:5)
将以下处理程序添加到应用程序在单独线程中创建的每个窗口中:
win.Closed += (o, e) => win.Dispatcher.InvokeShutdown();
如果主线程挂起,请在win.Dispatcher.InvokeShutdown()
中调用MainWindow.Closed
- 这将自动关闭在主线程中创建的所有其他窗口。
如果没有这个处理程序,带有以下代码的应用程序也会在退出时挂起:
void Worker() {
var win = new Window();
// win.Closed += onWindowClose ?? ((o, e) => editor.Dispatcher.InvokeShutdown());
editor.Show();
System.Windows.Threading.Dispatcher.Run();
}
答案 1 :(得分:3)
您看到的工作线程ID是0.这是一个框架线程,并且是预期的 - 它不是程序“生成”的线程。如果您附加到任何.Net流程,您将看到这一点。我不确定它是哪个框架线程 - 绝对不是终结器线程,因为它永远不是线程0.也许是JIT线程?
更有趣的是你的主线程,因为这似乎是悬挂的。我会集中精力调试你的主线程来解决这个问题。它是否已经从一个窗口关闭的事件处理程序中解锁,等待一些永远不会发生的事情?
<强>更新强>
在读取添加到问题的主线程的堆栈跟踪之后,运行测试以确定主线程是否停止或是否只是空闲等待消息(并且它正在等待)将会很有趣对于从未收到或从未收到过的WM_CLOSE。
以下代码可用于手动向您的应用程序发送WM_CLOSE消息。只需等待程序在关闭程序时挂起,然后运行代码即可。将进程名称替换为您自己的名称。
更新2
好吧,看起来主线程很好并且真正挂起,因为它没有处理WM_CLOSE或WM_QUIT消息。
请尝试制作可以重现问题的最小应用程序并发布代码。
示例WM_CLOSE \ WM_QUIT App
internal class Program
{
private const int WM_QUIT = 0x0012;
private const int WM_CLOSE = 0x0010;
[DllImport("user32.dll")]
private static extern bool PostMessage(int hhwnd, uint msg, IntPtr wParam, IntPtr lParam);
private static void Main()
{
Process p = GetProcess("Your process name - no '.exe' required");
CloseMainWindow(p);
}
private static Process GetProcess(string name)
{
List<Process> processes = Process.GetProcessesByName(name).ToList();
if (processes.Count != 1)
{
throw new Exception(
"Expected 1 process with name '" + name +
"' but found " + processes.Count + ".");
}
return processes[0];
}
private static void CloseMainWindow(Process p)
{
PostMessage(p, WM_CLOSE, "close");
}
private static void QuitApplication(Process p)
{
PostMessage(p, WM_QUIT, "quit");
}
private static void PostMessage(Process p, uint message, string name)
{
Console.WriteLine("Posting {0} message to '{1}'...", name, p.ProcessName);
bool succeeded = PostMessage(p.MainWindowHandle.ToInt32(), message, IntPtr.Zero, IntPtr.Zero);
Console.WriteLine("Posted {0} message to '{1}' (succeeded:{2}).", name, p.ProcessName, succeeded);
}
}
答案 2 :(得分:3)
我终于找到了这个问题。我有一个由MEF Import
控制的控件,但实际上还没有调用(尚未)。我认为MEF实例化了它,即使它没有在任何地方被引用(我假设创建没有发生直到资源被请求,但显然我错了)。我通过使用Lazy&lt;&gt;修复了这个问题。实例化,现在它可以工作。这个真的让我感动,但感谢大家的帮助。我学到了很多东西试图调试这个问题。
答案 3 :(得分:1)
如果要创建工作线程(而不是池线程),请将其Name设置为创建时的描述性内容。
答案 4 :(得分:1)
向所有代码添加日志记录以记录方法和线程ID。您可以使用正则表达式替换将它放在每个方法的开头。然后运行应用程序并关闭。查看哪些日志消息与未关闭的工作线程具有相同的线程ID。