27

可能な解決策が見つかりました!

私は解決策を見つけたと信じています!実際に機能することを確認するためにテストを続けますが、期待しています:)質問のEDIT 3で解決策を見つけた方法を詳しく説明しました!

私の問題の背後にある完全な背景と、この質問からの入力の結果として私が試したことを知りたい人は、これを参照してください: http://pastebin.com/nTrEAkVj

私はこれを頻繁に編集します (ほとんどの平日は 1 日 3 回以上) 調査と状況を進めているので、興味がある場合、または私の問題に関する情報や知識がある場合は、チェックし続けてください :)

簡単な背景:

私が作成したこのアプリは、スクリーン セーバーを変更したり、ワーク ステーションをロックしたりすることでクラッシュする可能性があります。

スクリーンセーバーを変更してアプリを一貫してクラッシュさせることができる場合、その一部はアプリに何らかの種類のメッセージを送信しています (必ずしも Windows メッセージではなく、最も一般的な意味でのメッセージを意味します)。応用。このため、問題を引き起こしているメッセージがアプリケーションによって処理されないようにする方法を見つけようとしています。これが解決策として最善の方法ではないことは承知していますので、私に教えていただく必要はありません。背景情報を確認するか、それが気になる場合はその理由を尋ねてください (正当な理由があります)。

私の質問:

関連性に応じてラベル付けされた、問題の解決に役立つ情報がいくつかあります (1 つは最も関連性が高く、3 つはあまり役に立ちません)。

  1. Wndproc() を使用して、次のようにメッセージを除外しようとしています:

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If CInt(m.Msg) <> CInt(26) then
            MyBase.WndProc(m)
        end if
    End Sub
    

    ただし、Windspector によると、WM_WININICHANGE メッセージはまだアプリに送信されています (これは理にかなっています)。ただし、0 も返されています...正常に動作していた場合、これは発生しないはずです。何でもないですよね?これが期待どおりに機能しない理由と、それを機能させる方法に関する情報は非常に役立ちます!

  2. メッセージフィルターも使用してみました:

    Public Class MyMessageFilter
        Implements IMessageFilter
        Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
            ' Return true for messages that you want to stop  << someone elses comment       
            Return m.Msg = 26
        End Function
    End Class
    

    次に、 mybase.load 処理メソッドに追加します。

    Application.AddMessageFilter(新しい MyMessageFilter())

    ただし、特定のメッセージのみをフィルタリングするように見え、私のようなメッセージは明らかにこれらに含まれていません。WM_メッセージをキャッチするために何らかの種類のフィルターを使用することが絶対に不可能であるかどうか、またはメッセージフィルターを使用して私の目標を達成する他の方法があるかどうかについての情報も役立ちます.

  3. スクリーンセーバーを変更すると、アプリケーションにメッセージを送信できますか? スクリーンセーバーの変更による別の種類のメッセージも致命的である可能性はありますか?

私の状況に関して他に役立つ情報があれば教えてください。それを得るために最善を尽くします! あなたが与えることができるどんな助けにも前もって感謝します:)

編集:

WM_CHANGESETTINGメッセージのみを送信し、メッセージを送信したsendmessagetimeoutのタイムアウトの長さを超えてプログラムを待機させた場合、プログラムはクラッシュしません... RESPONSEがプログラムをクラッシュさせているようです.. 。 面白い。私は間違いなく私の解決策に近づいています!もう少しテストすることで、プログラムがメッセージに応答しないことを確認する方法を見つけられるようになると思います。

編集2:

今日、非常に有望なものを発見しました。wndproc 関数を次のように定義しました。

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If CInt(m.Msg) <> CInt(26) Then
        MyBase.WndProc(m)
    Else
        MessageBox.Show("Get to work!", "Attention", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification)
    End If
End Sub

次に、プログラムを実行してから、次を使用して WM_SETTINGCHANGE メッセージを送信してみました。

SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero, _
             SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, IntPtr.Zero)

私が作った別のプログラムで。それでどうしたの?私はこれを数回試しましたが、メッセージボックスがポップアップするたびに (選択した単語は重要ではありません)、[OK] を押す前にさまざまな時間待機してみました。多くの場合、何も変わらず、それでもクラッシュします。しかし、場合によっては、おそらく 1/5 回、プログラムがまだ応答していることがあります。もしそうなら、私はメッセージをもう一度送信しようとしますが、通常は同じプログラムの実行中に2回目に失敗しますが、時々もう一度、さらに約1/5回、プログラムは送信されませんでした.もう一度クラッシュします。そして、2回クラッシュさせようとしたとき。どちらも間に合いませんでした

約 5 秒待つと、オッズが高くなるように見えました。フリーズ ボタンを押した直後に、メッセージをトリガーするフォームにまだフォーカスがあり (上部のバーは青色)、メッセージボックスがポップアップします。上部も青(焦点が合っていると思います)、両方ともまだ「焦点が合っています」(少なくとも青笑)。すると5秒くらいで元のフォームからフォーカスがずれてきて、それを見てOKを打ってみたり。

私は現在、これを少し待ってからメッセージボックスを確認すると、メッセージがタイムアウトして返されないため、プログラムがクラッシュしないことがあると考えています。ただし、メッセージが返されるかどうかが、プログラムの実際の動作に影響する理由はわかりません。これは、明確化が役立つ領域です:)

編集 3:

そのため、Winspector でもう少し調べてみると、デスクトップ ウィンドウ (Winspector では「sysListView32 'FolderView'」というラベルの付いたウィンドウ) に WM_ERASEBKGND が表示されるのを待ってから、メッセージボックスで「OK」を押すことがわかりました。 、その後、プログラムはクラッシュしません、興味深いです! 通常、WM_ERASEBKGND メッセージが表示されるまでには、sendmessagetimeout のタイムアウトに近い時間がかかります。これはもちろん、自家製のテスト アプリから WM_SETTINGCHANGE メッセージを送信した後です。

それで、この後、Winspector についてもう少し調べることにしました。もっと便利なキューを見つけることができるのではないでしょうか? 明らかに、winspector がデスクトップに送信されるメッセージを表示するのを待つことは、プログラムの実際の修正ではありません。私のプログラム プロセスの下に、変わった名前のウィンドウがいくつか見つかりました。デフォルト IME'".

これらのウィンドウに送信されるメッセージを調べて、WM_SETTINGCHANGE や WM_ERASEBKGND などの認識可能なメッセージを受信して​​いるかどうかを確認することにしました。結局のところ、彼らはメッセージを頻繁に受信するわけではありません.私が見ている間、GDI +はメッセージを受信しませんでしたが、.NET -BroadcastEventWindowはメッセージをいくつか受信しました. BroadcastEventWindow に移動するのは、自分のアプリケーション ウィンドウまたはその後に別のウィンドウをクリックしたときのほとんどが WM_appactivate だけでした。

しかし... .Net BroadcastEventWindow が WM_CHANGESETTING メッセージを受信して​​いることに気付きました!!!! 他に表示されるメッセージを確認します。多くはありませんが、バグが原因でアプリがクラッシュしたときに、認識できないメッセージがあることに気付きました: WM_USER+7194 (0x201A)。うーん、それが何であるか見てみましょう。Google で調べたところ、アプリケーション/ユーザー定義のメッセージであることがわかりました。それに関連する問題についてもう一度検索したところ、誰かがフィルタを使用してこのメ​​ッセージを除外し、問題を修正できることに気付きました。彼らの ( http://www.pcreview.co.uk/forums/handling-wm_user-messages-t1315625.html )。少なくとも私にとっては試してみる価値がありますよね?そのため、以前に試したフィルターを再度追加し、フィルターする値を変更します。アプリはクラッシュしませんでした!!!!!!!!

次に、ワークステーションをロックして、それでもクラッシュするかどうかを確認してみます (以前は WM_CHANGESETTING メッセージを送信するだけだったからです)。結局、それでもクラッシュしました:(しかし、そのウィンドウのwinspectorをもう一度見てみると、ああ、2つの新しいWM_USERメッセージがあります:WM_USER+7294(0x207E)とWM_USER+7189(0x2015)。あまりにも...そして、ワークステーションのロックでもクラッシュしません!!! :D

これまでのところ、これが通常のアプリの使用にも悪影響を及ぼしていないことに気付きました! ユーザー定義メッセージが私のプログラムに意図的に関与しているとは思わないので、これは理にかなっています。

私の解決策に問題がなく、うまく機能することを確認するまで、質問をもう少し開いたままにします。デバッグの中間段階でどのように進めるかについて少しアドバイスをくれた皆さんに感謝します:)

4

2 に答える 2

6

この問題は、長年にわたってさまざまな質問で言及されてきました。完全に診断したわけではありません。私が知っていることだけをお伝えします。

この問題は、SystemEvents クラスが初期化される方法に関連しています。セキュリティで保護されたデスクトップに切り替えるときに発生するイベントをトリガーするクラスであるため、事故に関係しています。スクリーン セーバーを使用するか、ワークステーションをロックする (Windows + L キー)。Winforms コントロールは、テーマやシステム カラーが変更されたときに再描画が必要になる可能性があるため、一般に SystemEvents.DisplaySettingsChanged イベントに関心があります。このイベントは、システムがデスクトップを切り替えたときにも一般的に発生します。

重要な問題の 1 つは、UI スレッドでイベントを発生させる必要があることです。SystemEventsは、実際にどのスレッドが UI スレッドであるかを正確に推測する必要があります。これは、プログラムで作成された最初のウィンドウが、実際には UI スレッドではないスレッドで作成され、それ以外の場合は、その COM アパートメントを STA に設定することによって 1 つになりすます場合に問題が発生します。スレッドが実際に実行を続けている場合、イベントはそのスレッドで発生します。スレッドがなくなった場合 (珍しいことではありません)、SynchronizationContext.Post() が呼び出しをマーシャリングしようとして失敗すると、例外が発生します。例外は飲み込まれ、イベントは任意のスレッドプール スレッドで発生します。

いずれにせよ、イベントは正しいスレッドで発生せず、UI コンポーネントのスレッド要件に違反しています。これは見過ごされる傾向があり、何らかの奇妙な理由により、デスクトップ スイッチで同じイベントが発生すると、デッドロックやクラッシュが頻繁に発生する傾向があります。

プログラムの初期化コードを注意深く確認する必要があります。最も一般的な間違いは、独自のスプラッシュ スクリーンを作成することです。それを正しく行うには、.NET フレームワークの組み込みサポートを必ず使用してください。

于 2012-01-25T18:51:26.143 に答える
4

独自のメッセージ フィルターを実装する

Public Class MyMessageFilter
    Implements IMessageFilter

    Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
        ' Return true for messages that you want to stop
        Return m.Msg = MessageToDiscard
    End Function
End Class

アプリケーションの開始時にこのフィルターを追加します

Application.AddMessageFilter(New MyMessageFilter())
于 2012-01-25T17:23:28.197 に答える