none
CMemDCのルーチン RRS feed

  • 質問

  • CMemDCのソースを見ていて疑問に感じることがあります。
    CWnd*が引数になっている方のコンストラクタを見ると、

        m_rect.right += pWnd->GetScrollPos(SB_HORZ);
        m_rect.bottom += pWnd->GetScrollPos(SB_VERT);

    と、ウィンドウ自身のスクロール位置の値を使って、領域を右下に広げています。
    m_rect.leftとm_rect.topは0のままです。

    これはどのような意図なのでしょうか。
    GetScrollPos()の値がピクセル単位である根拠もないですし、
    スクロール位置によっては、数万ピクセルのメモリビットマップを作りにいってしまいます。
    そもそもクライアント領域以上に広げる必要性があるのでしょうか。

    よろしくお願いいたします。

    2019年7月25日 4:40

回答

  • >>これってちゃんと描画されるものなのでしょうか。
    >やってみてはどうでしょう。裏バッファ用メモリーDCは大昔に自作してしまっているので、
    >CMemDCを試してみる気になりません。あしからず。

    Vista以降には、BeginBufferedPaint()やEndBufferedPaint()があって、
    CMemDCはそれを優先しているので、CRectで領域を部分的に指定しても問題なさそうですが、
    XPではメモリビットマップのほうが使われ、やはり正しく描画されませんでした。

    https://www.codeproject.com/Articles/1236711/CMFCEditBrowseCtrl-without-flickering
    上のサンプルもCRectで領域を部分的に指定しているものですが、XPでは正しく描画できていませんでした。

    スクロールバーの問題も含め、基本的にCMemDCは、
    CRectを引数に取る方でGetClientRect()全体を指定するパターンしか、
    正しく動作しなさそうです。
    2019年7月26日 1:31

すべての返信

  • おっしゃるように「スクロール位置」はピクセルとは無関係なので、確かに無意味ですね。

    「オーナーCWndのクライアント矩形」の「right」 += 「水平スクロール位置」
    「オーナーCWndのクライアント矩形」の「bottom」 += 「垂直スクロール位置」
    な矩形で、コンパチブルなビットマップを作成しています。
    しかし、当然意味はありませんね。

    さて、ここからは邪推なのですが、もし意味があるとすると、

    「スクロールバーの位置で拡大率(など)を指定する」

    つもりだったのかもしれません。
    で、ふと思い出すのは大昔のMSさんの開発IDEにはデスクトップを拡大するための
    Zoomin.exeというツールアプリケーションがありました。
    これはスクロールバーで拡大率を変更できたような気がします。
    これの残骸か痕跡なのかもしれません(まぁ茶飲み話しだと思って聞き流してください)。


    2019年7月25日 7:06
  • > しかし、当然意味はありませんね。

    回答ありがとうございます。
    意味がないだけならよいのですが、前述の通り、
    場合によっては数万ピクセルのメモリビットマップを作りにいってしまいます。
    これだとスクロールバーを持つウィンドウの画面描画には使えないですよね。

    ちなみに、CRectを指定する方のCMemDCも見てみたところ、
    GetClientRect()全体を指定して描画する分には大丈夫そうです。

    ただ、領域を部分的に指定する使い方だと、
    その領域のサイズでCreateCompatibleBitmap()が呼ばれていますが、
    その後の描画は、元々のデバイスコンテキストに対する座標のはずです。
    これってちゃんと描画されるものなのでしょうか。
    2019年7月25日 9:29
  • 手元のコードですと、

    (1) _AfxBeginBufferedPaint(・・m_rect・・);

    に成功すると

    (2) m_bmp.CreateCompatibleBitmap(・・m_rect・・);のルートは通りません。

    ちなみに、(1)に渡す矩形の幅に、50000程度を与えると失敗しますが、10000程度ならOKでした。
    (1)に失敗すると、(2)にも失敗する様なので、落ちはしないようです。

    つまり、CWndのスクロールレンジを大きな値にして、バー位置を最大値側にした状態で、
    CMemDC( CDC&, CWnd*)で構築すると、原因不明で描画できない(又はアサートする)
    状態になるのかもしれません。

    ということで、実害はそれほどでもないという事なのかもしれませんね。
    まぁバグですけど。

    >これってちゃんと描画されるものなのでしょうか。

    やってみてはどうでしょう。裏バッファ用メモリーDCは大昔に自作してしまっているので、
    CMemDCを試してみる気になりません。あしからず。

    2019年7月25日 10:37
  • >>これってちゃんと描画されるものなのでしょうか。
    >やってみてはどうでしょう。裏バッファ用メモリーDCは大昔に自作してしまっているので、
    >CMemDCを試してみる気になりません。あしからず。

    Vista以降には、BeginBufferedPaint()やEndBufferedPaint()があって、
    CMemDCはそれを優先しているので、CRectで領域を部分的に指定しても問題なさそうですが、
    XPではメモリビットマップのほうが使われ、やはり正しく描画されませんでした。

    https://www.codeproject.com/Articles/1236711/CMFCEditBrowseCtrl-without-flickering
    上のサンプルもCRectで領域を部分的に指定しているものですが、XPでは正しく描画できていませんでした。

    スクロールバーの問題も含め、基本的にCMemDCは、
    CRectを引数に取る方でGetClientRect()全体を指定するパターンしか、
    正しく動作しなさそうです。
    2019年7月26日 1:31
  • > 回答の候補に設定 Haruka6002Microsoft contingent staff, Moderator 2019年7月30日 0:46

    見逃していました。失礼しました。
    上の自分の見解を回答とさせていただきます。

    ただ、このままだと、
    CMemDCはバグを持っていて使えないとなってしまいますので、
    今からでも修正を検討いただけると嬉しいです。

    CWndを指定する方は、GetScrollPos()の値を参照する意味は無いはずですし、
    広い範囲を取り得るスクロールバーを持つクラスに対しては使えません。

    また、CRectを指定する方は、XPなどでは正しく機能しません。
    (SetWindowOrg()などを使った座標変換が必要?)

    2019年8月20日 6:21
  • https://docs.microsoft.com/ja-jp/cpp/mfc/reference/internal-classes?view=vs-2019
    The following classes are used internally in MFC. For completeness, this section describes these internal classes, but they are not intended to be used directly in your code.
    ... CMemDC Class ...
    なので、治りませんね。きっと。

    jzkey


    • 回答の候補に設定 jzkey 2019年8月21日 0:05
    • 回答の候補の設定解除 jzkey 2019年8月21日 0:05
    • 編集済み jzkey 2019年8月21日 0:07 コメント追加
    2019年8月21日 0:05
  • > they are not intended to be used directly in your code.

    公式サンプルのMSMoneyDemoの中で、CMemDCが使われているんですけどね…。

    確かに、スクロールバーを持たないクラスの中でthisを渡しているだけなので、
    今回の問題は起こらない使い方ではありますが。

    2019年8月21日 6:33