2011-10-21 15 views
0

.NET Windowsサービスに大きな問題があります。非常に異なる構成の複数のサーバー上で動作します。このサービスは一部のサーバーでクラッシュする可能性があるようですが、他のサーバーでは安定しています。不安定性は最近導入されましたが、これまでのところ不明です。私たちはWindows 2003/Windows 2003 R2/Windows 2008を実行しているサーバーを持っています。そのほとんどは完全に更新されています。Windowsメッセージのディスパッチ中に.NET Windowsサービスがクラッシュする

異なるターゲットフレームワークバージョン(2.0/3.5/4.0)に対してサービスを構築しようとしましたが、違いはありません。不安定なサービスを持つマシンは、フレームワークのすべてのバージョンで不安定です。私は.NETフレームワークを修復しようとしましたが、違いはありません。私が見て分かる限り、サービス全体とその依存関係は管理されたコードになっています。

また、コマンドラインバージョンでserver-codeを実行しようとしました。これは安定しているようだ。これを回避策として使用しています。ただし、問題はユーザーアカウントに関連していません。サービスは通常、「ローカルサービス」として実行されます。私はコマンドラインバージョンを実行するために使用するアカウントであるローカル管理者アカウントの下で実行させようとしました。しかし、サービスはまだ不安定です。

これまでのところ、いずれかのサーバーで再現可能な状況を作成できました。 - サーバー上でサービスを開始します。 - 同じサーバー上の新しいRDPセッションでドメインユーザーとしてログオンします。 - 私たちのクライアントソフトウェアを起動します。クライアントソフトウェアは、そのセッションでTCPリモート処理を通じて私たちのサービスにアクセスします。 - クライアントとセッションを閉じます。 - サーバー上のドメインユーザーとの新しいRDPセッションを開きます。 - サービスの即時クラッシュ!

ドメインユーザーが新しいRDPセッションにログオンした時点でサービスがクラッシュすることに注意してください。私たちのクライアントソフトウェアは、その時点でそのセッションで実行されていません。最初のセッションでクライアントを開いてTCPリモート処理サービスにアクセスしないと、2回目のログオン中にサービスがクラッシュすることはありません。ローカル管理者としてセッションを開くと、サービスはクラッシュしません。

クラッシュするサービスにネイティブデバッガ(OllyDbg)を接続できました。アドレス0x4bcdcee9で実行しようとすると、アクセス違反でクラッシュします。そのアドレスは、すべてのサーバーと構成で同じです(イベントログに毎回そのアドレスが記載されています)。私はクラッシュスレッドのスタックを見てきました。スレッドはクラッシュの直前に作成されたようです。まず、Ole32.dllを読み込もうとします。これは、OLE32からいくつかのコードを実行し、その後、私は、これらの関数が呼び出されている参照してください。

  • User32.SetTimer
  • User32.GetMessageW
  • User32.TranslateMessage
  • User32.DispatchMessageW

クラッシュDispatchMessageWのどこかにあります。スタック上のDispatchMessageWの* MSG引数を見ることができます。これが渡されるようになっています

  • のhWnd = 0x00090082
  • メッセージ= 0X0000001E
  • のwParam = 0x00000000の
  • のlParam = 0x00000000の

私が試したスパイを++。しかし、それはWindowsサービスのhWndを検出していないようです。

サービスはこのメッセージを受け取り、パースしてディスパッチしようとするたびに、マップされていないメモリである0x4bc4cee9を呼び出してクラッシュします。

編集: Hansの示唆によれば、私はシステムを調査しました。私はサービスをデバッグしました。サービス実行可能ファイルに余分なサービスを追加して、ヘルパーサービスを開始し、デバッガをアタッチしてから、実際のサービスを開始することができました。このようにして、サービスのOnStartでもデバッグすることができます。 SetWindowsHookA、SetWindowsHookW、SetWindowsHookExA、SetWindowsHookExWにブレークポイントを設定しましたが、どれもヒットしませんでした!

EDIT 2:私はすべての私のノートをチェックして、私は私のノートにタイプミスがあったので、私は、間違った結論に飛びついたことがわかった:-Sとにかく、クラッシュのアドレスは0x4bc4cee9です。実行中のある時点で、msado15.dllがそこにロードされます。クライアントがサーバーから切断されると、デバッガに2つの管理対象例外があることがわかります。その後、ディスパッチャによって処理され、CoFreeUnusedLibraries()を呼び出すWM_Timerメッセージが表示されます。その結果、msado15.dllがアンロードされます。ディスアセンブラでmsado15.dllを開き、Microsoftからシンボルをロードしました。 DLLは、Microsoft Data Access Components(MDAC)2.8 SP1の一部です。バージョンは2.82.4795.0で、2011年1月にリリースされた最新バージョンであることを示しています.ADOConnectionおよびADORecordsetのAdvise()およびUnadvise()関数があります。 Advise()はInitAsyncEvents()を呼び出し、RegisterClassEx()を呼び出します。 RegisterClassEx()に渡されるWndProcは0x4bc4cee9にあるFireEventOnMainThread()です!私はそこに機能を見ることができます!オブジェクトが破棄されると、Unadvise()およびDestroyAsyncEvents()およびUnregisterClassEx()が呼び出される必要があります。しかし、どういうわけか、それは起こっていません。 DLLは、クラスの登録を解除する前にアンロードされます。その結果、次のイベントでクラッシュします。これは、何とか2つの管理例外に関連している可能性があります。私はさらに調査します。

スタックトレース:http://pastebin.com/dsSjMe4Y

ログイン:http://pastebin.com/qD2MXvHd

私は本当にこの問題では、いくつかの指導をお願い申し上げます。同様に、どのプロセスがこのメッセージを送信している可能性がありますか?サービスがこれを完全に間違って派遣するのはどうでしょうか?これを避ける方法は?

は、私は問題を発見し、 ヒースクリフ

+0

非常に長い質問であり、情報のほとんどはすべて有用です。有益なことは、クラッシュと例外情報の時にコールスタックを持つことです。シンボルが正しくロードされていることを確認してください。 –

+0

WM_TIMECHANGEはブロードキャストされたメッセージです。すべてのトップレベルウィンドウに移動します。あなたが1つを持っていたように聞こえて、ウィンドウプロシージャを持つDLLがうまくウィンドウを閉じることなくアンロードされました。 .NETのSystemEvents btwは、明示的に登録解除する必要がある静的イベントです。 –

+0

@SevaTitovスタックトレースを追加しました。他のすべての情報は私にとって重要です。 – Heathcliff

答えて

0

、ありがとうございました。それをピン留めして回避策を作るのに、私にはおよそ8日間かかりました!

6.0までのすべてのADODBバージョンには深刻なバグがあります。 ADODB 2.8はMDAC 2.8(XPおよびWin2003用)の一部であり、ADODB 6.0はVista/Win2008の一部であり、ADODB 6.1はWin7/Win2008R2の一部です。コアDLLはmsado15.dllです。 ConnectionクラスまたはRecordsetクラスがインスタンス化されると、RegisterClass()で登録され、__FireEventOnMainThread()というWndProcがあります。すべてのCOMオブジェクトが再度破棄されると、参照カウントは0に設定されます.Ole32!CoFreeUnusedLibraries()が呼び出されると、すべてのCOM DLLのDllCanUnloadNow()が呼び出されます。 DllCanUnloadNow()は参照カウントをチェックし、0のときは0を返し、アンロードできることを示します。 ADODB 6.1(Win7およびWin2008R2用にのみリリース)では、MicrosoftはDllCanUnloadNow()で修正を実装しました。 AsyncEventsWndをチェックし、まだ存在する場合、DLLをアンロードしません。しかし、実際のバグはCOMオブジェクトの処分にまだ残っています。参照カウントは減少しますが、何らかの理由でUnregisterClass()が呼び出されません。 DLLがアンロードされ、ブロードキャストイベントが送信されると、WndProcがメモリにもう存在しないため、アプリケーションはアクセス違反に遭遇します。クラッシュ!サービスの場合は、Ole32!CDllHostがインスタンス化されます(場所がわからない場合)。このクラスはTimerProc STAHostTimerProc()でタイマーを開始し、300秒ごとに起動します。 STAHostTimerProc()はCoFreeUnusedLibraries()を呼び出します。多くの異なるブロードキャストメッセージがあります。たとえば、新しいユーザーセッションがターミナルサーバーで開始されると、WM_TIMECHANGEがブロードキャストされます。したがって、Vista/Win2008までのWindowsマシンでは、アプリケーションがADODB.ConnectionまたはADODB.Recordsetを作成し、Ole32!CDllHostを作成してCOMオブジェクトをすべて破棄してから、タイマがmsado15.dllをアンロードするまで待ってからブロードキャストメッセージを待つ、そのアプリケーションはクラッシュする!

マイクロソフトはMDAC 6.1でこの問題を修正しましたが、以前のバージョンの修正プログラムをリリースしなかったのは恐ろしいことです。古いオペレーティングシステムはすべて影響を受けます。

回避策として、静的なADODB.Connectionオブジェクトを作成することにより、ADO COMオブジェクトの参照カウントが0にならないようにします。

関連する問題