最近ほんと何もやる気が出なくてこまるなー。だいたいオフトゥン強いせい。
今週はRuby会議行くからそれでやる気出るといいね。木金のどっちかは仕事行かざるをえないのが残念だが、木曜、土曜は行けそうなので。
結局盛った仕様にした。 ダウンロードボタン押したらアプリ内でダウンロードして、ボタンが「終了してインストール」に変わるのでそれ押したらインストーラ起動しつつ自分は死ぬって寸法だ。
同じボタンを何度も押すと効果が違うってのは作るのめんどいし、作り的にもよろしくないんだがやってしまった。 async/awaitを使えば上手くいくんじゃないかとか思ったけど、ボタンが押されるのを待つっていうawaitが作れなくて1、まあそういうもんじゃないよねってことで諦めた。普通に自分でステートマシン作った。
ファイルのダウンロード先とかどうしようと考えて、ユーザのダウンロードフォルダのサーバ上のファイル名そのままで落とすことにした。インストーラだったら一時ディレクトリ、zipだったら保存ファイル名指定させるかと思ったんだけど、指定とかめんどいし、一時ディレクトリに保存したインストーラは消すタイミングが無いので、諦めて勝手に適当なユーザが見えるフォルダに置いておいた方がいいと判断した。TortoiseGitなんかもそんな感じだったし。
ユーザのダウンロードディレクトリの取得方法を調べたが、Environment.GetFolderPathで取れそう……取れない!?何故かダウンロードフォルダはSpecialFolder列挙体に入ってない。XPでは無いからだろうか。.NET4.5はVista以降なんだし追加してほしかった。
何か方法は無いかと調べてみるがStackOverflowでもユーザのホームディレクトリ取得してDownloadsをくっつけろとかいう頭の悪い話しか出てこねぇ。ダウンロードフォルダの位置はユーザが変更できるから!それだめだから!他にもレジストリのここに書いてあるよとかいまいちなのは出てくるが……。
仕方ないので自前でシェルの関数叩くか。Shell32.dllのSHGetKnownFolderPathで取れるのは調べてあったのでそれをP/Invokeで呼ぶ。
internal static class Shell
{
public enum KnownFolder {
Downloads,
}
private static Guid FOLDERID_Downloads = new Guid("{374DE290-123F-4565-9164-39C4925E467B}");
[DllImport("Shell32.dll", PreserveSig=false)]
private static extern void SHGetKnownFolderPath(ref Guid refid, uint flags, IntPtr htoken, out IntPtr path);
public static string GetKnownFolder(KnownFolder folder)
{
Guid id;
switch (folder) {
case KnownFolder.Downloads:
default:
id = FOLDERID_Downloads;
break;
}
IntPtr path_ptr;
SHGetKnownFolderPath(ref id, 0, IntPtr.Zero, out path_ptr);
var path = Marshal.PtrToStringUni(path_ptr);
Marshal.FreeCoTaskMem(path_ptr);
return path;
}
}
こんな感じになった。PreserveSigをfalseにするとHRESULTは勝手に例外に変換して投げるって話だけど本当なんだろうか。P/Invoke難しすぎなんよ。 とにかくこれで何の問題もなく取れたので大丈夫だろう。
ダウンロードはできるようになったのでダウンロード状況表示のためにProgressBarを追加したらなぜかスッと落ちるようになってしまった。デバッグ実行してるのにデバッガにもひっかからん。出力を見てもりとなんかAccess Violationで死んだとか書いてある。C#でAccess Violationとか難しいんだが。ていうかダイアログ表示しただけで落ちるとかどう考えてもWPF内部の問題じゃないですか!
何が悪いのか調べたらProgressBar.Valueプロパティへのバインディングだった。getしかないProgressプロパティを作ってそこにバインドしてたんだが、ModeをOneWayにしてないために書き込めないからって落ちてたようだ。なにそれおかしすぎるよ。 ProgressBarのValueがコントロール側から更新されるわけないから標準でOneWayでいいはずなのに、RangeBaseからそのまま継承してるためにTwoWayが既定値になってるようだ。そこまでは理解できるとしても、書き込めないプロパティにTwoWayでバインドしちゃってた場合にAccess Violationで落ちるのはおかしいでしょ!いつもは普通の例外が出て落ちるでしょ!再現可能なバグなのか環境依存の問題なのか調べておいた方がいいかなぁ。ちゃんとModeをOneWayにすることで問題なく動くようにはなったが。
終了してインストールだが、これは普通にインストーラ起動して終了するだけでよかろう。 しかしインストーラの場合はそれでいいんだが、zipの場合はどうすりゃいいんだ。何もしないとか勝手に開くだとかいろいろ考えたが、選択された状態でエクスプローラを開くあたりが妥当かな。zipの展開って人によっていろいろやり方あるだろうし。
指定したファイルを選択した状態でエクスプローラを開く方法を調べるとexplorer.exeに/selectオプションを指定するといいらしい。なるほどということでexplorer.exe /select "hoge.zip"
などとやっても上手くいかない。なんでだろうと思ったらexplorer.exeのオプションは空白じゃなくてカンマ区切りらしい。explorer.exe /select,"hoge.zip"
で上手く動いた。なんでこんな仕様なんだ?Program Filesに空白入れちゃったから空白区切りじゃないようにしたのかなぁ。だとしたら本当に頭悪い仕様だが。
これで一通り実装できるものはしたなーと思ったんだけどまだ気になるところが。 なんかGUIのメインウィンドウにアップデートチェックのメニュー追加するの忘れてた。 あと、アップデートチェックの結果がステータスアイコンの通知としか出てこないのが大変めんどい。 自動チェックの結果はそれでいいんだが、明示的にチェックした場合はウィンドウに出てほしいなぁ。 どこにどう出すか考え物だが、バージョン情報ダイアログにつっこむのがよさそうな気がしたな。FirefoxとかOperaみたいな感じで。 その方向で考えるかー。
作ればできるけど、さすがに不自然すぎる ↩