SelectObjectのはたらきを確認するには?

解決


たんとん  2005-11-27 06:57:30  No: 128275

こんばんは。

hOld = SelectObject(Picture1.hdc, hbr)
Call Rectangle(Picture1.hdc,10,10,20,20)
Call SelectObject(Picture1.hdc, hOld)
Call DeleteObject(hbr)

1行目と3行目がよくわかりません。APIの解説に、「直前のオブジェクトの
ハンドルが戻る」とあるのですが、直前もなにも、いま初めて、ピクチャ1に
渡したブラシの直前のブラシがあるはずもなく、3行目で、あるはずもない
hOldをピクチャ1に渡して、どうするのだろうかと・・・。
しかも、3行目は省略しても、なにも問題はなく、そのあと、別のイベントで
いきなり、Call Rectangleしても、問題なく描画できました。
1行目りhOldと3行目がないことで、どんな問題があるのでしょうか。
4行目のメモリがもったいないというのは、わかりました。


あん  2005-11-27 07:25:27  No: 128276

Picture1を貼り付けた時点で、
デフォルトのウィンドウハンドルが作成されます。
ですので
>あるはずもない
>hOldをピクチャ1に渡して、どうするのだろうかと・・・。
hOldはデフォルトのウインドウハンドルに
戻してることになります。

C言語とかで1からWindowsアプリケーションを
作成していると理解しやすいんですが
今はほとんど必要ないからなあ。


ガッ  2005-11-27 07:35:25  No: 128277

> あん さん
ウィンドウハンドルは訂正した方がええんとちゃ?


たんとん  2005-11-27 08:15:57  No: 128278

あんさん、お返事ありがとうございます。

そうですかぁ、デフォルトのハンドルがあるのですね。

では、3行目で、そんな昔のハンドルをまたピクチャに渡すのは、

どうしてなのでしょうか。ピクチャには、すでにソリッドブラシのハンドルが

渡っているのに(^_^;


たんとん  2005-11-27 08:22:15  No: 128279

しかも、3行目で古いハンドルをピクチャに渡すと、
1行目でピクチャに渡したソリッドブラシのハンドルは
どうなってしまうんでしょうか。

ピクチャ1は、何本でもブラシを持てるっていうような意味合いなんですかね。
でも、そんなことしたら、どのブラシで描くのか、コンピュータが迷いますかね。

わけがわからなくなってきました(^_^;


ガッ  2005-11-27 08:40:04  No: 128280

※一般的な意見として書かせていただきます。

> では、3行目で、そんな昔のハンドルをまたピクチャに渡すのは、
> どうしてなのでしょうか。
MSDNには「An application should always replace a new object with the original..」と書かれているので、
「すべき」であって、強制ではないように思います。
なので、別にしなくてもいいのでは?
(でもDeleteObject()するときは、SelectObject()で元のオブジェクトに挿し戻してDeleteObject()した方がいい気がする)

> しかも、3行目で古いハンドルをピクチャに渡すと、
> 1行目でピクチャに渡したソリッドブラシのハンドルは
> どうなってしまうんでしょうか。
"hbr"は存在し続けます。

> ピクチャ1は、何本でもブラシを持てるっていうような意味合いなんですかね。
Picture1のhDCは、SelectObject()によって自分に関連付けられたブラシを一つ知っているだけです。

> でも、そんなことしたら、どのブラシで描くのか、コンピュータが迷いますかね。
迷いませんかと。

※最近DIBSection関係をまとめたので、齧っただけの知識でスミマセン(orz


たんとん  2005-11-27 09:28:07  No: 128281

ガッさん、ありがとうございます。

おっしゃるとおり、古いものを置き換えなくても動作するので、
気にしないでも良かったのですが、性格的に「なんでだろう・・・」と
思い出すと止まらないもので。

あと、ブラシを何本もは、わたしの勘違いでした。
CreateSolidBrushやCreateHatchBrushなど何本も並べて
動作させてみたところ、最後に渡したもので描かれるだけなので、
1本しか持ってないですよね。
最初のプログラムでも、古いものから新しいものに持ち替えただけだから1本。
勘違いしました。

何行か描いたプログラムを消したり描いたりしているうちに
また疑問が出てきたのですが、

hOld = SelectObject(Picture1.hdc, hbr)
Call Rectangle(Picture1.hdc,10,10,20,20)
Call DeleteObject(hbr)

いちばん上の3行目を消して4行目は残したところ、ブラシが消去されたのに、
このあと、別のイベントで、Rectangleは正常に動作します。

つまり、hbrは、メモリ上から消えても、ピクチャがhbrを持っている。
ピクチャにhbrを渡してしまえば、メモリ上のオブジェクトを消しても、
なんら問題ないということですよね。ピクチャはhbrをもったまま。
そうか、関係ないのか。。。

ああ、なんか、書いているうちに、意味がわかってきました。

ありがとうございました。


abu  2005-11-27 10:55:57  No: 128282

>つまり、hbrは、メモリ上から消えても、ピクチャがhbrを持っている。
>ピクチャにhbrを渡してしまえば、メモリ上のオブジェクトを消しても、
>なんら問題ないということですよね。ピクチャはhbrをもったまま。

この解釈は少々誤解しているようです。
DeleteObjectのSDKの戻り値の説明をみると

関数が正常に終了した場合は、 TRUEを返します。
指定されたハンドルが有効でない場合や現在デバイス コンテキストに
選択されている場合は、 FALSEを返します。

と書かれています。
よって選択中のオブジェクトはDeleteObjectで消えていないということ
じゃないでしょうか。


たんとん  2005-11-27 17:25:36  No: 128283

abuさんお返事ありがとうございます。

おっしゃるとおり、勝手にわかった気になって、
誤解しているように思っていました。
解決にしてしまったので、読んでいただけないかもしれませんが。

たしかに、DeleteObjectで消えていないから、
このあと、何度Rectangleを呼び出しても使えるように思います。

ですが、いったん
hbr=CreateSolidBrush(...)をやってしまうと、
そのあとに、
x=DeleteObject(hbr)の戻り値は常にx=1(TRUE)なんです。
何回DeleteOjbect(hbr)をしたとしても、x=1なんです。

ということは、削除しました、正常に終了しましたということを
VBは返しているのですよね。

abuさんの書いてくださったように、
「指定されたハンドルが有効でない場合や
現在デバイス コンテキストに選択されている場合は、 FALSEを返す」
のだとしたら、

わたしの戻り値は、
「ハンドルは無効で、かつ、現在デバイスコンテキストに選択されてない
ものを消した」ということを意味しているのでしょうか。
つまり、DeleteObjectは、いらなくなったhbrしか消さない。

下のように書いてみたのですが、何度DeleteObjectをしても1が返ります。

x=DeleteObject(hbr)
y=DeleteObject(hbr)
z=DeleteObject(hbr)

x=text1.text
y=text2.text
z=text3.text

これを合理的に解釈するとしたら、「メモリ上に、これ以上消すものが
なくなったら1を返す」とか、「一度hbrを呼ぶと、最後の一本のhbrは消えず、
常にメモリ上に残るから、常に1が返る」というように考えるしかないの
でしょうか。

いらなくなったhbrを消すためにDeleteObjectがあるのであって、
選択されているものは消さない。
そして、選択されているものしかない場合は、
消せはしないが、消したことにする(1を返す)というふうにしているのかなぁ・・・

すいません、お時間ありましたら、もう少しアドバイスいただけませんか。


ガッ  2005-11-27 18:49:53  No: 128284

実験してみました。
Option Explicit
'Declare は省略
'Private Declare Function CreatePen
'Private Declare Function DeleteObject
'Private Declare Function SelectObject

Private hPen    As Long
Private hOldPen As Long

Private Sub Form_Load()
    '1  Pen を作成し、選択する
    hPen = CreatePen(0, 5, &HC0C0C0)
    hOldPen = SelectObject(Me.hdc, hPen)
    Debug.Print "hPen="; hPen, "hOldPen="; hOldPen
    
    '2  SelectObject で挿し戻さずに、DeleteObject する
    Debug.Print "DeleteObject"; DeleteObject(hPen)
    
    '3  SelectObject で挿し戻さずに、DeleteObject する
    Debug.Print "DeleteObject"; DeleteObject(hPen)
    
    '4  SelectObject で hOldPen に挿し戻す
    Debug.Print "SelectObject:"; SelectObject(Me.hdc, hOldPen)
    
    '5  DeleteObject する
    Debug.Print "DeleteObject"; DeleteObject(hPen)
    
    '6  DeleteObject する
    Debug.Print "DeleteObject"; DeleteObject(hPen)
End Sub

結果(イミディエイトウィンドウに表示されたもの):
SelectObject
hPen=-2110778441            hOldPen=-282061970 
DeleteObject 1 
DeleteObject 0 
SelectObject:-2110778441 
DeleteObject 0 
DeleteObject 0 

えーとですね。
SelectObject()に関連付けられた"ペン"は、削除できるみたいです。
MSDNには
「Do not delete a drawing object (pen or brush) while it is still selected into a device context.」
と書かれているので、してはならないことのようですが…
不思議ですねぇ。


たんとん  2005-11-28 03:21:28  No: 128285

ガッさん、お返事ありがとうございます。
大変参考になりました。

ガっさんの実験方法でいろいろとモニタしてみたところ、
以下の結論に達しました。

hPen1 = CreatePen(0,5,RGB(100,150,200))
hPen2 = CreatePen(0,5,RGB(200,150,100))

Call SelectObject(Picture1.hdc, hPen1)  
選択されただけの、この時点ではhPen1は削除可能

Call Rectangle(Picture1.hdc, 50, 50, 100, 100)
描いた時点で、hPen1は削除できなくなる

Call SelectObject(Picture1.hdc, hPen2)
hPen2を渡してはいるが、この時点では、前のhPen1は削除できず、
渡しただけのhPen2は削除可能

Call Rectangle(Picture1.hdc, 100, 100, 150, 150)
hPen2で描いた時点で、hPen2は削除できなくなり、
hPen1はここで手放され、削除可能になる。

Debug.Print "DeleteOject"; DeleteObject(hPen1)
Debug.Print "DeleteOject"; DeleteObject(hPen1)

Debug.Print "DeleteOject"; DeleteObject(hPen2)
Debug.Print "DeleteOject"; DeleteObject(hPen2)

要するに、SelectObjectが渡したオブジェクトは、
デバイスコンテキストがそれを使った時点で、削除できなくなる。
削除できないものを、DeleteObjectで削除しようとしても、
常に1が返ってくる。

とりあえず、納得できました。ありがとうございました。


たんとん  2005-11-28 03:29:04  No: 128286

あと、もうひとつ、ガッさんに書いて頂いた

An application should always replace a new object with the original

の意味もわかったような気がします(訳せたとかいうのではなく)

置き換えをすることによって、「選択」状態になり、
削除可能にしておくということですよね。

置き換えをしないと、デバイスコントキストが持っているオブジェクトは
削除できないままなので。

「使用中」と「選択中」をきちんと区別して、
使い終わったら、「使用中」から「選択中」に戻して、
いつでも削除できるようにしておこうという意味で良いでしょうか。

かさねまして、ありがとうございました。


たんとん  2005-11-28 03:39:41  No: 128287

いやぁ、前に書いたこと、やっぱり変ですね。
最初の質問の4行目が意味のないものになってしまう・・・

また考えてみます。すいません。


たんとん  2005-11-28 09:48:32  No: 128288

アドバイス頂いたみなさま、ありがとうごさいます。
結局、以下の結論になりました

hPen = CreatePen(0,5,RGB(100,150,200))

hOldPen = SelectedObject(Picture1.hdc, hPen)
hPenの直前のペンを保存

Call Rectangle(Picture1.hdc,10,10,20,20)    
ここでhPenが使用中になり削除できなくなる。

Call SelectObject(Picture1.hdc,hOldPen)
描画がおわったので、もとのペンに戻しておく。ただし、hOldPenは「選択中」
であって削除可能。「使用中」なのはhPenのままで削除できない。

     ***

DeleteObject(hPen)    このDeleteObjectは、このままでは意味がない。
                        (削除できないhPenを削除しようとしているから)
                        では、なぜここにhPenの削除を書いているか。
                        それは、***に別の描画プログラム、描画関数が
                        あることを想定している。
                        その場合、その描画関数を使用した瞬間にhPenは
                        開放され、削除可能な状態になる。使用しないhPenが
                        削除されずに残っていてもしょうがない。よって、
                        最下行にDeleteObjectを書いておく。

アドバイス頂いたみなさま、ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加