2021年5月30日日曜日

FAC製作記 その1 ダイアログを表示するときに例外発生

先日の通り、FACを作るときに遭遇したあれやこれやを早速書いていきます。

本日のお題はダイアログです。
GUIアプリケーションであれば、ダイアログを使わないもののほうが少ないのではないでしょうか。
FACでも結果の通知とフォルダーの選択にダイアログを使っています。

今回の現象と解決策だけ書くと以下のとおりです。

【何があったのか】
.NET Framework 4.7.2から.NET 5に移行するとき、ダイアログの表示で例外が発生した。

【どうしたのか】
ダイアログのライブラリを乗り換えた。

それぞれについて詳しく書く前に、.NET Framework 4.7.2時代のダイアログについて説明します。

【コトが起きる前】
.NET Framework 4.7.2時代、FACはダイアログとしてWindows API Code Packを使っていました。
これはもともとMicrosoftが提供していたもののようですが、俺が知ったときにはすでにMicrosoft直々の公開は終了していて、いろいろなユーザーがかつて公開されていたものをそのままor独自のモディファイを加えて公開している状況でした。
上記のリンクはそれらのうち俺が使っていたものです。

たかだかダイアログでなぜライブラリを使っているのかというと、標準では足りなかったからです。
FACはGUIフレームワークとしてWPFを使っています。
WPFにもダイアログは用意されているのですが、問題があります。
用意されているのは通常の通知用ダイアログのみで、フォルダーを選択するダイアログはないのです。(FACでは使っていませんが、ファイル選択ダイアログもない)

WPFでフォルダー選択ダイアログを使いたい場合、Windows API Code Packは定番と言っていいでしょう。
それについて解説しているブログもすぐ見つかるくらいなので、王道と言ってもいいかもしれません。
Windows API Code Packには通常のダイアログ(※)も用意されているので、ダイアログはすべてWindows API Code Packを使っている、というのが.NET Framework時代のFACでした。
※正確には機能が強化されたTaskDialogなのですが、通知用であれば同じ感覚で使えます。

【起きたこと】
FACを.NET 5に移行するため、事前検証としてテスト用アプリを作って、色々と動作を確認していたとき、事件は起こりました。
Windows API Code PackのGitHubのreadmeにも「.NET 5をサポートしてるよ!」と書いてあるので、同じようにコード書けば動くだろー、と楽観視していました。

まずは通常のダイアログの動作を確認します。


例外が発生しました。
サポートしてるんじゃなかったんかい。

次にフォルダー選択ダイアログの動作を確認します。


こちらは.NET Framework 4.7.2のときと同じように表示されます。
なんでやねん。

【例外のメッセージから調べる】
気を取り直して、例外のメッセージを確認してみます。

System.NotSupportedException: 'TaskDialog feature needs to load version 6 of comctl32.dll but a different version is current loaded in memory.'
EntryPointNotFoundException: Unable to find an entry point named 'TaskDialogIndirect' in DLL 'Comctl32.dll'.

「TaskDialogはcomctl32.dllのバージョン6以上が必要だが、そうでないバージョンがロードされている。」「comctl32.dllに'TaskDialogIndirect'というエントリーポイントがない。」とあります。
調べてみると、dllファイルのバージョンをマニフェストファイルで指定すれば良い、とのこと。

しかし、ここで俺は以下のように思いました。
「.NET Frameworkではマニフェストファイルなんか不要だった。.NETになって必要になるのは納得いかない」
今思い返すと逆ギレですが、「じゃあ別のダイアログを探そう」という発想に行き着きました。

【乗り換え先のダイアログを探す】
「.NET 5対応」「NuGetでインストール可能」を条件に探してみると、Ookii.Dialogsというものが見つかりました。
余談ですが、こちらの作者の方は東大に留学経験があり、サイトのトップにも「Ookii -発音は/oːkiː/(だいたい"oh-key")- は日本語でbigという意味で、私のラストネームと同じ意味です」とあるので、「大きい」を意識した命名です。
さっそくインストールして、Windows API Code Packのダイアログ類を使っていたところをOokii.Dialogsに差し替えてみます。

まずは通常のダイアログから。


問題なく表示されますね。
続いてファイル選択ダイアログも。


こちらも問題なく表示されました。

ということで、この件はWindows API Code PackからOokii.Dialogsへの乗り換えという形で解決しました。

【原因と解決策の考察】
精査したわけではないので、俺の想像であることをご承知おきください。
すでに書いたように、Windows API Code PackはもともとMicrosoft謹製です。
名前から想像するに、WindowsのAPIをごく薄くラップしたライブラリ類なのでしょう。
したがって、.NET Frameworkと .NET 5の差分(標準で参照するcomctl32.dllのバージョンが違う?)を隠蔽しきれなかったのではないかと思います。

Ookii.DialogsはTaskDialogクラスを独自に実装しています。
そのためにcomctl32.dllを参照する必要がなく、Windows API Code Packで地雷になった箇所の影響を受けなかったのではないでしょうか。

【参考資料】

今日の1曲:


0 件のコメント:

コメントを投稿