如何使用WinSpool API设置纸张尺寸?

发布于 2024-11-27 16:54:55 字数 2340 浏览 2 评论 0原文

我无法使用 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 调用成功并且 Info 结构正常,但对 SetPrinter 的调用失败。

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

我这样做是否完全错误,并且将正确设置的 PDeviceMode 传递给 OpenPrinter 就足够了? (我实际上是在写完这个问题后想到的:-)

关于VCL的另一个问题:

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

背景

系统为:Windows 7 Professional 64-Bit English,语言环境为英语。

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

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

换句话说:Delphi 程序不尊重打印机后台处理程序的默认设置。

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

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

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

I can't use the XPS API since the program has to be able to print on Windows XP.

I'm trying to set the paper size from Letter to A4 using WinSpool.

This is my test code:

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;

I have two problems related to access rights.

If I set PD.DesiredAccess to PRINTER_ACCESS_ADMINISTER the GetPrinter call fails, I guess this is due to UAC.

If I set it to PRINTER_ACCESS_USE the GetPrinter call succeeds and the Info structure is fine, but the call to SetPrinter fails.

Interestingly enough when I ignore the Result of SetPrinter the print dialog reports A4 as the printer size even though SetPrinter fails.

Am I doing it completly wrong and it is enough to pass a correctly setup up PDeviceMode to OpenPrinter? (I actually came up with this after writing this question :-)

Another question regarding the VCL:

If I use the Printers unit how do I know how big the buffers have to be that get passed as parameters to the TPrinter.GetPrinter method?

Background:

The system is: Windows 7 Professional 64-Bit English with English locale.

I'm trying to print to A4 paper on a network printer (Brother HL-5350DN).

I have set all printer settings in the control panel to A4 paper, but the Delphi 2009 program I'm writing still gets the paper dimensions for US Letter.

In other words: The Delphi program doesn't respect the default settings of the printer spooler.

If I run a TPrinterDialog first and select the correct paper size from there manually (in the advanced printer settings) everything is fine.

The program has to run without any UI, so I have to solve this programmatically or preferably the program should just respect the default Windows printer spooler settings.

Maybe I have missed some imporant setting?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

简单爱 2024-12-04 16:54:55

试试这个家伙
它对我有用

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;

try this guys
it work for me

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;
北斗星光 2024-12-04 16:54:55

正如 David 所写,我的具体问题是通过在 Windows 中设置正确的打印机首选项来解决的。

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

正如 Sertac 所写,您可以使用 TPrinter.GetPrinterTPrinter.SetPrinter 读取和写入全局打印机首选项。 (请参阅问题的评论)

由于没有人提供答案并且问题现已解决,我将其标记为社区维基。请随意改进这个答案。

Like David wrote, my specific problem is solved by setting the correct printer preferences in Windows.

I still haven't found a way to set the local printing properties for my application, but that is no longer necessary.

Like Sertac wrote you can read and write the global printer preferences using TPrinter.GetPrinter and TPrinter.SetPrinter. (See the comments to the question)

Since nobody provided an anwser and the problem is now solved, I'm marking this as community wiki. Feel free to improve this answer.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文