【C#】Windowsでデスクトップアイコンの矩形領域を取得する

Pocket

「やりたくなったのでやりました」という脈絡のないネタです。オリジナリティはそんなに無いです。

もくじ

  1. やったこと
  2. コード置き場
  3. 仕組み
  4. 注意点
  5. 参考

1. やったこと

本記事では、下記のツイートでやっていることの一部を解説します。

 

上記の動画では、プログラムからデスクトップアイコンの座標領域を読み取って使用しています1。この処理を実現するには、デスクトップ上の各アイコンについてアイコン名および領域(位置と高さ、幅)を拾ってくる必要があります。本記事では、これらのデータを拾ってくる方法を紹介します。

2. コード置き場

以下の通りGitHubに置いてあります。ライセンスは考えるのが面倒なのでPublic Domainです。

3. 仕組み

置いたソースのうち、DesktopIconGetter.csがメインなので、このソースを上から追っていきます。基本的にWin32APIをひたすら叩いて進みます。

3.1. 初期化

ここでやっている処理は以下の三つです。

  • 「デスクトップアイコン一覧」のウィンドウを探しに行ってウィンドウハンドルを取得
  • プロセスにアクセスするためにオープン
  • プロセスとメモリのやり取りするために領域を確保

オープンしたプロセスのハンドルとプロセス用メモリは、最終的に解放しないといけないので、
今回の例ではIDisposableインターフェースを実装して対応しています。

また、上記のコードで使っている”Progman”や”SHELLDLL_DefView”といった文字列(ウィンドウクラス名)が
一体どっから出てきたのかという事ですが、これはinspect.exeでウィンドウツリーを見ると確認できます。
inspect.exeの利用経験がない場合は、下記とかを参考に導入してください。

今回扱ってるデスクトップアイコンへ至るウィンドウ階層はこう。

  • デスクトップ
    • “Program Manager”ウィンドウ
      • “”ウィンドウ
        • “デスクトップ”一覧
          • アイコン0
          • アイコン1
          • アイコン2
          • アイコン3

inspect.exeでこんな感じに見えていれば想定通りです。

3.2. アイコンの個数を取得

これはAPIに任せきりなので「こう書いてね」としか言えません。
しいて言えばLVM_GETITEMCOUNTのページにそう書いてあります、という程度です。

3.3. アイコン一覧の取得

APIの仕様上、アイコン名とアイコンの領域は別々のサブルーチンに分けています。
まずアイコン名の取得方法から。

やってる作業は3ステップで、個別に見ればとくに難しいことはしていません。
(個人的に見慣れないAPIがバンバン出てくるので緊張はしますが。)

  • アイコン名を書き込ませるための文字列へのポインタを用意
  • SendMessageを用いて、アイコン名を含めたデータの書き込みを要求
  • 書き込まれたデータのうち、アイコン名の部分だけ読み込む

アイコン名が取得できたら、今度はアイコンの領域取得です。

こちらではアイコン名のときとメモリの読み書き位置が少し変わってますが、コンセプトは同じです。

注意としてはLVM_GETITEMRECTの説明にあるように、あらかじめ「この範囲の境界をちょうだい」というオプション値をRectのLeftに入れて渡す必要があります。この指定値が正しく入っていないと、SendMessageをしても境界のデータを書き込んでもらえなくなります。

4. 注意点

4.1. 動かないこともある

64bitなPCに対しては、コンパイル時に64bitの構成でビルドしないとダメみたいです。
理由はよくわかってませんが、なんかコケます。

また、64bitでビルドしてあってもたまにコケます
私の環境ではコケた状態でinspect.exeを使って確認したところ、
そもそもウィンドウ階層が想定と違う形式になっていました。
スクショを撮り忘れてしまったのですが、たしかウィンドウ階層はこのようになっていました。

  • デスクトップ
    • “”ウィンドウ (想定外: “Progman”ではなく無名のウィンドウクラスになっている)
      • “”ウィンドウ
        • “デスクトップ”一覧 (
          • アイコン0
          • アイコン1
          • アイコン2
          • アイコン3

こうなってしまった場合にアイコン一覧をうまく拾う方法はよく分かっていません。
手元のマシンの場合、この状態になってからWindowsを再起動したら元の(サンプルが動く)状態へ戻っていました。

4.2. アイコン名とファイルパスの対応づけ

アイコン名はほぼファイル名と同義であるため、フルパスも基本的にはすぐ得られます。

ただしアイコンがショートカットである場合、上記のパスの末尾へさらに拡張子.lnkを追加したものが実際のファイルパスとなります。
デスクトップにはファイルそのものではなくショートカットが置かれるケースが多いので、要注意です。

4.3. Unityでやる場合

GitHubの例にもありますが、Unityでこの処理をする場合DllImportに拡張子なしのファイル名を渡す必要があります。
NativeMethodでは該当部分を#ifdefで切り替えてそれらしくしてあります。

5. 参考


  1. 動画中では視線トラッキングという全く別の事もやっていますが、視線トラッキングについては別の記事に書きます。 

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です