午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

関連ウィンドウもまとめてスクショできるようにしたPixcren、キャプチャ範囲はこれで完成

Pixcren1.2.4

ダウンロード先

ここのPixcren1.2.4zip
github.com

作成動作環境

動作に必要なのは.NET 5がインストール済みのWindows


規定ファイル名を変更した

ファイル名の日時の書式を空白にしたときの書式を
yyyyMMdd_hh_mm_ss_fff
から
yyyyMMdd_hhmmss_fff
にした

f:id:gogowaten:20210216224656p:plain
日時の書式


関連ウィンドウをまとめてスクショできるようにした

キャプチャ範囲、ウィンドウ特殊1+

f:id:gogowaten:20210216175748p:plain
ウィンドウ特殊1+
キャプチャ範囲の選択でウィンドウ特殊1+を追加した

ウィンドウ特殊1+でスクショしてみる

デスクトップ画面がこの状態のとき

f:id:gogowaten:20210216175950p:plain
デスクトップ画面
エクセルのセルの書式で塗りつぶし→塗りつぶし効果→グラデーションの色1→その他の色→色の設定を開いたところ
この状態でスクショすると
f:id:gogowaten:20210216180329p:plain
ウィンドウ特殊1+でスクショ
この画像が得られる

プレビューウィンドウではこうなっている

f:id:gogowaten:20210216180525p:plain
プレビューウィンドウの状態

ウィンドウ枠外に伸びたリストウィンドウとか

デスクトップ画面

f:id:gogowaten:20210216183740p:plain
デスクトップ画面
これをウィンドウ特殊1+でスクショすると↓

f:id:gogowaten:20210216182057p:plain
Visual Studioのオプションでフォントの選択してるところをスクショ
マウスカーソルが枠外に伸びたリストウィンドウの上にあれば、途切れずにスクショできる。これはキャプチャ範囲のウィンドウ特殊1と同じ効果によるもの、それに加えて+1では関連ウィンドウもキャプチャする、ここでの関連ウィンドウはオプションウィンドウを開いたもとのVisual Studioのウィンドウになる


別のキャプチャ範囲でスクショ

ウィンドウ特殊1

f:id:gogowaten:20210216182840p:plain
キャプチャ範囲をウィンドウ特殊1でスクショ

ウィンドウ

f:id:gogowaten:20210216182951p:plain
キャプチャ範囲をウィンドウでスクショ
大抵のスクショアプリだとこうなる

ウィンドウのクライアント領域

f:id:gogowaten:20210216183128p:plain
ウィンドウのクライアント領域でスクショ

カーソル下のコントロール

f:id:gogowaten:20210216183239p:plain
カーソル下のコントロールをスクショ



関連ウィンドウのハンドル取得方法は

WinAPIのGetWindowでOwnerを使った、なので元のウィンドウのオーナーを辿っているだけ

リボンメニュー系アプリはGetForegroundWindowからGetParentからGetWindowのOwner
それ以外のアプリはGetForegroundWindowからのGetWindowのOwner


仕様という名の不具合

f:id:gogowaten:20210216211201p:plain
Visual Studioデバッグ
一時停止して配列の中を見ているところ、この状態でのスクショ結果は

f:id:gogowaten:20210216211359p:plain
スクショ結果
こうなってしまう、肝心なVisual Studioのウィンドウがキャプチャできていない
キャプチャ範囲はウィンドウ、ウィンドウ特殊1、ウィンドウ特殊1+でも、ほとんど同じで期待通りにならない

処理の途中を見てみると

f:id:gogowaten:20210216212542p:plain
キャプチャできていない
cursorが基点になるウィンドウハンドルで、マウスカーソル下のウィンドウ
foreがGetForegroundWindowで取得したウィンドウハンドル、通常は最前面ウィンドウ
これを見るとforeはカーソル下ウィンドウの1階層下のウィンドウになっている。通常ならここでアプリのウィンドウが取得されるから、この場合はVisual Studioのウィンドウが取得されるはずなんだけどねえ
じゃあどうしたらVisual Studioのウィンドウが取得できるのかなって、試しているのがその下に並ぶ変数

cParent {IntPtr(0000000000000000), Rect(0,0,0,0), 不可視, Text()}
cOwner {IntPtr(0000000000000000), Rect(0,0,0,0), 不可視, Text()}
cRoot {IntPtr(00000000001e293a), Rect(1105,623,198,116), 可視, Text()}
act {IntPtr(0000000000000000), Rect(0,0,0,0), 不可視, Text()}

上から順番にパレント、オーナー、ルートオーナー、アクティブウィンドウ
カーソル下ウィンドウを基点にこれだけ試したけど、どれもVisual Studioのウィンドウは取得できず
さらにGetForegroundWindowを基点にしても取得できなかった
ってことでお手上げ、仕様にしよう

こうなってしまう条件は、ブレークポイントで一時停止中に、コードの上にマウスカーソルを乗せると出てくるポップアップウィンドウの中に、虫眼鏡アイコンがあって、その横の▼をクリックしてドロップダウンリストを表示しているとき、かな、他にもあるかもしれないけどかなり限定的だと思う

f:id:gogowaten:20210216214657p:plain
ここまでなら正常にキャプチャできる
ここまではいいけど、▼からのドロップダウンリスト表示してしまうと

f:id:gogowaten:20210216214844p:plain
これじゃない
こうなってしまう
他のスクショアプリもいくつか試したけど、まともに撮れるものはなかったから、かなり特殊なウィンドウみたいねえ


完走した感想

今回で考えていたキャプチャ範囲は全部できた
難しかったのはアプリのウィンドウ枠外に伸びたメニューウィンドウや、右クリックメニューのキャプチャ
普通系アプリの右クリックメニューウィンドウを取得するWinAPIは見つけられなかったので、スクショするときはマウスカーソルをメニューウィンドウに乗せている前提にして、マウスカーソル下のウィンドウを取得するWinAPIで対処
エクセルの同時に2枚出てくる右クリックメニューは無理かなあと思っていたけど、指定ウィンドウの1階層下のウィンドウを取得するGetWindow+NEXTを繰り返して下層ウィンドウを集めて、その中からGetForegroundWindowのルートオーナーと同じルートオーナーを持つウィンドウを選別するという強引な方法と、ドロップシャドウ専用のウィンドウをサイズで判断するとかでなんとかできて、このときは嬉しかった
キャプチャ範囲に関しては予想よりも良くできたので、これで完成!
あとは、プレビューウィンドウをもっとプレビューっぽい動作にしたいのと、画像サイズを縮小して保存できるようにしたい


関連記事
次回は4日後

gogowaten.hatenablog.com

前回のWPF記事は昨日

gogowaten.hatenablog.com

最初のPixcrenは50日前
gogowaten.hatenablog.com