WinFormsでWPFのユーザーコントロールを使うと発生する例外の解決方法

この記事は公開から4年以上経過しています。

WinForms上でWPFユーザーコントロールを使うと、WPFユーザーコントロールが破棄されるタイミングで内部例外が発生する問題の解決方法です。
限定されたケースでのみしか発生しないうえ例外自体もハンドルされているため問題に気付いていないケースもあると思いますが、気付いてしまい対応に迫られた方の解決の糸口になれば幸いです。

問題

WinForms上にElementHostコントロールを使って配置したWPFのユーザーコントロール上でツールチップやポップアップを表示すると、ElementHostをDisposeするタイミングで内部例外が発生する。また、このエラーが1度発生するとプロセスを再起動するまで他のElementHostの動作が極端に遅くなる。

確認できている再現条件は以下のとおり。

  1. VisualStudio2015、またはロードされるCLRバージョン(App.configのSKU指定)がv4.0の.NETプロジェクト
  2. デバッガでプロセスのモジュール一覧を確認したときAccessibility.dllがロードされている(ソフトウェアキーボードやGoogle Chromeを起動していると自動的にロードされる)
  3. WinFormsでElementHostを用いてWPFユーザーコントロールをホストしている
  4. 上記3のElementHostのWPFユーザーコントロールでウィンドウハンドルを持つツールチップ/ポップアップ(コンボボックス)などを表示
  5. 上記4のElementHostをDispose

これら全てを満たすと、VisualStudioの出力ウィンドウにのタイミングで例外がスローされました: 'System.ComponentModel.Win32Exception' (WindowsBase.dll の中)といったエラーメッセージが表示される。

出力ウィンドウの出力内容:
file

例外設定のCommonLanguageRutimeの例外スローで中断を指定したときのエラー発生箇所:
file

例外が発生した処理のスタックトレース:
file

原因

ElementHostでWPFのユーザーコントロールをホストした場合、ビジュアルツリールート要素のAdornerDecoratorのAutomationPeerのNameが未設定であることから、以下のFrameworkコード部分で例外がスロー(ハンドル済)される。
また、当該エラー発生後にはプロセスを再起動するまで同じウィンドウハンドルを利用しているWPFユーザーコントロールにウィンドウメッセージが発生(マウスオーバーやクリックなど)する度に同じ例外がハンドルされるようになることで処理が遅くなる。

Microsoft Reference Sourceから抜粋したFrameworkコード(赤枠内で例外発生)
file

対応

以下のようにビジュアルツリールート要素のAdornerDecoratorに対してAutomationProperties.Name添付プロパティを設定することにより、例外が発生するロジックが実行されないようにする。

サンプルソースコード

C#

public Form()
{
    InitializeComponent();

    // ↓使用する全てのElementHostにこのイベントを追加
    elementHost.HandleCreated += (o, e) =>
    {
        var preSrc = System.Windows.PresentationSource.FromVisual(elementHost.Child);
        System.Windows.Automation.AutomationProperties.SetName(preSrc.RootVisual, elementHost.Name);
    };
}

VB.NET

Public Sub New()

    InitializeComponent()
    ' ↓使用する全てのElementHostにこのイベントを追加
    AddHandler ElementHost.HandleCreated,
        Sub(o, e)
            Dim preSrc = System.Windows.PresentationSource.FromVisual(ElementHost.Child)
            System.Windows.Automation.AutomationProperties.SetName(preSrc.RootVisual, ElementHost.Name)
        End Sub

End Sub

以上です。

シェアする

  • このエントリーをはてなブックマークに追加

フォローする