#pragma twice

KAB-studio > プログラミング > #pragma twice > 123 Version 7.03 アタッチとデタッチ

#pragma twice 123 Version 7.03 アタッチとデタッチ

前のページへ 表紙・目次へ 次のページへ

 Version 7.03
アタッチとデタッチ

まずは前回の復習から。1ドットの点を打つ時には

void CAnimeDlg::OnBDraw() 
{
    COLORREF dwColor = RGB( 255, 0, 0 );
    HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
    ::SetPixel( hDC, 10, 10, dwColor );
}

で、これの MFC 版

void CAnimeDlg::OnBDraw() 
{
    COLORREF dwColor = RGB( 255, 0, 0 );
    CDC *pcDC = m_cCanvasStatic.GetDC();
    pcDC->SetPixel( 10, 10, dwColor );
}

2行目と3行目、違うような似てるような……
細かく見ていこうね。まず

    HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );



    CDC *pcDC = m_cCanvasStatic.GetDC();

の違いから
んーと、上のはまず m_cCanvasStatic からウィンドウハンドルをもらっ
て、 GetDC() でそのデバイスコンテキストのハンドルをもらって、それを
hDC に入れてるんだよね
そうでした
下のは…… m_cCanvasStatic の GetDC() ってメンバ関数を呼んでて、そ
れを pcDC ってので受けてる。これ、 CDC * だからポインタだよね
そう、ポインタ。まずはこの CDC クラスについて見ておこうか
やっぱし、デバイスコンテキストを使うためのクラスなんだよね
もちろん。まず、 CDC は中に HDC 型のメンバ変数を持ってます
それって CWnd と同じ?
そゆこと。 Ver 3.8 ( No.033 ) でやったように、 CWnd は HWND を持っ
てて、中で API を呼ぶ時にこの HWND を渡します
 CDC も同じってことは、3行目の

    ::SetPixel( hDC, 10, 10, dwColor );



    pcDC->SetPixel( 10, 10, dwColor );

だと、 pcDC->SetPixel() してる中で ::SetPixel() を読んでて、その最
初の引数に、中に持ってる HDC 渡してるってことね
ま、これは見た方が早いでしょ。 pcDC->SetPixel() にブレークポイント
を置いて
実行してステップイン! afxwin1.inl ってファイルの 759 行目に……
::SetPixel() 呼んでて、第1引数に m_hDC 渡してる!
というわけで、結局は CWnd と同じく、 API を呼んでるだけってことだ



ASSERT(m_hDC != NULL);

ってゆーのがある。 ASSERT って Ver 6.17 ( No.117 ) で教えてもらっ
たのだよね
そう。これだとどうなる?
 ASSERT は if の逆だから、 m_hDC != NULL にならないときに止まるん
だよね。ってことは  m_hDC == NULL のとき、あ!  m_hDC が NULL のと
きは止まるんだ!
そゆこと。 API 呼ぶ前にそのチェックをしてるってことだね
こういう細かいチェックが必要なんだ……
さて話を戻して

    CDC *pcDC = m_cCanvasStatic.GetDC();

ここで CDC のポインタを受け取ってます。ってことは、ポインタはどっ
かの CDC 変数を指してるってこと
その指してる先の CDC が本体、ってことはその中に HDC が入ってる?
そう! API 版の

    HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );

との違いは、 m_cCanvasStatic.GetDC(); は CDC 型変数をどこかに作っ
て、その中の m_hDC にデバイスコンテキストのハンドルを入れてから、そ
のポインタを返してるんです
デバイスコンテキストのハンドルは……
それは ::GetDC( m_cCanvasStatic.GetSafeHwnd() ); で得たの
あ、そっか! そこまでは同じで、 CDC 用意してそれに入れてポインタ
を返す、っていうのが MFC 版の機能なんだ
そういうこと。なんでこんな面倒なことしてるかって言えば、全部 MFC 
でしたいから、ってとこかな
どゆこと?
もし MFC 版の GetDC() が HDC を返しちゃったら、 HDC をそのまま使う
ことになるでしょ。 MFC には CDC があるから、それを使って欲しいわけ
全部 MFC でって、そういうことなのね……
で、ここで一番重要なのは、 CDC のポインタを返してるとこ
そいやそうね、ポインタじゃなくてもよさそうだけど
この辺は CWnd の場合も同じだから、分かりやすくするために CWnd の例
を紹介するね

void CAnimeDlg::OnBDraw() 
{
    CWnd cWnd;
    cWnd.Attach( m_cCanvasStatic.GetSafeHwnd() );
}

 Attach() って前に見たことあるような……
 Ver 5.26 ( No.091 ) だね
そういえば。でも、あのときは Detach() っていうのも呼ばないとダメっ

そう、そこが味噌。とりあえず試してみて
ほい。あ、 ASSERT した
これを【無視】して
していいの?
うん、そのためのテストだから。 ASSERT が気になるならリリースビルド
で試してもいいよ
リリースビルドなら ASSERT 消えちゃうんだもんね
そゆこと。で、どうなった?
ん〜、あ! IDC_S_CANVAS がなくなってる!!
つまり、自動的に削除されちゃったんです
自動的に削除!?
ちょっと飛ぶけど、ファイル操作の時に使った std::ifstream を思い出
して
思い出し中……
ランタイムの時は fopen() で開いて fclose() で閉じてたけど、 
std::ifstream の時は閉じなくても良かったでしょ
 Ver 5.11 ( No.076 ) で言ってたね、自動的に閉じてくれるって
実は CWnd とかにもこの〈自動的に閉じてくれる〉機能があるんです
自動的に閉じる……ウィンドウが閉じる……だから消えちゃった!?
そゆこと。 CWnd::Attach() は、外のウィンドウハンドルを中の m_hWnd 
に格納するメンバ関数
うん、それは分かる
で、 CWnd には、 CWnd 型変数がなくなるときに〈 m_hWnd が指すウィン
ドウを閉じる〉って機能が付いてるんです
つまりファイル操作で fclose() を自動的にするのと同じように、ね
そういうこと。でももちろんこれはまずいわけ
まだ使ってるんだもんねぇ
そこで解決法。解決法にはふたつあって、ひとつは
 Detach() !
そのとおり。 Detach() を呼ぶと、中の m_hWnd に NULL を入れてくれ
て、自動的に削除されなくなります
それって CDC の方でも?
っていうより、 MFC の〈ハンドルを扱うクラス〉全部に言えることだ
ね。この辺はどれも共通だから
ほぉ。で、もうひとつは?
もうひとつは、ポインタを使う方法
あ、さっきの!
そう、最初に MFC 使ってた時にポインタを使ってたでしょ。これなら大
丈夫
あれ? ポインタだと、その自動的に削除ってされないの?
されないよ。たとえば

void CAnimeDlg::OnBDraw() 
{
    CDC *pcDC1 = m_cCanvasStatic.GetDC();
    CDC *pcDC2 = m_cCanvasStatic.GetDC();
    CDC *pcDC3 = m_cCanvasStatic.GetDC();
}

で、もし pcDC1 、 pcDC2 、 pcDC3 が消えるたびにそういうことが起き
たら何度も消えちゃうことになっちゃうでしょ?
そっか、ポインタは本体を指すだけだから、消えたり作られたりっていう
のと関係ないんだ
そゆこと。そういうことが起きるのは、ポインタが指す本体が消えるとき
ってことだね
ってことは……自動的に消えたりしないように、わざわざポインタを返し
てるってことなんだ
そういうことになるね。 MFC を使って〈元々存在するもののハンドル〉
を使う時とかは、こういうふうにポインタを使うことが多いかな
それに、ポインタだとなんかハンドルに似てるし
そう、それ重要
そなの?
ハンドルのシステムって、ウィンドウズの中にウィンドウとかデバイスコ
ンテキストが入ってて、それに直接触れないようにするためのもの
ハンドル使って間接的に操作するんだもんね
ポインタを使うってことも、 MFC 内部にある、ウィンドウやデバイスコ
ンテキストの管理システムに直接触れないで、ポインタを通して操作するっ
て部分で
ハンドル使うのと似てるねー
 MFC がそういう感じに似せて作られた、っていうのもあるかもね

/*
    Preview Next Story!
*/
自動的にって、便利だったり不便だったりするのねー
これ、使う側だからいいけど、作る側は大変なんだよね
自動的にって、難しいの?
ううん、それは簡単。難しいのは、便利か不便かの判断
そうよねー、場合によって便利だったり不便だったり、違うだろうし
というわけで次回
< Version 7.04 GDI とペン >
に続く!
もちろんいつかは火美ちゃんも迷う日が来るから
そういうのの方がむしろ面白そうなんじゃない?
こういうのの方がむしろ大変なんだよね……
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。