65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C# 管理打印机设置以实现灵活打印

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (5投票s)

2008年3月18日

CPOL

7分钟阅读

viewsIcon

95574

downloadIcon

2256

一篇关于在 .NET 程序中设置打印机设置的文章

引言

本文介绍了一种在 C#/.NET 控制台应用程序中管理打印机设置的方法。该方法使用 COM 调用随附在 Windows 2003 Server Resource Kit 中的 prnadmin.dll 中的方法。使用 prnadmin.dll,您可以添加/删除打印机、设置打印机端口、设置默认打印机和打印测试页。本文描述的方法也可以嵌入到类库、Windows Forms 应用程序或 Web 应用程序中,只要 prnadmin.dll 已安装并注册。本文提供的演示程序将接受打印机名称、文件名和打印机驱动程序名称(用于新打印机),并设置默认打印机。它展示了如何在 C#/.NET 应用程序中使用 prnadmin.dll 实现“直接到 Postscript”打印功能。

背景

如果您的系统尚未安装 prnadmin.dll,则可以将其作为 Windows 2003 Server Resource Kit 的一部分进行安装,该工具包可从 Microsoft 获取。默认情况下,它会安装到 C:\Program Files\Windows Resource Kits\Tools 文件夹。prnadmin.dll 暴露标准 COM 方法。为了在 COM 客户端中使用 prnadmin.dll,需要对其运行 regsrv32.exe 以将其注册为本地 COM 服务器。此程序通常在 C:\Windows\System32 中,因此请确保此路径在您的环境变量中。

C:Tools> regsvr32.exe prnadmin.dll

将显示一个对话框,其中包含简短消息,告知您成功或失败。如果未对 prnadmin.dll 运行 regsvr32.exe,则尝试对其运行 COM 客户端应用程序将失败并出现 COM 错误 80040154。

为了在 .NET 应用程序中使用 COM 组件,必须使用互操作程序集。互操作程序集包含 .NET 描述 COM 类型所需的类型元数据。(即,您不能仅仅在 Visual Studio 的“添加引用”中浏览并添加原生的 prnadmin.dll。)prnadmin.dll 不附带互操作程序集,但您可以使用 tlbimp.exe 命令自行创建它们。如果您安装了 Visual Studio 2005,您可能会在 C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\Bin 中找到此命令,因此请确保此文件夹在您的路径中。(Tlbimp.exe 也随许多其他 .NET 相关软件包和 SDK 分发。)

C:Tools> tlbimp.exe prnadmin.dll

此命令将在当前文件夹中创建一个名为 PRNADMINLib.dll 的文件,其中包含互操作程序集。这是您可以添加到 .NET 项目的 DLL 文件。(注意:示例代码中包含对 PRNADMINLib.dll 的引用,但您的系统上很可能需要对其进行更新以反映您的安装路径。)

最后,Windows 2003 Server Resource Kit 附带了 prnadmin.dll 的非常有用的文档。它面向 Visual Basic,但转换为 C# 很简单。

Using the Code

这里给出的示例程序将默认打印机设置为打印到本地系统上的文件。它接受三个可选参数:打印机名称、完整文件名和打印机驱动程序名称;并且每个都有硬编码的默认值。如果给定的打印机名称不存在,则首先创建它,然后将其设置为默认值。给定的驱动程序名称仅在创建新打印机时有效,它不会更改现有打印机的驱动程序。

PrinterExample.exe [printerName [filename [drivername]]]

默认打印机名称是“TESTPRINTER”,默认文件名是 @"C:\postscript.ps",默认打印机驱动程序是“HP DeskJet 1200C/PS”,它生成彩色 Postscript。Postscript 驱动程序相当常见;如果您想打印到 Postscript,您应该可以通过打开系统上现有打印机的打印机属性对话框,并在“添加打印机驱动程序”向导的驱动程序下拉菜单中选择一个来找到它。(幸运的是,您通常只需按名称指定驱动程序,Windows 就会负责在驱动程序缓存中找到正确的驱动程序。)

在示例程序中,我们首先获取对 PrintMaster 接口 的引用。这是处理打印机对象的主要 接口

// Get a reference to the printer subsystem
PRNADMINLib.PrintMaster pMaster = new PRNADMINLib.PrintMasterClass();

当打印到文件时,需要创建一个称为端口的特殊对象,打印机可以将数据发送到该对象。该端口又可以配置为将其数据发送到本地系统上的文件。进行了一些检查以确保端口文件名使用完整的路径名,然后检查系统上是否存在与给定文件名对应的端口。如果端口不存在,则创建它。如果端口存在,则重新配置它。示例中使用的方法只是循环遍历 PrintMaster 中的端口枚举。

// Check existence of port
PRNADMINLib.Port pPort = null;
PRNADMINLib.PortCollection portColl =
    (PRNADMINLib.PortCollection)pMaster.get_Ports(ref oServerName);
IEnumerator portEnumerator = portColl.GetEnumerator();
portEnumerator.Reset();
while (portEnumerator.MoveNext())
{
    PRNADMINLib.Port portCurrent = portEnumerator.Current as PRNADMINLib.Port;
    if (portCurrent.PortName == portfileName)
    {
        pPort = portCurrent;
        break;
    }
}

// Get reference to port or create it if it does not exist
if (pPort == null)
{
    pPort = new PRNADMINLib.PortClass();
    pPort.PortName = portfileName;
    pPort.PortType = 3;  // Simple local port
    pMaster.PortAdd(pPort);
}

以相同的方式检查给定打印机的存在。

// Check existence of printer
PRNADMINLib.Printer pPrinter = null;
PRNADMINLib.PrinterCollection prnColl =
    (PRNADMINLib.PrinterCollection)pMaster.get_Printers(ref oServerName);
IEnumerator prnEnumerator = prnColl.GetEnumerator();
prnEnumerator.Reset();
while (prnEnumerator.MoveNext())
{
    PRNADMINLib.Printer prnCurrent =
    prnEnumerator.Current as PRNADMINLib.Printer;
    if (prnCurrent.PrinterName == printerName)
    {
        pPrinter = prnCurrent;
        break;
    }
}

如果打印机不存在,则使用给定驱动程序和上述端口创建它。参数 namedriverport 是最低要求。如果打印机确实存在,则会更改其配置以反映给定的端口文件和驱动程序。

// Get reference to printer and create it if it does not exist
if (pPrinter == null)
{
    // To add printer, name, driver, and port are required
    pPrinter = new PRNADMINLib.PrinterClass();
    pPrinter.PrinterName = printerName;
    pPrinter.DriverName = newPrinterDriver;
    pPrinter.PortName = pPort.PortName;
    pMaster.PrinterAdd(pPrinter);
}
else
{
    // Make sure port is set on existing printer
    if (pPrinter.PortName != pPort.PortName)
    {
        pPrinter.PortName = pPort.PortName;
    }
    // Driver cannot be re-set on existing printer
    if (args.Length == 3 && newPrinterDriver != pPrinter.DriverName)
    {
        Console.WriteLine("Warning: Driver remains set to " + pPrinter.DriverName);
    }
    pMaster.PrinterSet(pPrinter);
}

如果系统上找不到给定驱动程序,此代码可能会引发异常,但在示例中未进行检查。

最后,将打印机设置为默认值,并将测试页打印到已配置的文件中。

// Set printer as system default
pMaster.DefaultPrinter = pPrinter.PrinterName;

// Print test page
pMaster.PrintTestPage("", pPrinter.PrinterName);

您可以通过检查“控制面板”中的“打印机和传真”文件夹,并检查默认打印机的属性来验证这一点。

关注点

在示例程序中,使用了 Postscript 打印机驱动程序。示例代码针对彩色“HP DeskJet 1200C/PS”驱动程序和黑白“HP LaserJet 2100 Series PS”进行了测试,但任何 Postscript 驱动程序都应该可以在此处使用。Postscript 是一种几乎所有打印机都能理解的格式,但它已过时。要获取 PDF,可以使用威斯康星大学麦迪逊分校提供的免费软件程序 Ghostscript。作者已成功将上述 Postscript 驱动程序之一的输出链接到运行 gsview32c.exe 的外部进程,以一步创建 PDF。Ghostview 双重优点在于它还可以合并 PDF。命令如下:

> gswin32c.exe  -dNOPAUSE -dQUIET -dBATCH -sDEVICE=pdfwrite 
    -sOUTPUTFILE="C:\converted.pdf" "C:\postscript.ps"

确保选项的大小写正确。(对于那些在派生进程中运行此命令的人,请注意即使选项暗示相反,消息也可能会写入 stdout 或 stderr。)

或者,如果能找到,可以用 PDF 打印驱动程序代替,为您的应用程序提供直接的“打印到 PDF”功能。不幸的是,作者尝试了这一点,但使用了两个流行的“免费”PDF 编写器都失败了。在每种情况下,产品都在系统上安装了一个“PDF 打印机”,并且它试图使用这些打印机配置的相同驱动程序。结果是,安装的 PDF 打印机也配置为使用特殊端口,这些端口似乎负责生成弹出对话框以调整 PDF 生成设置。不使用这些特殊端口,PDF 生成会导致 Acrobat 阅读器无法读取的输出文件。可能有一种方法可以使用 prnadmin.dll 接口 来管理这些特殊端口,但这超出了本文的范围。

在将打印设置与应用程序中的其他事件同步时必须小心。打印本质上是异步的,设置通常也是如此。连接到打印机的端口就是这种异步设置的一个例子。允许指定输出文件的“打印到 Postscript”的明显实现策略是按需重新配置端口。在打印之前必须小心验证端口设置。作者对此进行了实验,发现如果重新配置端口文件并在之后立即打印测试页,则会生成两个输出文件:一个用于以前的端口,另一个用于新的端口设置。结果发现,设置默认打印机似乎不太容易出现同步时序问题,但它仍然会受到并发问题的影响。

类似上述代码可以包含在处理通过其他 COM 对象(如 Microsoft Office 或 Internet Explorer)访问的数据的 .NET 应用程序中。可以创建命令行程序将 Office 文件或网站直接打印到 Postscript。然后可以调用 Ghostscript 等免费工具将 Postscript 转换为和/或合并为 PDF。

历史

  • 2008年3月18日:文章发布
© . All rights reserved.