びったんびったん

ユーザビリティ・プログラミングについて。

C# で SetWindowsHookEx() を使うと Form がフリーズするのをごり押す

C# でマウスグローバルフックを使うと Form がフリーズします。
c# - SetWindowsHookEx and clicking minimize/maximize/close buttons on form freezing - Stack Overflow

C# から直接呼び出しても、 C++ で DLL を作って呼び出してもフリーズしたので C# でグローバルフックを使えるといっても Form との併用はできないのかと思っていましたがごり押しで動いてしまいました。

やり方は fork もどきをして自身のプロセスを2つ起動します。そして1つ目でフォームを作り、2つ目でグローバルフックをするです。1つ目が2つ目を起動するときに自身のハンドルをコマンドライン引数で渡して、2つ目が1つ目と通信したいときにはそのハンドルに PostMessage() します。片方が死んだときにもう片方を殺すのを忘れないでください。

あまりにごり押しなのでコードは載せません。

Undo の設計 - 元に戻す・戻さない操作の違い

例えばテキストエディタに文字を入力してから元に戻すと、入力した文字は消える。しかしテキストエディタでスクロールしてから元に戻しても、スクロールは戻らない。当たり前かな?じゃあコードの折りたたみはどうだろう?答えはどちらもで、 Visual Studio は元に戻るし、 Sublime Text は元に戻らない。つまり Sublime Text でコードを折りたたんだ後で折りたたみを解除しようとしたら、その直前に入力した文字が消えてあわててやり直す( Ctrl + Y )ことになる。大変よくない。

これはほんの一例だが元に戻す操作と元に戻さない操作の違いは不明瞭だ。この辺りをネットで調べようと思っても Undo の実装しか引っかからない(それはそれで大事なのだが)。仕方がないので自分で書く。なのでこの辺りに詳しいサイトや書籍をご存知でしたら教えてください。

元に戻す操作と元に戻さない操作の違いが明瞭になると何が嬉しいのか?

ユーザの予想が効く

文字の入力は元に戻りそうだから Ctrl + Z 、スクロールは元に戻らなさそうだから逆スクロール、のような予想の効く操作が増え、とりあえず Ctrl + Z してみて駄目だったら Ctrl + Y するような残念な UI が減る。

Undo は PC 初心者でもわりと使うショートカットなのでユーザ全体の使い勝手が向上する。馬鹿にできない。

開発が楽になる

昨今のリッチなソフトウェアにおいて Undo 機能は必須だが売りにはできない空気のようなものだ。そんなもののために元に戻すか戻さないかで悩まなくてすむ。

あれ?スマホアプリや Web サービス全盛の時代にこの話関係なくない?スマホアプリは関係ないかもしれない。しかし遠い将来、少なくない数の Web サービスが PC Twitter クライアントのようにブラウザ上ではなく OS 上で動くかたちになっているはずだ(なっていてほしい)。Web サービスを使うにはブラウザをアクティブにしてからタブを切り替えるなど2度手間だし、ブラウザに奪われてショートカットも満足に使えないし、うんざりである。Web サービスはどの OS でもどのスマホでも動くクロスプラットフォームだからって安易に量産されすぎだ!開発者の怠慢だ!

元に戻す操作と元に戻さない操作のパターン

ファイルの内容が変更される操作を元に戻せるようにする

先の例だがテキストエディタに文字を入力するとファイルの内容が変更される。しかしスクロールしても見え方(ソフトウェアの状態)が変わるだけでファイルの内容は変更されない。このパターンに従うとコードの折りたたみは元に戻らないことになるが、私は元に戻るほうが便利だと思う。

元に戻すコストが大きい操作もしくは不可逆な操作を元に戻せるようにする

文字列を Backspace 押しっぱなしでガーッと消した後に Ctrl + Z なしに元に戻すのは手間である。またコードの折りたたみも押しやすいショートカットが割り当てられていることは少なく元に戻しづらい。だから元に戻せるようにする。しかし Visual Studio の出力のクリアなどは元に戻せなかったりする。

メインコンテキスト上の操作を元に戻せるようにする

Visual Studio の出力のクリアなどを元に戻せるようにするとひとつ困ったことが起こる。それはエディタ上で Undo するとエディタが元に戻り、出力上で Undo すると出力が元に戻るため、エディタを元に戻したいのに出力上で Undo してしまうことだ。そのためメインコンテキストであるエディタだけ元に戻せるようにし一貫性を保つことでユーザが混乱しなくてすむようにする。しかし別のコンテキスト上のインプットボックスも元に戻せたりする。

ユーザが元に戻したいと思う操作を元に戻せるようにする

ここまで譲歩して都合のよい解釈をしても駄目だ。 Visual Studio の定義へ移動という機能があるがこれは Ctrl + Z では元に戻らない。しかしページ遷移専用の元に戻るとも言うべき前に戻る( Ctrl + - )で元に戻る。つまり Ctrl + Z には一定の基準がありそれにあぶれつつも元に戻したい操作には別の元に戻るコマンドを新たに作らないといけないらしい。

すべての操作を元に戻せるようにする

Ctrl + Z を連打することになってしまう。

まとめ

複雑すぎてまとめきれなかった。元に戻す操作か元に戻さない操作か迷っている方の参考なれば嬉しいです。

Windows 10 未満のコマンドプロンプトを Ctrl + V で貼り付け

Windows 10 でコマンドプロンプトが使いやすくなるみたいですね。 Windows 10 ほどではありませんが既存の Windowsコマンドプロンプトを Ctrl + V で貼り付けができるようにし、少しだけ使いやすくする方法を紹介します。

汎用キーバインディング変更ソフトである yamy を使います。

Yet Another Mado tsukai no Yuutsu プロジェクト日本語トップページ - SourceForge.JP
こちらのページからダウンロードして解凍します。インストールは不要ですので yamy.exe を実行します。

次に設定ファイルを作成して、拡張子を .mayu とする適当な名前で保存します。 # 以降の行はコメントになるので、お使いのキーボードに合わせて1行目・2行目いずれかをコメントアウトしてください。

include '109.mayu' # 日本語キーボード
# include '104.mayu' # 英語キーボード

window CommandPrompt /ConsoleWindowClass/ : Global

key C-V = A-Space E P

最後にタスクトレイにある yamy アイコンを右クリックして設定・追加・参照の順にクリックして先ほど作成した設定ファイルを指定します。これで設定は終了です。

実際にコマンドプロンプトを起動して Ctrl + V してみると貼り付けができると思います。

yamy は応用が効き様々なことができるのでおすすめです。

Form.Close() メソッドの CloseReason は UserClosing

Form アプリケーションがどのように終了する(した)のかは CloseReason で知ることができる。
CloseReason 列挙体 (System.Windows.Forms)

これの注意点として CloseReason が UserClosing になるのはウィンドウの閉じるボタンや Alt + F4 で終了するときだけではなく、 Form.Close() メソッドを呼んだときもである。

ShowWindow(hWnd, SW_RESTORE) は Visible プロパティを更新しない

やりたいこと

最小化されていてかつ非表示なフォームがある。これを表示し元のサイズに戻したい。

バグ

状況を再現する最小のコードを書いた。元のサイズに戻すことは .NET ではできないので Win32 の ShowWindow(hWnd, SW_RESTORE) 関数を使う。

class C {

    [DllImport("user32")]
    private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    private const int SW_RESTORE = 9;

    private void F() {
        var f = new Form {
            WindowState = FormWindowState.Minimized
        };
        ShowWindow(f.Handle, SW_RESTORE);

        Console.WriteLine(f.Visible); // フォームは表示されているのになぜか Visible は false
    }
}

フォームは表示されているのになぜか Visible は false になる。イミワカンナイ

なにがまずいのか

.NET がフォームをごにょごにょするときに Visible を参照して表示か非表示かを判断していることがありそこがバグる

具体的にはフォームに関連付けられたコンテキストメニューは、フォームが表示されているときにしか表示できないらしい。先のバグでフォームが表示されているにもかかわらずコンテキストメニューが表示できなかった。

じゃあどうすんの

ShowWindow(f.Handle, SW_RESTORE);
f.Show();
// f.Visible = true;

ShowWindow 関数の直後に Show メソッドを呼ぶか Visible プロパティに true をセットする。かなり気持ち悪いが、今のところ不具合は起きていない。もっとよい方法があれば教えてください。

まとめ

.NET と Win32 が混在するときには最新の注意を払いましょう。

次バージョンを決めた

自作ソフトの現バージョンは 0.3 なのだけれど次バージョンは 1.1 に決めた。

メジャーバージョンを 0.X から 1.X に上げているのは大きな機能がたくさん増えるため。

なぜ 1.0 ではなく 1.1 なのかというと 1.0 だとユーザに初公開と勘違いされるかもしれないから。初公開ではなくバージョンアップですよーというのがユーザに伝わるメリットは3つ。

  • メンテナンスしてるよーというのが伝わる
  • ユーザがたくさんいるからバージョンアップしたと思ってくれるかもしれない
  • 個人的に更新履歴を見るのって楽しい(私だけ?

Firefox 32.0 リリースノート
まったく関係ないけどブラウザって結構な速度で JavaScriptCSS の標準規格への準拠を進めてるなーと思った。

バージョン番号に最終更新日を含めるべきか?

自作ソフトの現バージョンは 0.3 と 0.3.20140712 の2つともを正式なバージョンとしている。

他のサイト様に紹介いただいてソフト一覧に並ぶことがあると思いますが、このとき紹介いただくサイト様ごとにデザインが違い最終更新日があるサイトもないサイトもありますよね。そんなときにこんな感じで適切に使い分けてくれたらなーという思いをこめたからです。

最終更新日の記載があるサイト

ソフト名 バージョン 最終更新日
ソフト v1.0 2014/xx/xx
私のソフト v0.3 2014/07/12
ソフト v1.0 2014/xx/xx

最終更新日の記載がないサイト

ソフト名 バージョン
ソフト v1.0
私のソフト v0.3.20140712
ソフト v1.0

・・・なのですがコノザマダヨ

f:id:hakomof:20140922192134p:plain

動作環境をわかりやすくするためのバージョン番号

最近では OS に限らずブラウザや IDE などにサードパーティ製のソフトウェアやアドオン、プラグインなどが増えてきました。

しかしソフトウェアの動作元のバージョンアップでソフトウェアが動かなくなることもあるので動作環境を明らかにすることは重要です。そのとき動作元が(あまり頻繁に数字が増えないような)メジャーバージョン番号をユーザにわかりやすく周知させているとよいです。

なぜかというと Windows OS のようにバージョンごとに異なる名前が振られていると動作環境の記述が冗長になるからです。以下の例は同じ動作環境の2種類の書き方です。

わかりやすいが冗長
動作環境: Windows Vista, Windows 7, Windows 8 以上

簡潔だが例えば Windows 8 ユーザには動作するかどうかが知識がないとわからない
動作環境: Windows Vista 以上

バージョンアップごとに無機質な数字ではなく名前を振りたくなる気持ちもわかりますが、メジャーバージョン番号を周知させてください。