#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のトップページをご覧ください。