我正在使用我在代码项目中找到的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的速度渲染,但每个页面使用的内存不会返回到操作系统,也不会被垃圾回收。
最后,如果我选择了足够的文件,我的内存不足。为什么呢?
答案 0 :(得分:1)
好的,所以我终于明白了。
首先,就图像而言,CLR显然不知道为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
事件或PDFWrapper
中的FormClosed
对象上调用Finalize
方法包含它的类的方法。
但是,PDFWrapper
实际上似乎缓存了从中检索的图像。因此,当您翻阅PDF时,内存使用量将会增长,直到缓存整个PDF的图像为止。如果您使用这些图像以300DPI打印PDF,那么这是一个更大的问题(在使用1.5GB内存的情况下,我会在60页以上的PDF结尾处出现内存错误。)
据我所知,这个对象没有'Clear Cache'方法。但是我以前使用它的黑客工作是在获得我需要的图像后以1DPI获取图像,然后执行如上所述的垃圾收集。这间接释放了缓存的内存。但是,和以前一样,我们必须告诉CLR我们使用了多少字节。它与上面的计算方法相同。
但还有一个问题。看起来,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
读取,我不认为该项目中的任何其他内容都包括在内。
任何阅读此内容的人都可以快乐编码!