我的.NET Windows服务存在一个主要问题。它运行在配置完全不同的多台服务器上。该服务似乎易受某些服务器上的崩溃影响,但在其他服务器上保持稳定。最近引入了不稳定性,但迄今为止条件未知。我们有运行Windows 2003/Windows 2003 R2/Windows 2008的服务器。它们中的大多数都已完全更新。.NET Windows服务在调度Windows消息时崩溃
我们试着针对不同的目标框架版本(2.0/3.5/4.0)构建服务,但它没有什么区别。对于每个版本的框架,具有不稳定服务的机器都不稳定。我试过修复.NET框架,但这也没有什么不同。据我所知,整个服务及其依赖关系都在托管代码中。
我也尝试在命令行版本中运行服务器代码。这似乎运行稳定。我们现在使用这个作为解决方法。但是,该问题与用户帐户无关。该服务通常作为“本地服务”运行。我试图让它在本地管理员帐户下运行,这是我用来运行命令行版本的帐户。但服务仍然不稳定。
到目前为止,我已经能够在其中一台服务器上创建可重现的情况: - 在服务器上启动服务。 - 作为域用户登录到同一台服务器上的新RDP会话中。 - 启动我们的客户端软件,该软件通过该会话中的TCP远程访问来访问我们的服务。 - 关闭客户端和会话。 - 在服务器上与域用户打开一个新的RDP会话。 - 服务即时崩溃!
请注意,服务在域用户登录到新的RDP会话时崩溃。当时我们的客户端软件尚未在该会话中运行。如果我在第一个会话中未打开客户端并使用TCP远程访问服务,则该服务在第二次登录期间不会崩溃。如果我以本地管理员身份打开会话,则该服务也不会崩溃。
我已经能够将本机调试器(OllyDbg)附加到崩溃服务。尝试在地址0x4bcdcee9处执行时,它会因访问冲突而崩溃。该地址在所有服务器和配置上都是相同的(我在eventlog中每次都看到该地址)。我已经看过了崩溃线程的堆栈。该线程似乎是在崩溃之前创建的。首先它会尝试加载Ole32.dll。它运行从OLE32一些代码,然后我看到被称为以下功能:
- User32.SetTimer
- User32.GetMessageW
- User32.TranslateMessage
- User32.DispatchMessageW
崩溃在DispatchMessageW中。我可以在堆栈上看到DispatchMessageW的* MSG参数。它看起来像这样被传递:
- 的hWnd = 0x00090082
- 消息= 0X0000001E
- 的wParam = 00000000
- 的lParam = 00000000
我试过间谍++。但它似乎没有检测到Windows服务中的任何hWnd。
因此,该服务收到此消息,试图解析和调度它,每次最终调用0x4bc4cee9,这是未映射的内存,并崩溃。
编辑:根据汉斯的建议,我调查了系统事件。我调试了该服务。我为服务可执行文件添加了额外的服务,以便我可以启动帮助程序服务,然后附加调试程序,然后启动真实服务。这样我就能够调试服务的OnStart。我在SetWindowsHookA,SetWindowsHookW,SetWindowsHookExA和SetWindowsHookExW上放置了断点,但是没有一个被击中!
编辑2:我检查了我所有的笔记,发现我跳到错误的结论,因为我在我的笔记有一个错字:-S反正崩溃的地址是0x4bc4cee9。在执行的某个时候,msado15.dll被加载到那里。我可以看到,当客户端与服务器断开连接时,调试器中有2个受管异常。不久之后,我看到一个WM_Timer消息,它由调度程序处理并调用CoFreeUnusedLibraries()。这导致卸载msado15.dll。我在反汇编程序中打开了msado15.dll并加载了来自Microsoft的符号。该DLL是Microsoft数据访问组件(MDAC)2.8 SP1的一部分。该版本是2.82.4795.0,表示它是2011年1月发布的最新版本。ADOConnection和ADORecordset有Advise()和Unadvise()函数。 Advise()调用InitAsyncEvents()并调用RegisterClassEx()。传递给RegisterClassEx()的WndProc是FireEventOnMainThread(),它位于0x4bc4cee9!我可以看到那里的功能!应该发生的是,当对象被处置时,应调用Unadvise()和DestroyAsyncEvents()和UnregisterClassEx()。但不知何故,这并未发生。 DLL可以在取消注册类之前被卸载。这导致下一个事件发生崩溃。这可能以某种方式与2个管理的例外相关。我会进一步调查。
堆栈跟踪:http://pastebin.com/dsSjMe4Y
登录:http://pastebin.com/qD2MXvHd
我真的很感激在这个问题上提供一些指导。比如,哪个流程可以发送此消息?而这种服务怎么可能将这个完全错误的发送出去呢?如何避免这种情况?
谢谢 希刺克厉夫
非常长的问题,大部分信息都是有用的。在崩溃和异常信息时有一个调用堆栈会很有用。确保你有符号加载正确。 –
WM_TIMECHANGE是一个广播消息。去所有的顶级窗口。听起来就像你曾经有一个,然后与窗口过程的DLL被卸载,没有很好地关闭窗口。 .NET中的SystemEvents btw,需要明确注销的静态事件。 –
@SevaTitov我加了一个堆栈跟踪。所有其他信息似乎与我有关。 – Heathcliff