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 とペン >
『に続く!』
「もちろんいつかは火美ちゃんも迷う日が来るから」
『そういうのの方がむしろ面白そうなんじゃない?』
「こういうのの方がむしろ大変なんだよね……」