C# WinUI 3アプリを作っていく途中で、躓いた部分を備忘録的に投稿します。今回はWinUI3プロジェクトでタイマーなどのスレッドから別のプロシージャや関数を呼び出した際に「別のスレッドにマーシャリングされたインターフェイスを呼び出しました。(0x8001010E(RPC_E_WRONG_THREAD))」が表示された場合の対処法です。
COMの例外というよりMVCの弊害
C#のWinUI 3プロジェクトでメインウィンドウはXamlで構成して処理をCSファイル側で記述しますが、Xamlで作った画面がViewでCSで構成するクラス部分がModelでMVC構造でアプリが構成されています。
View側から処理要求(例えば、ボタンがクリックされたなど)で処理が動いて結果を更新するような形です。
つまり作成したイベントハンドラーからであれば、関数でもプロシージャでも問題なく動作しますが、別スレッド(今回はタイマーで作成されたスレッド)から関数やプロシージャを呼び出して、Xamlの画面を更新しようとするとエラーになるようです。
System.Runtime.InteropService.COMException: “アプリケーションは、別スレッドにマーシャリングされたインターフェイスを呼び出しました。(0x8001010E(RPC_E_WRONG_THREAD))”
エラー画面より
でこんな時、Dispatcherを使って次のような感じで処理を包むことで対処できていたのですが、WinUI3 (Windows APP SDK)では回避できませんでした。
await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
//行いたい処理;
});
色々調べてみて、Windows App SDKでは[Dispatcher]ではなく「DispatcherQueue」を使うように変更されているようです。
公式ページの「スレッド機能の移行」を参考にしました。
WinUI3(というかWindows App SDKは随分分かり辛くなってきました。これが業界標準になるので、慣れるしかありません。(笑)
DispatcherQueueを使って回避
さっそくエラーの回避を実装していきます。
別スレッドなどで呼び出している部分を次の様に変更します
if (this.DispatcherQueue.HasThreadAccess)
{
//行いたい処理;
}
else
{
this.DispatcherQueue.TryEnqueue(
Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () =>
//行いたい処理;
);
}
Dispatcherを実装しても、実行時にアクティブなウィンドウのDispatcherが取得できないようになっているので、互換性のために残されている感じがします。
Windows.UI.Core.CoreDispatcherを、Microsoft.UI.Dispatching.DispatcherQueue]に
CoreDispatcher.RunAsyncを、DispatcherQueue.TryEnqueueに変更することでエラーを回避することができました。
右クリックでメニューを表示する際に利用するので単純にコントロール上でマウスの右クリックを検出したい場合はContextRequestedだけで十分です。
まとめ
今回は短い記事ですが、WinUI 3(Windows App SDK)で 別スレッドからプロシージャーや関数を呼び出した場合に「別のスレッドにマーシャリングされたインターフェイスを呼び出しました。(0x8001010E(RPC_E_WRONG_THREAD))」が表示された場合の対処法を書きました。
Windows.UI.Core.CoreDispatcherによる回避方法が見つかりますが、最新のWindows App SDKでは、Microsoft.UI.Dispatching.DispatcherQueueとDispatcherQueue.TryEnqueueでエラーの回避を行うことができました。
C#のWinUI 3アプリでCOM Excetion 0x8001010E(RPC_E_WRONG_THREAD) が発生して困っている場合の参考になれば幸いです。
スポンサーリンク
最後までご覧いただき、ありがとうございます。