投稿者 魔界の仮面弁士  (社会人) 投稿日時 2021/12/29 10:21:05
Rectangle するには、枠線のための「ペンオブジェクト」と
塗り潰し領域のための「ブラシオブジェクト」が必要となります。
枠線のみが必要な場合は、空ブラシ(NULL_BRUSH / HOLLOW_BRUSH)を割り当てます。

現状、最初に白背景で塗りつぶした後、ループ処理中のブラシは何が割り当てられていますか?
先の回答でも述べましたが、SelectObject で割り当ててある状態のペンやブラシを DeleteObject していないかも再確認しましょう。


> MyBrush = GetStockObject(WHITE_BRUSH)
CreateSolidBrush や CreateBrushIndirect で【作成】したブラシは DeleteObject が必須ですが、
GetStockObject で【取得】した定義済み(ストック)ブラシは DeleteObject すべきではありません。
(実際の所、後者を削除しても害は無いですが…)


> 直してみました
> Call SelectObject(MyDC1, MyPen)
> Call Rectangle(MyDC1, f2, f1, f2 + 3, f1 + 3)
> Call SelectObject(MyDC1, MyPen)
> DeleteObject (MyPen)
肝心の復元処理が無いままですよ。

まず、SelectObject の戻り値が描画オブジェクトのハンドルであることは御存知ですよね?
(HBITMAP, HBRUSH, HFONT, HPEN など)

それが分かっているからこそ、Long ではなく LongPtr に書き直したのだと思いますが、
であれば Call ステートメントではなく、戻り値を受け取って処理すべきではないでしょうか。

Rectangle や DeleteObject の戻り値は成否を表す BOOL なので、失敗の恐れが無い場合は
Call ステートメントを使うのも分かりますが…SelectObject の場合は戻り値も必要かと。

言語は違いますが、下記の WM_PAINT の処理を見てください。
http://kaitei.net/winapi/pens-brushes/

抜粋するとこんな感じです。
// ペンとブラシを選択
hpenPrev = (HPEN) SelectObject(hdc, hpen);
hbrPrev = (HBRUSH) SelectObject(hdc, hbr);

Rectangle(hdc, 50, 50, 200, 150);

// 元のペンとブラシを選択
SelectObject(hdc, hpenPrev);
SelectObject(hdc, hbrPrev);


snowmansnow  さんが書き直したコードでは、上記の処理が
 SelectObject(hdc, hpen);
 SelectObject(hdc, hbr);
 Rectangle(hdc, 50, 50, 200, 150);
 SelectObject(hdc, hpen);
 SelectObject(hdc, hbr);
になってしまっています。これでは復元処理になりません。
先に紹介した URL と今回の URL の内容を確認しつつ、もう一度コードを見直してみましょう。


> DeleteObject (MyPen)
修正前のコードでは Call DeleteObject(MyPen) で
修正後のコードでは DeleteObject (MyPen) なのは何故ですか?
Call ステートメントの表記揺れが気にかかりました。

そもそも Call 表記に揃えるというのなら、rs.MoveNext メソッドも Call を付けるものかと
思いましたが、もしかして、API にのみあえて Call を付けているということなのでしょうか。

いずれにせよ修正後の方は、括弧の記述が不自然です。
現状の『DeleteObject (MyPen)』という記述を Call 記法に書き換えた場合、
『Call DeleteObject((MyPen))』という意味になってしまいます。

『DeleteObject MyPen』と記述すれば、Call 記法で言うところの
『Call DeleteObject(MyPen)』相当の記述になります。

Call を使うか否かはお任せしますが、引数一つの場合の括弧の付与については
Call 無しでは「DeleteObject MyPen」
Call 付きでは「Call DeleteObject(MyPen)」としましょう。


>> そもそも、ループ中で何度も MyPen を作り直している理由が分からない…。
> 直せません・・・
今回紹介した URL では
 ①初期処理(WM_CREATE メッセージ)の段階で描画オブジェクトを作成
 ②描画処理(WM_PAINT メッセージ) ではそれらを使って Rectangle を呼び出して描く
 ③終了処理(WM_DESTROY メッセージ)では使用済みのペンやブラシを処分
という流れになっていますよね。

今回のプログラムも同様で、
 ①ループ前(Do Until rs.EOF より前)の段階で描画オブジェクトを作っておく
 ②ループ中(Do~Loop 内)では、Rectangle で描画
 ③ループ後(Loop より後)では、使用済みの描画オブジェクトを処分
という流れにするということです。
オブジェクトの生成・破棄や SelectObject を、ループ内で毎回行う必要があるとは思えなかったので。


> もっと多いデータだと、実行途中ではエラーは出ませんが、
描画元の BMP ファイルや、描画用の CSV などの具体的なデータがあるわけではないため、
回答側としても手軽に検証することができません。
(具体的にどういう結果になることを望んでいるのか、朧気にしか想像できていません)

何行目のどのデータの時に問題が出ているのかまで、具体的に追跡できますか?

・塗りつぶしブラシを明示的に指定してみた場合はどうなるか?
・1 レコードも処理しない状態でも問題が起きるのか?
・最初の 1 レコード目の描画時点で、既に問題が起きているのか?
・特定のレコードまでは問題が無く、あるところで急に黒くなるのか?
・問題が生じるタイミングは毎回同じなのか? それとも異なるのか?
・問題が生じ始めるタイミングが同じ場合、その時点の描画データの具体的な座標値は?