我们使用 Win32 调用 GDI 打印 API 来打印图形。
我注意到在某些 PCL 打印队列上,我们在打印时会泄漏 GDI 内存。据我所知,一切都被妥善处理。不是我公司的管理员,我很难判断哪些驱动程序打印队列正在使用。PCL6 驱动程序似乎是罪魁祸首。我见过的其他看似无关的问题:
System.ComponentModel.Win32Exception:传递给系统调用的数据区域太小 System.ComponentModel.Win32Exception:操作成功完成
var hdc = GdiPrintWin32.CreateDC(IntPtr.Zero, printerName, IntPtr.Zero, IntPtr.Zero);
if (hdc == IntPtr.Zero)
throw GetError("Device context failed for {0}", printerName);
try
{
if (GdiPrintWin32.StartDoc(hdc, ref doc) <= 0)
throw GetError("Failed to start print job");
此逻辑当前在 Windows 服务内托管的 WCF 中完成。当我将实例模式设置为单一并锤击它时,我只在前几次调用中得到它;当我将实例模式设置为每次调用时,重现起来要容易得多。
我还看到了一些错误,在大约 20 分钟内基本上没有呼叫可以通过该服务,需要重新启动才能修复。我注意到停止服务后,Windows 事件日志的效果如下:
2 个用户注册表句柄从 \Registry\User... 泄露:进程 3792 (\Device\HarddiskVolume1\Windows\System32\rundll32.exe) 已打开密钥 \REGISTRY\USER... 进程 3792 (\Device\HarddiskVolume1\Windows\ System32\rundll32.exe) 已打开密钥 ...\Software\Hewlett-Packard\HP SSNP
基本上,这对我们来说只不过是一场噩梦。有没有人有过这样的经历?
我们用来打印的代码的核心是:
var doc = new Docinfo { DocName = printJobName };
// create a device-context
var hdc = GdiPrintWin32.CreateDC(IntPtr.Zero, printerName, IntPtr.Zero, IntPtr.Zero);
if (hdc == IntPtr.Zero)
throw GetError("Device context failed for {0}", printerName);
try
{
if (GdiPrintWin32.StartDoc(hdc, ref doc) <= 0)
throw GetError("Failed to start print job");
foreach (PrintingMetafile metafile in pages)
{
var bytes = metafile.GetBytes();
// load the bytes to a memory location and get the pointer
var inMemRef = GdiPrintWin32.SetEnhMetaFileBits((uint)bytes.Length, bytes);
if (inMemRef == IntPtr.Zero)
throw GetError("Failed to create EMF in memory");
// Get the pixel coordinates of the paper
var x = 0; // GdiPrintWin32.GetDeviceCaps(hdc, HORZSIZE);
var y = 0; // GdiPrintWin32.GetDeviceCaps(hdc, VERTSIZE);
var hres = GdiPrintWin32.GetDeviceCaps(hdc, HORZRES);
var vres = GdiPrintWin32.GetDeviceCaps(hdc, VERTRES);
var rect = new Rect { Left = x, Top = y, Right = hres, Bottom = vres };
if (GdiPrintWin32.StartPage(hdc) <= 0)
throw GetError("StartPage failed");
if (GdiPrintWin32.PlayEnhMetaFile(hdc, inMemRef, ref rect) == 0)
throw GetError("PlayEnhMetaFile failed");
if (GdiPrintWin32.EndPage(hdc) <= 0)
throw GetError("EndPage failed");
if (inMemRef != IntPtr.Zero)
{
GdiPrintWin32.DeleteEnhMetaFile(inMemRef);
}
}
if (GdiPrintWin32.EndDoc(hdc) <= 0)
throw GetError("Failed to finish print job");
}
finally
{
GdiPrintWin32.DeleteDC(hdc);
}