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

Jen*_*off 3 printing delphi winapi access-control windows-7

我不能使用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;
Run Code Online (Sandbox Code Playgroud)

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

如果我设置PD.DesiredAccessPRINTER_ACCESS_ADMINISTERGetPrinter调用失败,我想这是由于UAC.

如果我将它设置为PRINTER_ACCESS_USEGetPrinter调用成功,并且信息结构是好的,但调用SetPrinter失败.

有趣的是,当我忽略SetPrinter打印对话框的结果时报告A4作为打印机大小即使SetPrinter失败.

我是完全错误的,它是否足以将正确设置的PDeviceMode传递给OpenPrinter?(在写完这个问题之后我真的想出了这个:-)

关于VCL的另一个问题:

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

背景:

该系统是:带有英语语言环境的Windows 7 Professional 64位英语.

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

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

换句话说:Delphi程序不尊重打印机假脱机程序的默认设置.

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

程序必须在没有任何UI的情况下运行,因此我必须以编程方式解决此问题,或者最好是程序应该尊重默认的Windows打印机假脱机程序设置.

也许我错过了一些令人着迷的环境?

小智 7

试试这个人,它适合我

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;
Run Code Online (Sandbox Code Playgroud)