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
を再現できればそれはそれでいいのですが…
以上、宜しくお願いいたします!
Set sFont = New StdFontをループより前で定義したらどうですか
やじゅ様
ありがとうございます。
>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があるのでしょうか…。
追記ですが、
この帳票出力システムは十余年問題なく動いていて
ここ最近、落ちる落ちないの問題が出てきたようです。
引き続き宜しくお願いいたします。
stdFontって使う必要があるのかな
Printer.FontSize = 15 とか直接では駄目なん?
http://support.microsoft.com/kb/194323/ja
やじゅ様
再度ありがとうございます。
仰るとおり、直接やりたいところなのですが、
既にこのやり方で数百の帳票出力のレイアウトを作成しているため、
修正した場合に膨大なチェック作業が必要になってしまうのです。
初期設計で失敗していたということだと思いますが、
GDIオブジェクトの問題はここ最近なので、それさえなければ…
と、お手上げ状態です。
補足ですが
上のチェック作業とはフォントサイズが変わる事による
出力物のチェック作業です。
テストコードだけでは何とも言えないので、想像で言いますが、
ある行を印刷するか又はあるページを印刷するときには、
前の状態に関係なく、
Set sFont = New StdFont
と宣言して
前とフォント名とフォントサイズが同じでも、
フォント名とフォントサイズが同じでも、
Set Printer.Font = sFont
と新しいインスタンスを作成していると思うのだけれど、
フォント名とフォントサイズが同じなら、前のインスタンスを使えば、
(要するにフォント名とフォントサイズを指定しない)
かなりインスタンス作成の頻度は下がるのでは。
後はメモリーを増やす位しか思いつきません。
以前はWin2Kだったが、最近は同じマシンでOSはXpに変えた
等と言う落ちは無しにしましょうね。
環境で結果が変わるのならば、まずはその異なるところをチェック
するべきでしょう。
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
としてみると、どうなります?
我龍院様
ありがとうございます。
確かに1帳票のフォント数は多くて20種ぐらいですので
使用フォントでソートをかければ、かなり抑えられそうですね。
数万ページの出力物もあるのですが、それに耐えられるかは…
とりあえずやってみます。
PCはXPが出た頃からXPみたいです。
K.J.K.様
ありがとうございます。
oleaut32.dllはどれも同じバージョンでした。
プリンタドライバーもやはり、同バージョンでもPCによって結果が異なります。
現在「落ちるPC」は本番稼働中ですので、頂いたコードは午後一に試してみたいと思います。
ちなみに、stdFontを使用している出力プログラムはOCXに固め、
フォームに貼って使用しています。
現行のOCXが問題ないとすると、OCXに固めるときの環境に問題があるのでしょうか。
でも、VB6でSPなし〜SP6でそれぞれOCX作ってもダメでした…。
K.J.K.様
頂いたコードを試しましたが、結果変わらずでした…。
只今まさかのVB5を引っ張り出してOCXを作ってみようかと思ってます。
これでもダメだとは思いますが、一応。
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
辺りかもしれませんが、条件には合いませんよね。
数万ページは迫力ですね。
何も変えないのに最近は良く落ちるようになったのであれば、
ページファイルのフラグメントが問題かも知れませんね。
はじめの頃はメモリーリークが有っても、ページファイルにスワップ・アウトされ、
ぎりで間に合っていたのが、フラグメントが多くなって、間に合わなくなってきたとか。
駄目なマシンの仮想メモリをOFFってデフラグをかけて見る手も有ります。
K.J.K.様
ありがとうございます。
コードはまた午後からテストさせて頂きたいと思います。
リンクもありがとうございます。
ダメなPCの場合、1ページも出力できずに落ちますので
これが原因とは思えないのですがいかがでしょうか…。
実際ページ数はPCの個体差があるのですが、それぞれ
同じページ数(Printer.NewPageのタイミングで落ちる事が多い)で
GDIオブジェクトがいっぱいになってしまうのです。
我龍院様
ありがとうございます。
上記の通り、メモリを食って仮想メモリも食って(ってことですよね?)
と言う症状ではないように感じます。メモリも4G積んでいるものでも
数ページで落ちてしまいますので…。
※見当違いの事を言っていたらすいません!※
VB5の検証を他の人に頼んでますが、
今のところ落ちないかも、という報告が…
NewPage のタイミングで起きるとすると、プリンタドライバの不具合の
可能性が高いと思います。最新のドライバが出ていないか調べてみるとか。
K.J.K.様
ありがとうございます。
テストコード試してみました。が、ダメでした…。
落ちるタイミングは変わらずでしたが、
hFont = oFont.hFont
のところでOLEオートメーションエラーとなりました。
ハンドルが取れなくなってエラーって事でしょうか。
そういうの弱いもんですいません…。
プリンタドライバは常に最新のものにしています。
またOK/NGのPCでも全て同じドライバ(XEROX/RICOH/FUJITSUそれぞれ)を使用し
落ちるページとスプールサイズは同じ(厳密な調査はしていませんが…)なので
ドライバではないと考えていますがいかがでしょうか。
VB5でOCXを作る件ですが、こちらは全てのPC、プリンタの組み合わせで
落ちる事が無かったようです。これが原因とすると、Printerオブジェクトの
バージョンアップに伴い、VB6でバグが出た。或いはバグが修正されたことにより
弊社のコーディングミスが炙り出された。という事も考えられますでしょうか…。
VB5で作ってみたのは、「現行のOCXはもしかしたらVB5かも…」という
若干頼りない長老の一言からなのですが。
> hFont = oFont.hFont
これはよく考えてみると、
> Printer.Print "テスト太郎"
の直後でないとダメですね。
# 直後でもダメかもしれませんが。
まぁ、コーディングの問題とは考えづらいでしょう。
プリンタドライバに問題点が見つからないのならば、VB6の問題と判断する
のが良さそうです。
で、MS側にそれを訊いてるとか。
K.J.K.様
本当にありがとうございます。
(盆休みで返事が送れました。スミマセン)
>> Printer.Print "テスト太郎"
>の直後でないとダメですね。
># 直後でもダメかもしれませんが。
変わらずでした…。
どうにかGDIオブジェクトを開放できればいいのですが、
何も分からず実力不足を実感しています。
自分で書けるとしたら Set StdFont = Nothing くらいですが、
MSDNのサンプルにはその記述が無かったので余計に分からないですね…。
>で、MS側にそれを訊いてるとか。
VB6のサポートってもう終わってませんでしたっけ?
今のところVB5のOCXでやろうかという話に決まりつつありますが、
もう少し最後の悪あがきをしようと考えてますので、
解決のチェックはもう少し先延ばしにします。
※VB5で解決って訳ではないかもしれませんが…
> VB6のサポートってもう終わってませんでしたっけ?
終わっていたとしても報告と確認にはなりますし。
基本的にはやじゅさんのいう通り、Set命令をループの外にだす。
その上で、
「例えばstdFont.Sizeに7をセットすると、実際は6.72になるなど、」
が、いつ変更されているのか探したほうがいいとおもいますけど。
WithEventsとかで飛んでませんか?
StdFontを検索すると、GDIを経由して、プリンタに送っているらしいので、
下記のメモリーの限界が影響している可能性が考えられます。
Win2000・XPにも「GUI、フォント」のデータを管理している
「デスクトップアプリケーションヒープ」と言うのもがありますので
確認してみてはいかがですか。
http://support.microsoft.com/kb/126962/ja
いつか書かなくては…
と思いつつ、だいぶ時間が経ってしまいました。
申し訳ありません。
結論としてはVB5で作成したOCXを使うことになりました。
K.J.K.様
ウチはどうやらMSDNのサポートに入っていないようで
確認することができませんでした…。
ぽき様
フォントサイズは
sFont.Size = 7
Msgbox sFont.Size
で、既に7ではありません。
ループの外に出したいのは山々ですが、
上記にあるように全帳票チェックの問題がありますので、
今回は見送らせて頂きました。
higadesu様
いじってみましたが、結果は変わらずでした。
GDIオブジェクトの最大値をいじると若干寿命は延びますが、
それも時間の問題でした。
結果としてよくわかんないけどとりあえずVB5で、
と、若干後味悪いですが、とりあえず出力には問題ありませんでしたので
安心しております。
皆様には沢山のアドバイスを頂き本当にありがとうございました。
ツイート | ![]() |