tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板
VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板
[ツリー表示へ]  [ワード検索]  [Home]

タイトル Re^3: GetDIbitsの使用方法について
投稿日: 2014/03/16(Sun) 18:22
投稿者魔界の仮面弁士
こういう場合は、API 宣言も含めて記述するようにしていただけるとありがたいです。

その方が、API 宣言そのものに問題があったときにも指摘できますし(今回は大丈夫だと思いますが)、
何より今のままだと、回答者は自分で API 宣言を用意しないと実行できないことになってしまいます。
それでは、『現象を再現可能なコード』の提示にはなりませんので…。


> Dim bmpinfo As BITMAPINFO
>     bmpinfo.biBitCount = 32
>     bmpinfo.biHeight = .Controls(PIC).ScaleHeight
>     bmpinfo.biPlanes = 1
>     bmpinfo.biSize = 40
>     bmpinfo.biWidth = .Controls(PIC).ScaleWidth

このユーザー定義型は、正確には「BITMAPINFO 構造体」では無いですね。
内包する BITMAPINFOHEADER 構造体が展開して宣言されているので。



>> GetDC API で得た hDC を使うのではなく、PictureBox1.hDC に対して SelectObject するのなら、
>> (AutoRedraw = True なときのみ)非ゼロを返してきましたが…。
> hbm = CreateCompatibleBitmap(.Controls(PIC).hdc, .Controls(PIC).ScaleWidth, _
>       .Controls(PIC).ScaleHeight)

やはり、PictureBox の hDC プロパティを使っておられたのですね。
これでは、デバイスコンテキストの意味が変わってきますよ。
(ちなみに、AutoRedraw プロパティは何にセットされていますか?)


No15857 では、「GetDC」で得た DC に処理しようとしていたのに、
今回の No15862 では、「hDC プロパティ」で得た DC に対して
処理しようとしています。そもそもの描画対象が異なっているわけです。

GetDC 関数から得られる値が、何のデバイスコンテキストハンドルなのかを確認してみましょう。


ということで、Frame に対する描画の件の前に、まずはそのために重要となる
DC に関する認識について確認していきます。

まずは、PictureBox.hDC についてです。
AutoRedraw = True の場合は、メモリデバイスコンテキストのハンドルですし、
AutoRedraw = False の場合は、ディスプレイデバイスコンテキストのハンドルです。
この違いは認識されていますか?


AutoRedraw = False の場合は、WM_PAINT への応答として Paint イベントが発生します。
AutoRedraw = True の場合は、WM_PAINT への応答は自動的に行われるため、
Paint イベントは発生しません。

なので下記の場合、Picture2 のみに描画が行われます。

'=======================
' Sample 1:Paint イベントでの描画処理
'-----------------------
Option Explicit
Private Sub Form_Load()
    Picture1.AutoRedraw = True
    Picture2.AutoRedraw = False
    Picture1.ScaleMode = vbPixels
    Picture2.ScaleMode = vbPixels
End Sub
Private Sub Picture1_Paint()
    Picture1.PaintPicture Me.Icon, 0, 0
End Sub
Private Sub Picture2_Paint()
    Picture2.PaintPicture Me.Icon, 0, 0
End Sub
' Picture1: 再描画×
' Picture2: 再描画○
'-----------------------

ここまでは宜しいでしょうか。


このように、Paint イベントでの再描画の機会は与えられませんが、
いずれも「VB の標準命令」での描画はどちらも可能です。

たとえば下記のコードでは、両方の PictureBox に描画されます。

'=======================
' Sample 2:標準命令での描画処理
'-----------------------
Option Explicit
Private Sub Form_Load()
    Picture1.AutoRedraw = True
    Picture2.AutoRedraw = False
    Picture1.ScaleMode = vbPixels
    Picture2.ScaleMode = vbPixels
End Sub

Private Sub Command1_Click()
    Picture1.PaintPicture Me.Icon, 0, 0
    Picture2.PaintPicture Me.Icon, 0, 0
End Sub
' Picture1: 描画○ 再描画○
' Picture2: 描画○ 再描画×
'-----------------------

ただし Picture2 は継続表示属性を持たないため、
フォームを最小化してから元に戻すなどした場合には
再描画の機会が無いために、描画結果が失われることになります。


さて、VB の標準命令(Line、PaintPicture、Print、Circle、PSet…)を
使うのではなく、API での描画になると、また動作が異なってきます。

それでは、hDC プロパティに対する描画をみてみましょう。

'=======================
' Sample 3:API による hDC プロパティへの描画処理
'-----------------------
Option Explicit
Private Declare Function DrawIcon Lib "user32" _
    (ByVal hDC As OLE_HANDLE, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal hIcon As OLE_HANDLE) As Long
Private Sub Form_Load()
    Picture1.AutoRedraw = True
    Picture2.AutoRedraw = False
    Picture1.ScaleMode = vbPixels
    Picture2.ScaleMode = vbPixels
End Sub

Private Sub Command1_Click()
    DrawIcon Picture1.hDC, 0, 0, Me.Icon.Handle
    DrawIcon Picture2.hDC, 0, 0, Me.Icon.Handle
End Sub
' Picture1: 描画× 再描画○
' Picture2: 描画○ 再描画×
'-----------------------

ボタンを押した直後は、Picture2 にしか描画されません。
しかし、再描画が発生したタイミングでは、
Picture1 の方は消えて、Picture2 の方のみ描画されます。
(PictureBox の Refresh メソッドを呼び出した場合や、
 PictureBox の Image プロパティにアクセスした場合も再描画されます)


これの動作の違いの理由は分かりますか?

Picture1 のほうは、メモリDC への描画です。
Picture2 のほうは、画面DC への描画です。

API で描画した場合、VBの標準命令を使った場合とは異なり
VB自身は、メモリDC への描画結果を画面に反映させねばならないことを
知りません。その結果、Picture1 への描画が遅れてしまいます。

Picture2 については、メモリDCに対してではなく、画面に直接
描画していますので、VB 側が認識しているかどうかに関係なく、
直ちに描画されます。しかし、AutoRedraw による自動再描画属性を
与えておらず、また、Paint イベントも未実装のため、再描画はなされません。



長くなりましたが、ここまでが前提知識。ここから本題です。

hDC プロパティではなく、GetDC API を使った場合はどうでしょうか?

'=======================
' Sample 4:GetDC で得たハンドルへの描画処理
'-----------------------
Option Explicit
Private Declare Function GetDC Lib "user32" _
    (ByVal hWnd As OLE_HANDLE) As OLE_HANDLE
Private Declare Function ReleaseDC Lib "user32" _
    (ByVal hWnd As OLE_HANDLE, _
    ByVal hDC As OLE_HANDLE) As Long
Private Declare Function DrawIcon Lib "user32" _
    (ByVal hDC As OLE_HANDLE, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal hIcon As OLE_HANDLE) As Long
Private Sub Form_Load()
    Picture1.AutoRedraw = True
    Picture2.AutoRedraw = False
    Picture1.ScaleMode = vbPixels
    Picture2.ScaleMode = vbPixels
End Sub

Private Sub Command1_Click()
    Dim h1 As OLE_HANDLE, h2 As OLE_HANDLE
    h1 = GetDC(Picture1.hWnd)
    h2 = GetDC(Picture2.hWnd)

    DrawIcon h1, 0, 0, Me.Icon.Handle
    DrawIcon h2, 0, 0, Me.Icon.Handle

    ReleaseDC Picture1.hWnd, h1
    ReleaseDC Picture2.hWnd, h2
End Sub
' Picture1: 描画○ 再描画×
' Picture2: 描画○ 再描画×
'-----------------------

どちらも、Sample3 の Picture2 と同じ動きになりますよね?

GetDC が返すのは、画面に直結したキャンバスである、
「ディスプレイデバイスコンテキストのハンドル」だからです。
http://msdn.microsoft.com/ja-jp/library/cc428664.aspx

Picture1 が管理しているメモリデバイスコンテキストのハンドルに対しては
描画していませんので、再描画の機会は与えられません。


ここまでの段階で、認識にズレはありますか?

- 関連一覧ツリー をクリックするとツリー全体を一括表示します)

古いスレッドにレスはつけられません。