stdFontを使うとGDIオブジェクトが際限なく増える?

解決


yama  2008-08-19 21:15:45  No: 101003

VB6です。宜しくお願いします。

社内の帳票出力システムの開発を引き継いだのですが、
出力実行時にGDIオブジェクトが上限を超えてしまい、
途中で落ちてしまいます。

が、PCによっては問題なく実行でき、何が原因なのか
よく分からないのでアドバイスを頂きたいと思います。
gdi.dllのバージョンが同じでも動作が異なります。

下記がその問題を再現するためのサンプルです。

Private Sub Command1_Click()
    Dim i        As Long
    Dim ii       As Long
    Dim sFont    As StdFont

    For ii = 1 To 100
        For i = 1 To 1000
            Set sFont = New StdFont

            If i Mod 2 = 0 Then
                sFont.Name = "MS 明朝"
                sFont.Size = 15
            Else
                sFont.Name = "MS ゴシック"
                sFont.Size = 14
            End If

            Set Printer.Font = sFont

            Printer.CurrentX = 1000
            Printer.CurrentY = 1000

            Printer.Print "テスト太郎"

        Next
        Printer.NewPage
    Next
    Printer.EndDoc

End Sub

stdFontを使用しなければGDIオブジェクトの問題は解決するのですが、
例えばstdFont.Sizeに7をセットすると、実際は6.72になるなど、
その数値で帳票がデザインされているため、容易に変えられないのです。

>stdFont.Sizeに7をセットすると、実際は6.72
を再現できればそれはそれでいいのですが…

以上、宜しくお願いいたします!


やじゅ  2008-08-19 21:49:31  No: 101004

Set sFont = New StdFontをループより前で定義したらどうですか


yama  2008-08-19 23:46:15  No: 101005

やじゅ様
ありがとうございます。

>Set sFont = New StdFontをループより前で定義したらどうですか
試してみたところGDIオブジェクトが増えることなく、
どのPCでも落ちなくなりました。

が、FontSizeがこれまでと変わってしまい、出力自体がうまくいかなくなってしまいました…。
Befor) StdFont.Size = 10 をセットすると 9.719999 
After) StdFont.Size = 10 をセットすると 10.08 

そもそもこれまでの記述が間違いであればどうしようもありませんが、
上にも書いたとおり、PCによってGDIオブジェクトの増え方が違うので、
なんとかこれまでのFontSizeを維持(=ループ内で定義?)しながら
GDIオブジェクトの問題を解決したいのですが…。

しかしなぜGDIオブジェクトが際限なく増えるPCとそうでないPCがあるのでしょうか…。


yama  2008-08-20 00:34:36  No: 101006

追記ですが、
この帳票出力システムは十余年問題なく動いていて
ここ最近、落ちる落ちないの問題が出てきたようです。

引き続き宜しくお願いいたします。


やじゅ  2008-08-20 03:01:06  No: 101007

stdFontって使う必要があるのかな
Printer.FontSize = 15 とか直接では駄目なん?
http://support.microsoft.com/kb/194323/ja


yama  2008-08-20 03:12:48  No: 101008

やじゅ様
再度ありがとうございます。

仰るとおり、直接やりたいところなのですが、
既にこのやり方で数百の帳票出力のレイアウトを作成しているため、
修正した場合に膨大なチェック作業が必要になってしまうのです。

初期設計で失敗していたということだと思いますが、
GDIオブジェクトの問題はここ最近なので、それさえなければ…
と、お手上げ状態です。


yama  2008-08-20 03:14:32  No: 101009

補足ですが
上のチェック作業とはフォントサイズが変わる事による
出力物のチェック作業です。


我龍院  2008-08-20 04:14:41  No: 101010

テストコードだけでは何とも言えないので、想像で言いますが、
ある行を印刷するか又はあるページを印刷するときには、
前の状態に関係なく、
Set sFont = New StdFont    
と宣言して
前とフォント名とフォントサイズが同じでも、
フォント名とフォントサイズが同じでも、
Set Printer.Font = sFont      
と新しいインスタンスを作成していると思うのだけれど、
フォント名とフォントサイズが同じなら、前のインスタンスを使えば、
(要するにフォント名とフォントサイズを指定しない)
かなりインスタンス作成の頻度は下がるのでは。

後はメモリーを増やす位しか思いつきません。
以前はWin2Kだったが、最近は同じマシンでOSはXpに変えた
等と言う落ちは無しにしましょうね。


K.J.K.  2008-08-20 17:59:07  No: 101011

環境で結果が変わるのならば、まずはその異なるところをチェック
するべきでしょう。

gdi32.dll よりは、プリンタドライバや oleaut32.dll など、今回直接
関係がありそうなところのバージョンを調べてみるとか。

ただ、VB6以前の Printer オブジェクトはいろいろ問題も多いので、
そちらに起因しないとも限りません。
# 本当はプリンタドライバの実装に問題があるケースも多いのですが...

で、とりあえず、

Private Sub Command1_Click()
    Dim i        As Long
    Dim ii       As Long
    Dim oFontNew As Font
    Dim oFontOld As Font

    Set oFontOld = Printer.Font

    For ii = 1 To 100
        For i = 1 To 1000
            Set oFontNew = New StdFont

            If i Mod 2 = 0 Then
                oFontNew.Name = "MS 明朝"
                oFontNew.Size = 15
            Else
                oFontNew.Name = "MS ゴシック"
                oFontNew.Size = 14
            End If

            Set Printer.Font = oFontNew

            Printer.CurrentX = 1000
            Printer.CurrentY = 1000

            Printer.Print "テスト太郎"

            Set Printer.Font = oFontOld
            Printer.Print ""

            Set oFontNew = Nothing
        Next
        Printer.NewPage
    Next
    Printer.EndDoc

End Sub

としてみると、どうなります?


yama  2008-08-20 19:25:16  No: 101012

我龍院様
ありがとうございます。

確かに1帳票のフォント数は多くて20種ぐらいですので
使用フォントでソートをかければ、かなり抑えられそうですね。
数万ページの出力物もあるのですが、それに耐えられるかは…
とりあえずやってみます。
PCはXPが出た頃からXPみたいです。

K.J.K.様
ありがとうございます。

oleaut32.dllはどれも同じバージョンでした。
プリンタドライバーもやはり、同バージョンでもPCによって結果が異なります。
現在「落ちるPC」は本番稼働中ですので、頂いたコードは午後一に試してみたいと思います。

ちなみに、stdFontを使用している出力プログラムはOCXに固め、
フォームに貼って使用しています。
現行のOCXが問題ないとすると、OCXに固めるときの環境に問題があるのでしょうか。
でも、VB6でSPなし〜SP6でそれぞれOCX作ってもダメでした…。


yama  2008-08-21 00:01:15  No: 101013

K.J.K.様
頂いたコードを試しましたが、結果変わらずでした…。
只今まさかのVB5を引っ張り出してOCXを作ってみようかと思ってます。


K.J.K.  2008-08-21 08:08:49  No: 101014

これでもダメだとは思いますが、一応。

Private Sub Command1_Click()
    Dim i        As Long
    Dim ii       As Long
    Dim oFontNew As Font
    Dim oFontOld As Font
    Dim oFont As IFont
    Dim hFont As OLE_HANDLE

    Set oFontOld = Printer.Font

    For ii = 1 To 100
        For i = 1 To 1000
            Set oFontNew = New StdFont

            If i Mod 2 = 0 Then
                oFontNew.Name = "MS 明朝"
                oFontNew.Size = 15
            Else
                oFontNew.Name = "MS ゴシック"
                oFontNew.Size = 14
            End If

            Set Printer.Font = oFontNew
            Set oFont = oFontNew
            hFont = oFont.hFont

            Printer.CurrentX = 1000
            Printer.CurrentY = 1000

            Printer.Print "テスト太郎"

            Set Printer.Font = oFontOld
            Printer.Print ""

            Call oFont.ReleaseHfont(hFont)
            hFont = 0&
            Set oFont = Nothing
            Set oFontNew = Nothing
        Next
        Printer.NewPage
    Next
    Printer.EndDoc

End Sub

で、ダメになる数をチェックして、それをやや下回るぐらいの数で
動かして、実行後に正しく解放されるのか、などもチェックするとか。

関係ありそうなのは、
http://support.microsoft.com/kb/821792/en-us
辺りかもしれませんが、条件には合いませんよね。


我龍院  2008-08-21 17:44:34  No: 101015

数万ページは迫力ですね。
何も変えないのに最近は良く落ちるようになったのであれば、
ページファイルのフラグメントが問題かも知れませんね。
はじめの頃はメモリーリークが有っても、ページファイルにスワップ・アウトされ、
ぎりで間に合っていたのが、フラグメントが多くなって、間に合わなくなってきたとか。
駄目なマシンの仮想メモリをOFFってデフラグをかけて見る手も有ります。


yama  2008-08-21 19:04:49  No: 101016

K.J.K.様
ありがとうございます。
コードはまた午後からテストさせて頂きたいと思います。

リンクもありがとうございます。
ダメなPCの場合、1ページも出力できずに落ちますので
これが原因とは思えないのですがいかがでしょうか…。
実際ページ数はPCの個体差があるのですが、それぞれ
同じページ数(Printer.NewPageのタイミングで落ちる事が多い)で
GDIオブジェクトがいっぱいになってしまうのです。

我龍院様
ありがとうございます。

上記の通り、メモリを食って仮想メモリも食って(ってことですよね?)
と言う症状ではないように感じます。メモリも4G積んでいるものでも
数ページで落ちてしまいますので…。
※見当違いの事を言っていたらすいません!※

VB5の検証を他の人に頼んでますが、
今のところ落ちないかも、という報告が…


K.J.K.  2008-08-21 22:32:08  No: 101017

NewPage のタイミングで起きるとすると、プリンタドライバの不具合の
可能性が高いと思います。最新のドライバが出ていないか調べてみるとか。


yama  2008-08-22 01:31:23  No: 101018

K.J.K.様
ありがとうございます。

テストコード試してみました。が、ダメでした…。
落ちるタイミングは変わらずでしたが、
hFont = oFont.hFont
のところでOLEオートメーションエラーとなりました。
ハンドルが取れなくなってエラーって事でしょうか。
そういうの弱いもんですいません…。

プリンタドライバは常に最新のものにしています。
またOK/NGのPCでも全て同じドライバ(XEROX/RICOH/FUJITSUそれぞれ)を使用し
落ちるページとスプールサイズは同じ(厳密な調査はしていませんが…)なので
ドライバではないと考えていますがいかがでしょうか。

VB5でOCXを作る件ですが、こちらは全てのPC、プリンタの組み合わせで
落ちる事が無かったようです。これが原因とすると、Printerオブジェクトの
バージョンアップに伴い、VB6でバグが出た。或いはバグが修正されたことにより
弊社のコーディングミスが炙り出された。という事も考えられますでしょうか…。
VB5で作ってみたのは、「現行のOCXはもしかしたらVB5かも…」という
若干頼りない長老の一言からなのですが。


K.J.K.  2008-08-22 02:30:40  No: 101019

> hFont = oFont.hFont
これはよく考えてみると、

> Printer.Print "テスト太郎"
の直後でないとダメですね。
# 直後でもダメかもしれませんが。

まぁ、コーディングの問題とは考えづらいでしょう。
プリンタドライバに問題点が見つからないのならば、VB6の問題と判断する
のが良さそうです。
で、MS側にそれを訊いてるとか。


yama  2008-08-25 18:33:44  No: 101020

K.J.K.様
本当にありがとうございます。
(盆休みで返事が送れました。スミマセン)

>> Printer.Print "テスト太郎"
>の直後でないとダメですね。
># 直後でもダメかもしれませんが。
変わらずでした…。
どうにかGDIオブジェクトを開放できればいいのですが、
何も分からず実力不足を実感しています。
自分で書けるとしたら Set StdFont = Nothing くらいですが、
MSDNのサンプルにはその記述が無かったので余計に分からないですね…。

>で、MS側にそれを訊いてるとか。
VB6のサポートってもう終わってませんでしたっけ?

今のところVB5のOCXでやろうかという話に決まりつつありますが、
もう少し最後の悪あがきをしようと考えてますので、
解決のチェックはもう少し先延ばしにします。
※VB5で解決って訳ではないかもしれませんが…


K.J.K.  2008-08-25 23:27:23  No: 101021

> VB6のサポートってもう終わってませんでしたっけ?

終わっていたとしても報告と確認にはなりますし。


ぽき  2008-09-06 02:14:31  No: 101022

基本的にはやじゅさんのいう通り、Set命令をループの外にだす。

その上で、
「例えばstdFont.Sizeに7をセットすると、実際は6.72になるなど、」

が、いつ変更されているのか探したほうがいいとおもいますけど。
WithEventsとかで飛んでませんか?


higadesu  2008-09-07 10:22:25  No: 101023

StdFontを検索すると、GDIを経由して、プリンタに送っているらしいので、
  下記のメモリーの限界が影響している可能性が考えられます。
  Win2000・XPにも「GUI、フォント」のデータを管理している
「デスクトップアプリケーションヒープ」と言うのもがありますので
確認してみてはいかがですか。

http://support.microsoft.com/kb/126962/ja


yama  2008-10-14 23:28:08  No: 101024

いつか書かなくては…
と思いつつ、だいぶ時間が経ってしまいました。
申し訳ありません。

結論としてはVB5で作成したOCXを使うことになりました。

K.J.K.様
ウチはどうやらMSDNのサポートに入っていないようで
確認することができませんでした…。

ぽき様
フォントサイズは
sFont.Size = 7
Msgbox sFont.Size
で、既に7ではありません。

ループの外に出したいのは山々ですが、
上記にあるように全帳票チェックの問題がありますので、
今回は見送らせて頂きました。

higadesu様
いじってみましたが、結果は変わらずでした。
GDIオブジェクトの最大値をいじると若干寿命は延びますが、
それも時間の問題でした。

結果としてよくわかんないけどとりあえずVB5で、
と、若干後味悪いですが、とりあえず出力には問題ありませんでしたので
安心しております。

皆様には沢山のアドバイスを頂き本当にありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加