質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

Q&A

解決済

1回答

1615閲覧

GDIオブジェクト解放時のDeleteObjectとSelectObjectの順番

Kosho9513

総合スコア6

MFC

MFC (Microsoft Fouondation Class)とは、MicrosoftがVC++用に開発したWindows用アプリケーションのフレームワークです。

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

Win32 API

Win32 APIはMicrosoft Windowsの32bitプロセッサのOSで動作するAPIです。

1グッド

1クリップ

投稿2022/11/04 05:50

疑問

現在MFCでWindowsアプリケーションを開発している者です。

Win32APIの公式ドキュメントで、::DeleteObjectのページに

指定したハンドルが有効でない場合、または現在 DC に選択されている場合、戻り値は 0 です。

描画オブジェクト(ペンまたはブラシ)は、DC で選択されている間は削除しないでください。

と記述がありました。
https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-deleteobject

MFCにおいてもそれは同様と思い、GDIオブジェクトを解放するときはCDC::SelectObject → CGdiObject::DeleteObjectの順番で呼ぶようにしていました(DeleteObjectはCGdiObjectのデストラクタで呼ばれることも認識しています)。
ところが、この順番でなくても正しくGDIオブジェクトは解放されていることに気づき、疑問が生まれ調べましたが、有効な手掛かりが得られず、質問に至っています。

以下に詳細を書いていきます。

実験用のソースコード

C++

1// View.cpp 2 3// MFCのクラスを用いた実験 4void ResourceLeakTest1(CDC& dc); 5 6//Win32APIを用いた実験 7void ResourceLeakTest2(CDC& dc); 8 9static const int penNumber = 5000; 10 11// DeleteObjectを自分で呼ぶためstatic変数 12static CPen pens[penNumber]; 13 14void MyView::OnDraw(CDC* pDC) 15{ 16 ResourceLeakTest1(*pDC); 17 ResourceLeakTest2(*pDC); 18} 19 20void ResourceLeakTest1(CDC& dc) 21{ 22 for (int index = 0; index < penNumber; index++) 23 { 24 pens[index].CreatePen(PS_SOLID, 0, RGB(200, 200, 0)); 25 CPen* oldPen = dc.SelectObject(&pens[index]); 26 27 // 適当にたくさん円を描く 28 dc.Ellipse(CRect(CPoint(10 + index * 2, 10 + index * 2), CPoint(30 + index * 2, 30 + index * 2))); 29 30 // SelectObjectより先にDeleteObjectを呼んでみる。 31 BOOL result = pens[index].DeleteObject(); 32 33 // 予想では、resultはFALSEになり、ASSERTに引っ掛かる。 34 // 仮に引っ掛からなかったとしても、この関数を抜けたときに 35 // 5000個のGDIオブジェクトのリソースリークが起きている。 36 ASSERT(result != FALSE); 37 38 dc.SelectObject(oldPen); 39 } 40} 41 42static HPEN hPens[penNumber]; 43 44void ResourceLeakTest2(CDC& dc) 45{ 46 HDC hdc = dc.GetSafeHdc(); 47 for (int index = 0; index < penNumber; index++) 48 { 49 hPens[index] = ::CreatePen(PS_SOLID, 5, RGB(255, 0, 0)); 50 HPEN hOldPen = (HPEN)::SelectObject(hdc, hPens[index]); 51 52 // 適当に直線をたくさん描く 53 ::MoveToEx(hdc, 100 + index * 2, 10, NULL); 54 ::LineTo(hdc, 10, 100); 55 56 // 先ほどと同様 57 BOOL result = ::DeleteObject(hPens[index]); 58 ASSERT(result != 0); 59 60 ::SelectObject(hdc, hOldPen); 61 } 62} 63 64

実際に起こっていること

GDIオブジェクトのリソースリークは、タスクマネージャーの詳細タブにある「GDIオブジェクト」列で確認しています。

ResourceLeakTest1は、MFCのクラスを用いた実験です。
ソースコード中のコメントに書いた自分の予想に反して、ASSERTに引っ掛からず(DeleteObjectは1を返している)、GDIオブジェクトのリソースリークも起きていませんでした(penNumberの数だけGDIオブジェクトが増えなかった)。

ResourceLeakTest2はWin32APIを使った実験です。こちらも、同様の結果となりました。

なお、ソースコード中のDeleteObjectをコメントアウトすると、当たり前ですがGDIオブジェクトがpenNumberの数だけ(今回は5000個)増えました。

教えていただきたいこと

なぜ、リソースリークが起きないのでしょうか。
また、公式にこのことについて言及している記事があれば教えてください。

よろしくお願いいたします。

fanaを押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

完全に信用に足る情報とは言えませんが…

ここの1つ目の回答が同じような実験をしており,2つ目の回答がそれに関する話をしている様子です.(公式にドキュメントされてはいないけれども,あまりにも多くの人がここでミスるのでちょっとした対策が講じられている…という話っぽい? ただし示されているリンク先がForbiddenで見れないみたいです)

https://stackoverflow.com/questions/8500137/

投稿2022/11/04 06:13

fana

総合スコア11658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2022/11/04 06:18

本当に削除が遅延されるような仕組みになっているのかもしれませんが,(残念ながら)それが正式なドキュメントに書かれていない以上,我々があえてそのことを前提とした実装を行う理由はないので,その意味で「どうでもいい」話でしかない,と思います.
Kosho9513

2022/11/04 06:30

ご回答いただきありがとうございます。 リンク先のやりとりを読みました。Forbiddenの記事もぜひ読んでみたかったです。。。 公式ドキュメントに書いてあることと実際の動きが異なることが気持ち悪くて、なぜなのか知りたかったのですが、fana様がおっしゃるように、実際の実装では公式で推奨されていない方法をあえて選択しないようにします。
Kosho9513

2022/11/04 07:07

ありがとうございます。 おそらくこれだと思います! 英語が苦手なので、じっくり読んでみます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問