1

コードプロジェクトで見つけたDLLを使用してVB.NETでPDFを操作しています。

http://www.codeproject.com/Articles/37458/PDF-Viewer-Control-Without-Acrobat-Reader-Installe

私のアプリでは、グリッド内の複数のファイルを選択して印刷することができます。ファイルはパスワードで保護されたzipファイルに保存されるため、最初に行う手順は、選択した各ファイルをメモリストリームに抽出し、それを新しいPDFラッパーオブジェクトに渡すことです。各オブジェクトはキューに追加されます。次に、キュー内の各オブジェクトがsystem.drawing.imageとしてページごとに印刷されます。すべてがバックグラウンドワーカーで実行されます。

現在、PDFをキューに抽出すると、メモリはほとんど使用されません。しかし、PrintPageイベントハンドラーで、画像を抽出してプリンターに送信すると、問題が発生している可能性があります。私のメモリ使用量が爆発します。もちろん、各画像は300 dpiでレンダリングされるため大きくなりますが、各ページで使用されるメモリはOSに戻されず、ガベージコレクションも行われません。

結局、十分な数のファイルを選択すると、メモリが不足します。なんで?

4

1 に答える 1

1

わかりました、それで私はついにそれを理解しました。

まず、画像に関する限り、CLR は a に割り当てられているメモリの量を明らかに知らないため、Drawing.Image破棄するときに次のように伝える必要があります。

'It's 4 bytes per pixel with RGBA
'Use Drawing.Image.PixelFormat to get
'the number of bytes if you don't know
Dim countBytes as long = 4 * img.Width * img.Height

'Let the CLR know of the memory we want to free
if countBytes > 0 then GC.AddMemoryPressure(countBytes)

'Get rid of the image
img.Dispose()
img = Nothing

'Free up the unused memory
GC.Collect()

'Tell the CLR we took care of it
GC.RemoveMemoryPressure(countBytes)

さて、CodeProject サンプルの PDF ライブラリは、かなり難しいものでした。

まず、ラッパーを保持するフォームのイベント、またはラッパーを保持するクラスのメソッドのいずれかで、オブジェクトのDisposeメソッドを呼び出すようにしてください。PDFWrapperFormClosedFinalize

しかし、PDFWrapper実際には、そこから取得した画像をキャッシュしているようです。そのため、PDF のページをめくると、PDF 全体の画像がキャッシュされるまでメモリ使用量が増加します。これらの画像を使用して PDF を 300DPI で印刷する場合、これはさらに大きな問題になります (1.5GB のメモリを使用すると、60 ページ以上の PDF の終わりに向かってメモリ不足エラーが発生します)。

私が知る限り、このオブジェクトには「キャッシュをクリア」するメソッドはありません。しかし、それを機能させるために使用したハックは、必要な画像を取得した後に 1DPI で画像を取得し、上記のようにガベージ コレクションを実行することでした。これにより、キャッシュされたメモリが間接的に解放されます。ただし、前と同様に、使用したバイト数を CLR に通知する必要があります。上と同じ計算です。

しかし、もう1つ問題があります。オブジェクトは実際にはPDFWrapper別のスレッドで画像を取得しているようです。そのため、300DPI の画像をリクエストした後に別の 1DPI の画像をリクエストすると、印刷する 300DPI の画像が必要なときに、混乱してランダムに 1DPI の画像を吐き出してしまいます。したがって、これに対する回避策は次のとおりです。

Dim img As System.Drawing.Image
img = AFPDFLibUtil.GetImageFromPDF(pdfWrapper, currentPage, DPI)

'Wait for PDFWrapper to finish rendering
Dim sw As New Stopwatch()
sw.Start()
While _pdfWrapper.IsBusy
    If sw.ElapsedMilliseconds < TimeoutMS Then
        System.Threading.Thread.Sleep(10)
    Else
        Throw New Exception("This page took too long to render.")
    End If
End While
sw.Stop()
sw.Reset()

そして、そこに行きます。おそらくそれが、CodeProject サンプルで別の DLL を使用して印刷を行っている理由です。ただし、PDFWrapperオブジェクトは からの読み取りをサポートしIO.MemoryStreamています。そのプロジェクトの他のインクルードはサポートしていないと思います。

これを読んでいる人は誰でも幸せにコーディングしてください!

于 2012-06-19T20:36:00.993 に答える