none
ウィンドウの表示されていない部分の画像を取ってくる RRS feed

  • 質問

  • 例えばブラウザのウィンドウに対して、CreatePolyPolygonRgn等により(100,100)から(150,150)の部分のみを表示させます。

    この時、例えばajax等により一定時間後に非表示領域のを変化させます。

    そして自分のプログラムのウィンドウ上に、(150,150)から(200,200)の領域に本来表示されているデータを、ウィンドウの表示領域は変化させずに取得し表示したいと思っています。

    Windows7ではタスクバーで、非表示状態のウィンドウの現在の状態を確認できたので、人間の目に見せずに描画された状態を取得すると行ったことは可能だと思うのですがどうでしょうか。


    2012年9月18日 15:50

回答

  • Windows 7 テーマが有効の場合は DWM(Desktop Window Manager) が有効である、その逆は DWM が無効であると考えてください。
    DWM 無効の時は、隠れているウィンドウの描画を取得することはできません。(一つのキャンバスをすべてのアプリで共有しているようなイメージと言えるので、誰かが上書きした、描かなかったところは見ることができません)
    逆に、DWM 有効時は隠れているウィンドウの描画も、DWM が保持しているので取得することができます。
    この辺、私の認識が間違っているかもしれないので、DWM について調査してください。

    で、私の裏付けのない予想としては、リージョンで切り抜かれた領域は、ウィンドウの描画上不要とみなして、DWM のサーフェスにも載らないのでは?このために、取得できないというのは筋が通ると思っています。
    ウィンドウ表示上不要なデータを DWM が覚えておくのも無駄なので、省かれていると考えて、実験結果を読んでみると、私は納得できてしまいました。。。

    // 裏付けがないので参考レベル。


    2012年9月19日 14:38
    モデレータ

すべての返信

  • 思いたアイデアの全体の実現可能性について何の試みもしないまま
    他の人に質問してみるのは、一般にはあまりほめられた行為ではない、
    との認識をもっていますので、親切な回答にはなりません。
    具体的にどこまでやってみたのかを説明してみてはどうでしょう。
    とりあえず、

    1.他ウインドウのキャプチャ画像を取得するのにはPrintWindow()を使うのが便利です。

    くらいでしょうか。

    2012年9月19日 1:34
  • すみません…

    現状では対象となるウィンドウハンドルを取得して、

    そのウィンドウに対して(250, 0)から(350, 200)までの四角形領域のみをCreatePolyPolygonRgnによって表示、

    またそのウィンドウの(50,50)から(150,150)と(150,150)から(250,250)までの画素をStretchBltで取得し、

    自分のプログラムのウィンドウ上に表示するといった事をしています。

    hdc = BeginPaint(hWnd, &paint);
    SetStretchBltMode(hdc , COLORONCOLOR);
    StretchBlt(
    hdc, 0, 0, width, height,
    GetWindowDC(check_hwnd), x, y, width , height , SRCCOPY
    );
    EndPaint(hWnd, &paint);

    CreatePolyPolygonRgnを実行しない場合はStretchBltの対象部分をコピーし、表示する事が出来ていますが、

    実行してStretchBltの対象部分を非表示にすると、非表示部分をコピー出来ず、その位置にある下のウィンドウやデスクトップが表示されてしまいます。

    また,CreatePolyPolygonRgnで表示部分されている部分に対してStretchBltを実行するとコピーされているため、正常にデスクトップのその領域がコピーされているのだと思います。

    ちなみにCreatePolyPolygonRgnを実行するとウィンドウの枠が残ってしまうので、Windowsのテーマはクラシックで行っています。

    テーマをWindows 7に変更すると非表示の部分の画像は取れるのですが、例えばキーボード入力などでその部分を書き込んでも取れる画像が更新されず、CreatePolyPolygonRgnを解除しないと初期状態が表示され続けます。

    おっしゃられていたPrintWindowを使ってみましたが、こちらの場合は非表示領域に他のウィンドウは入らず、表示領域は表示されるのですが、非表示領域は真っ黒に塗りつぶされてしまいます。

    このため,この関数も非表示領域は取得できないようです。


    2012年9月19日 8:01
  • Windows 7 テーマが有効の場合は DWM(Desktop Window Manager) が有効である、その逆は DWM が無効であると考えてください。
    DWM 無効の時は、隠れているウィンドウの描画を取得することはできません。(一つのキャンバスをすべてのアプリで共有しているようなイメージと言えるので、誰かが上書きした、描かなかったところは見ることができません)
    逆に、DWM 有効時は隠れているウィンドウの描画も、DWM が保持しているので取得することができます。
    この辺、私の認識が間違っているかもしれないので、DWM について調査してください。

    で、私の裏付けのない予想としては、リージョンで切り抜かれた領域は、ウィンドウの描画上不要とみなして、DWM のサーフェスにも載らないのでは?このために、取得できないというのは筋が通ると思っています。
    ウィンドウ表示上不要なデータを DWM が覚えておくのも無駄なので、省かれていると考えて、実験結果を読んでみると、私は納得できてしまいました。。。

    // 裏付けがないので参考レベル。


    2012年9月19日 14:38
    モデレータ
  • なるほど…DWMというのがあるのですね、初めて知りました。

    確かに普通に考えると、切り抜かれた部分はリージョン指定を再び行わない限りは絶対に表示されないので、省いて処理をするというのは正しそうですね。

    とりあえずDWMについて詳しく調べてみることにします。どうもありがとうございました。

    2012年9月20日 5:07
  • >おっしゃられていたPrintWindowを使ってみましたが、
    >こちらの場合は非表示領域に他のウィンドウは入らず、
    >表示領域は表示されるのですが、非表示領域は真っ黒に塗りつぶされてしまいます

    そうですか。

    Xpでやってみましたが、他のウインドウに隠れた部分も完全に描画してくれました。
    当たり前ですが、ウインドウのひとつも無いデスクトップも取れますし。

    渡すHDC(たぶんメモリーHDCになると思いますが)の起源となるHWNDを、
    コピーターゲットのHWNDにしてしまう(笑)等の根本的な誤りがないかぎり、
    正しく動作するはずだと考えます。
    こんな基本的な部分でWindows7と動作が異なるなど、ありえないとの認識です。

    2012年9月20日 6:08
  • 手元の Windows 7 環境で実験する限り、リージョンを適用していると切り抜いている箇所は PrintWindow であっても描画されませんでした。(DWN ON/OFF 問わず)
    Windows XP の実験環境が手元にないので再現性は見れていませんが、リージョンの有無か、OS の違いかで差があると思います。

    なお、リージョンなしの場合、隠れていようがいまいが、PrintWindow でとれることは DWM ON/OFF 問わず、確認しています。

    2012年9月20日 13:36
    モデレータ
  • お手数をかけて申し訳ありませんでした。
    なるほど、了解しました。
    そうなると、リージョン(クリップリージョン)のかけ方しだいということになるのでしょうか。
    ウインドウ(HWND)に対して独自リージョンを設定するのは、最終的にデスクトップへの
    描画に依存する又はその目的のみに最適化される可能性が残されます。
    互換性の範囲で目的を実現するには、やはり、一度全てを描画域(HDC)に取得してから
    必要部分をトリミングする方法しか残されていないのかもしれないとの印象を持ちました。
    2012年9月21日 0:42
  • 誤解があるとよくないので、私の手元で再現を試みた事例でのスクリーンショットを掲載します。
    私がとらえているリージョンとは、SetWindowRgn のことです。(SelectClipRgn ではないので、HDC に対する操作と言われて違和感がありました)


    このスクリーンショットは、Window Region 適用前に一度 PrintWindow で取得し、Window Region 適用後に PrintWindow で再取得した結果です。
    対象ウィンドウに2つのエディットボックスを配置し、タイマーで同じ数字をセットしていますが、2 度目の PrintWindow ではその部分がとれませんでした。
    「更新されない」と blue_wind_ さんが言われている状態を再現できているのかなととらえていますが、見落としなどあればご指摘いただけると助かります。

    2012年9月21日 14:32
    モデレータ
  • 認識は一致しています。

    HWNDを引数とする「リージョンを設定する関数」がSetWindowRgn()であることは
    自明と判断して省略しました。すみません。

    自分の発言はその件ではなく、
    PrintWindow( HWND hwnd, HDC hdc, UINT flg)の第2引数のhdcのことでした。
    HDCの有効領域は、そのHDC作成時に、起源となったHWNDの描画有効領域に依存します。
    従ってこのhdcはhwnd(ターゲットであるHWND)を基にして作成してはいけないとの意見です。
    このように作成した場合、作成したHDCは最初から基のHWNDの更新無効領域の影響を受けるとの認識です。

    従って、当該hdcは、hwndをキャプチャして表示しようとしている自作アプリの(View等の)
    HWNDから作成されるべきで、かつそれは無効領域が無いことを要求されます。
    又は、いわゆるメモリーHDCや、いわゆるプリンターHDC等、
    HWNDへの依存関係が切り離されたHDCであることが必要ではないか
    という意味の発言でした。

    また、DWMは「物理的な画面(ディスプレイデバイス)」に対するオフスクリーンバッファ
    であるとの認識です。これは原理的に、HWNDベースで管理する手法以外は思い浮かびません。
    従って、HWNDに非依存なHDCに対してはその存在すらわからないはずだと想像します。
    なぜなら、それらのHDCにはデバイスに依存しないビットマップが関連付けられており、
    ディスプレイデバイスとは本質的に無関係だからです。

    さて、自分としては、この手順で当該ウインドは、PrintWindow()実行時の最新の
    画面を描画してくれるとの認識をもっています(XPでしかテストできませんあしからず)。
    従って、Azuleanさんの実験結果が、本手順に相似するものであった場合、
    本件の解決は非常に深刻な状態に陥っているとの感想を持っています。

    2012年9月24日 1:35
  • 認識は一致しています。

    杞憂でしたか、こちらこそ失礼しました。

    又は、いわゆるメモリーHDCや、いわゆるプリンターHDC等、
    HWNDへの依存関係が切り離されたHDCであることが必要ではないか
    という意味の発言でした。

    手元のキャプチャのためのテストコードは手抜きで .NET で作っています。Bitmap クラスを引数に Graphics を作り、その Graphics から GetHdc を使っていますので、メモリ DC 相当と考えて問題ないと考えています。

    同じプログラムを XP Mode で試せば「何か違いが見いだせるのでは?」と思って手元で試したら、Win7 と同じ感じでした。(黒くはなりますが)
    XP Mode って動きが違うかもしれないので参考になりませんが…。

    2012年9月24日 14:34
    モデレータ
  • 気になったので、ちょっとだけ意見を・・・。

    ウィンドウにリージョンを適用してればリージョン外は描画対象外なので、その領域を PrintWindow で描画しないのは当然なのではないでしょうか?

    というか、非表示領域をどういう風に描画したい(して欲しい)のかが判らないですね・・・。

    2012年9月25日 12:12
  • というか、非表示領域をどういう風に描画したい(して欲しい)のかが判らないですね・・・。

    たぶん、こちらのスレッドで言われていたことか、その延長戦のことをやろうとしているのではないかなぁと、私は思っています。
    http://social.msdn.microsoft.com/Forums/ja-JP/vcgeneralja/thread/27f20953-d7f0-4133-ba5e-51395a5097ca

    どんなデザインになるのかはわかりませんが…。

    2012年9月25日 13:21
    モデレータ
  • なるほど、前スレッドの延長線で考えると、非表示領域(ウィンドウリージョン外)の描画内容が取得したいということですか・・・。

    Win7 タスクバーの状態は非表示といっても、ウィンドウ領域は描画できる状態だから状況が違いますね・・・、Windows の描画機構でリージョン外の描画はできないと考えてますから API では無理じゃないでしょうか?

    リージョン外を描画させるような API は記憶にありません。

    前スレッドのような事が行いたいなら、自前でメインウィンドウ描画を全てメモリビットマップで行って分割展開で描画するしかないんじゃないですかね?

    ---------------------------------------------------------------------------------------------------

    リージョンをはずして PrintWindow 直後にリージョンを戻すという逃げを思い付いたので....。

    しかし、重そう、結局無理、とかいう可能性たかそうですね。

    • 編集済み kyano30 2012年9月27日 9:54 追記
    2012年9月27日 9:30
  • いつの間にかこんなについてる…

    返事が遅くなってしまい申し訳ありませんでした.

    やろうとしていたことはAzuleanさんがおっしゃっているように,前回のスレッドで言っている,任意のウィンドウの一部だけを切り抜き,それの位置関係を変更可能にするといったことです.

    とりあえず普通のやり方では不可能らしく,かつ今回は動かす時間も短く対象パソコンが決まっていたため,デスクトップの外側に持って行って,スクリーンショットで現在の状態を撮影し,それを自分のウィンドウに表示させるというかなり重い処理によって実現させました.

    自分のウィンドウに対してのイベントを元のウィンドウに送ってあげる事で,使う上では元のウィンドウと一部がそこに表示されているように見せています.

    2012年10月17日 7:37