【问题标题】:How to set the paper size using the WinSpool API?如何使用 WinSpool API 设置纸张大小?
【发布时间】:2011-10-16 23:03:53
【问题描述】:

我不能使用 XPS API,因为该程序必须能够在 Windows XP 上打印。

我正在尝试使用 WinSpool 将纸张尺寸从 Letter 设置为 A4。

这是我的测试代码:

var
  H          : THandle;
  I          : TBytes;
  Info       : PPrinterInfo2;
  NeededSize : DWORD;
  DevMode    : PDeviceMode;
  PD         : TPrinterDefaults;
begin
  PD.pDatatype     := nil;
  PD.pDevMode      := nil;
  PD.DesiredAccess := PRINTER_ACCESS_ADMINISTER;
  if not OpenPrinter('Brother HL-5350DN series Printer', H, @PD) then begin
    raise Exception.Create('OpenPrinter error: ' + SysErrorMessage(GetLastError));
  end;
  try
    Assert(not GetPrinter(H, 2, nil, 0, @NeededSize));
    SetLength(I, NeededSize);
    Info := @I[0];
    if not GetPrinter(H, 2, Info, NeededSize, @NeededSize) then begin
      raise Exception.Create('GetPrinter error: ' + SysErrorMessage(GetLastError));
    end;
    DevMode             := Info.pDevMode;
    DevMode.dmFields    := DevMode.dmFields or DM_PAPERSIZE;
    DevMode.dmPaperSize := DMPAPER_A4;
    Info.pSecurityDescriptor := nil; // According to MSDN it has to be niled if we're not going to change it.

    if not SetPrinter(H, 2, Info, 0) then begin
      raise Exception.Create('SetPrinter error: ' + SysErrorMessage(GetLastError));
    end;
  finally
    ClosePrinter(H);
  end;
  TPrintDialog.Create(Self).Execute; // This is just so I can check the paper size
end;

我有两个与访问权限相关的问题。

如果我将PD.DesiredAccess 设置为PRINTER_ACCESS_ADMINISTER GetPrinter 调用失败,我猜这是由于UAC。

如果我将其设置为PRINTER_ACCESS_USE,则GetPrinter 调用成功并且信息结构正常,但对SetPrinter 的调用失败。

有趣的是,当我忽略 SetPrinter 的结果时,即使 SetPrinter 失败,打印对话框也会将 A4 报告为打印机尺寸。

我是不是完全错了,将正确设置的 PDeviceMode 传递给 OpenPrinter 就足够了吗? (我实际上是在写完这个问题后想出了这个:-)

关于 VCL 的另一个问题:

如果我使用Printers 单元,我如何知道作为参数传递给TPrinter.GetPrinter 方法的缓冲区必须有多大?

背景

系统为:Windows 7 Professional 64-Bit English with English locale。

我正在尝试在网络打印机 (Brother HL-5350DN) 上打印到 A4 纸。

我已将控制面板中的所有打印机设置都设置为 A4 纸,但我正在编写的 Delphi 2009 程序仍然获取 US Letter 的纸张尺寸。

换句话说:Delphi 程序不遵守打印机假脱机程序的默认设置。

如果我首先运行 TPrinterDialog 并从那里手动选择正确的纸张尺寸(在高级打印机设置中)一切都很好。

程序必须在没有任何 UI 的情况下运行,所以我必须以编程方式解决这个问题,或者最好程序应该只尊重默认的 Windows 打印机后台处理程序设置。

也许我错过了一些重要的设置?

【问题讨论】:

  • @David:TPrinter 中没有 PageFormat 设置,我能看到的唯一方法是使用 TPrinter.GetPrinter 和 TPrinter.SetPrinter。我如何知道为TPrinter.GetPrinter 的参数分配了多少空间?
  • 你到底想在这里做什么?打印机肯定知道纸张的尺寸。用户从打印机首选项对话框中选择他们想要的托盘。您无法在代码中更改纸张大小!如果您将整个故事告诉我们,而不是仅仅提供一个窗口来了解您的问题的一个小角落,这可能会对我们有所帮助。
  • 我在问题中添加了更多背景信息。
  • 在我看来,打印机首选项设置不正确。我希望解决这个问题,而不是尝试围绕它编写代码。
  • @Jens - VCL 不填写“驱动程序”和“端口”字段(除非在 W9x 上运行),并且您似乎已经知道打印机名称,为“驱动程序”保留一个字符和“端口”以及“设备”的打印机名称的长度。如果您不知道打印机名称,则为at most 539 characters。您甚至可以将Printer.Printers.Objects[#] 破解为TPrinterDevice (=class Driver,Device,Port: string; end;) 以确保。

标签: delphi winapi windows-7 printing access-control


【解决方案1】:

试试这个家伙 它对我有用

uses WinSpool,Windows,System;

procedure SetPrinterInfo(APrinterName: PChar);
var

  HPrinter : THandle;
  InfoSize,
  BytesNeeded: Cardinal;
  DevMode    : PDeviceMode;
  PI2: PPrinterInfo2;
  PrinterDefaults: TPrinterDefaults;

begin
  with PrinterDefaults do
  begin
    DesiredAccess := PRINTER_ACCESS_USE;
    pDatatype := nil;
    pDevMode := nil;
  end;
  if OpenPrinter(APrinterName, HPrinter, @PrinterDefaults) then
  try
    SetLastError(0);
    //Determine the number of bytes to allocate for the PRINTER_INFO_2 construct...
    if not GetPrinter(HPrinter, 2, nil, 0, @BytesNeeded) then
    begin
      //Allocate memory space for the PRINTER_INFO_2 pointer (PrinterInfo2)...
      PI2 := AllocMem(BytesNeeded);
      try
        InfoSize := SizeOf(TPrinterInfo2);
        if GetPrinter(HPrinter, 2, PI2, BytesNeeded, @BytesNeeded) then
        begin
          DevMode := PI2.pDevMode;
          DevMode.dmFields := DevMode.dmFields or DM_PAPERSIZE;
          DevMode.dmPaperSize := DMPAPER_A4;
          PI2.pSecurityDescriptor := nil;
          // Apply settings to the printer
          if DocumentProperties(0, hPrinter, APrinterName, PI2.pDevMode^,
                                PI2.pDevMode^, DM_IN_BUFFER or DM_OUT_BUFFER) = IDOK then
          begin
            SetPrinter(HPrinter, 2, PI2, 0);  // Ignore the result of this call...
          end;
        end;
      finally
        FreeMem(PI2, BytesNeeded);
      end;
    end;
  finally
    ClosePrinter(HPrinter);
  end;
end;

【讨论】:

    【解决方案2】:

    就像大卫写的那样,我的具体问题是通过在 Windows 中设置正确的打印机首选项来解决的。

    我仍然没有找到为我的应用程序设置本地打印属性的方法,但这已不再需要。

    就像 Sertac 所写的那样,您可以使用 TPrinter.GetPrinterTPrinter.SetPrinter 读取和写入全局打印机首选项。 (参见问题的 cmets)

    由于没有人提供 anwser 并且问题现已解决,因此我将其标记为社区 wiki。随时改进此答案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-15
      • 2015-09-09
      • 2021-05-26
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多