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曲:


2021年5月22日土曜日

作ってるもの

ブログの記事としてはお久しぶりです。
前回の記事が2016年の10月ですので、約4年半ぶりです。

この間特に何かあったとかではないのですが、
「1週間空いてしまった、来週は書こう」
「そうこうしてるうちに1ヶ月空いてしまった、来月こそ書こう」
「1年空いたし、もうよくね?」
という感じで、ずるずると4年半以上何も更新がないという事態になっていました。
これからは不定期でも年に1回くらいは更新したいな、と思っています。

今回のタイトルの「作っているもの」ですが、パソコン上で動かす簡単なツールを作っています。
名前を「Fur Affinity Classifier」としました。以下では頭文字取って「FAC」と記載します。
ソースコードや実行ファイルはGitHubにおいてあります。
READMEは英語で書いてありますが、今回は日本語での説明兼宣伝をさせてください。

【何これ?】
Fur Affinity(以下FA)からダウンロードしたファイルを、ユーザーのIDごとにフォルダーに分類するツールです。
FAについてはご存じの方が多いと思うので、詳しい説明は省略しますが、ケモノに特化したpixivと考えていただいてよいと思います。
classify (分類) に -er を付けて「分類機」という意味合いで名付けました。

【なんで作ったの?】
俺はFur Affinityで気に入ったイラストなどがあると自分のパソコンに保存するのですが、ファイルの分類が手間でした。
ファイルを分類するためには
1. ファイル名を見て
2. ユーザーIDを確認して
3. ユーザーIDに応じたフォルダーを開いて
4. ファイルを移動する
という手順が必要でした。
これがファイル2~3個とかならまだなんとかなりますが、5個10個となると面倒、20を超えると分類に割く時間がもったいないくらいになります。
じゃあ自分の勉強も兼ねて、自動でやってくれるツールを作ってしまえ、というのがスタートラインになっています。

【何ができるの?】
「何これ?」の通り、FAからダウンロードしたファイルを、フォルダーに分類することができます。
FAからダウンロードしたファイルは所定の形式になっていますので、後述する対象のフォルダー内のファイルを走査して、合致するファイルを移動します。
また、「このユーザーはIDと違うフォルダーに振り分ける」という設定もできます。
日本のような非ラテン文字圏だとこういう事態は起きやすいのではないかと思います。

【どうすれば使えるの?】
FACの実行には.NET 5が必要になります。
また、FAC内でも設定が必要です。
実際に俺が使っているときのスクリーンショットを以下に貼ります。
フォルダー名は黒で潰してありますがご了承ください。


スクショのうち「フォルダー設定」、つまり「ダウンロードしたファイルが入っているフォルダー」「IDごとのフォルダーが入っているフォルダー」の設定が必須になります。
IDと違うフォルダーに振り分けたい場合は、スクショで「IDと異なるフォルダーに分類する」となっている一覧に入力することで、「ID」列のIDは「フォルダー」列のフォルダーに振り分けるようになります。

【注意することとかある?】
あくまでも「俺が使うためのツール」を公開したものであるので、要望をいただいた場合は検討しますが、対応をお約束することはできません。
ライセンス(MIT)のとおり、保証とかは一切ありません。

今後はFACを作る上で困ったこととその解決法とか、そういうことも書いていきたいなと思っています。
「え、こんな簡単なモンでもそんなんあるの?」と思われるかもしれませんが、あるんだなこれが。

今日の1曲: