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

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

新規登録して質問してみよう
ただいま回答率
86.02%
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

解決済

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

Kosho9513
Kosho9513

総合スコア4

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グッド

1クリップ

324閲覧

投稿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を押しています

以下のような質問にはグッドを送りましょう

  • 質問内容が明確
  • 自分も答えを知りたい
  • 質問者以外のユーザにも役立つ

グッドが多くついた質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

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

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

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

下記のような質問は推奨されていません。

  • 間違っている
  • 質問になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

適切な質問に修正を依頼しましょう。

回答1

0

ベストアンサー

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

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

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

投稿2022/11/04 06:13

fana

総合スコア10672

良いと思った回答にはグッドを送りましょう。
グッドが多くついた回答ほどページの上位に表示されるので、他の人が素晴らしい回答を見つけやすくなります。

下記のような回答は推奨されていません。

  • 間違っている回答
  • 質問の回答になっていない投稿
  • スパムや攻撃的な表現を用いた投稿

このような回答には修正を依頼しましょう。

回答へのコメント

fana

2022/11/04 06:18

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

2022/11/04 06:30

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

2022/11/04 07:07

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

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

ただいまの回答率
86.02%

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

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

質問する

関連した質問

同じタグがついた質問を見る

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です。