ResetDC() ничего не делает (API диспетчера очереди печати для Windows)

Я написал классы-оболочки для API диспетчера очереди печати Windows, которые работают... в основном.

Единственное, что не работает, — это применение настроек принтера.

Сначала я вызываю DocumentProperties() для успешного получения и использования настроек принтера.

Затем я пытаюсь применить эти настройки с помощью ResetDC(). но ничего не происходит. Функция принимает допустимый дескриптор и возвращает тот же допустимый дескриптор, что означает, что она должна была применить настройки. Но, похоже, ничего не произошло: на распечатки не повлияли изменения в настройках принтера.

Я даже пытался использовать SetPrinter() с уровень 9, тоже без эффекта.

Эта задача становится довольно срочной. Какие-либо предложения?

Использую Delphi XE2, Windows 7 64-bit.


Хорошо, пропустим всю большую часть больших объектно-ориентированных оберток. Вот сжатая процедурная версия. (Обратите внимание, что вы должны предоставить файл XPS.)

Изменения настроек принтера применяются (при использовании Info2_Apply()) и видны в MS Word. Они просто игнорируются при печати. Вот в чем загадка.

Я пробовал так много вещей, что у меня заканчиваются варианты. Помощь была бы... очень признательна.


У меня наконец закончились варианты.

Вот последняя версия моего тестового кода. Он самодостаточен и содержит больше функций, чем вам нужно — результат отчаянных попыток сделать все, что я мог придумать.

Если кто-нибудь еще может заставить работать настройки принтера, не могли бы вы сообщить мне?

Заполните поле со списком именами принтеров, используя:

uses
  Printers

ComboBox1.Items.Assign(Printer.Printers);

Процедура печати (мой тестовый код):

uses
  Winapi.WinSpool

procedure PrintXPS(PrinterName, FileNameXPS: string; ParentFormHandle: THandle = 0);

  //  Printer handle

  procedure Printer_Open(out Printer: THandle; Defaults: PPrinterDefaultsW = nil);
  begin
    if  not OpenPrinterW(PWideChar(PrinterName), Printer, Defaults) then
      RaiseLastOSError;
  end;

  procedure Printer_Close(Printer: THandle);
  begin
    if  not ClosePrinter(Printer) then
      RaiseLastOSError;
  end;

  //  Printer defaults

  procedure Defaults_Obtain(out DefaultsHandle: THandle; out Defaults: PPrinterDefaultsW);
  begin
    DefaultsHandle  := GlobalAlloc(GHND, SizeOf(TPrinterDefaultsW));
    if  DefaultsHandle = 0  then
      RaiseLastOSError;
    Defaults  := GlobalLock(DefaultsHandle);
    if  Defaults = nil  then
      RaiseLastOSError;
  end;

  //  Print settings

  procedure Settings_Obtain(Printer: THandle; out SettingsHandle: THandle; out Settings: PDeviceModeW);
  var
    DeviceModeSize: integer;
  begin
    DeviceModeSize  := DocumentProperties(0, Printer, PWideChar(PrinterName), nil, nil, 0);
    if  DeviceModeSize < 0 then
      RaiseLastOSError;
    //  Allocate memory
    SettingsHandle := GlobalAlloc(GHND, DeviceModeSize);
    if  SettingsHandle = 0 then
      RaiseLastOSError;
    //  Lock memory
    Settings := GlobalLock(SettingsHandle);
    if  Settings = nil then
      RaiseLastOSError;
    //  Populate memory
    if  DocumentProperties(ParentFormHandle, Printer, PWideChar(PrinterName), Settings, Settings, DM_OUT_BUFFER) < 0  then
      RaiseLastOSError;
  end;

  procedure Settings_Show(Printer: THandle; var Settings: PDeviceModeW; Options: Cardinal);
  var
    Return: integer;
  begin
    Return  := DocumentProperties(ParentFormHandle, Printer, PWideChar(PrinterName), Settings, Settings, Options);
    if  Return < 0  then
      RaiseLastOSError;
  end;

  //  DC

  function  ObtainDC(Printer: THandle; DeviceMode: PDeviceModeW): HDC;
  begin
    Result  := CreateDC(nil, PWideChar(PrinterName), nil, DeviceMode);
    if  Result = 0  then
      RaiseLastOSError;
  end;

  procedure ApplyDC(DC: HDC; DeviceMode: PDeviceModeW);
  begin
    if  ResetDC(DC, DeviceMode^) = 0  then
      RaiseLastOSError;
  end;

  //  PRINTER_INFO_2

  procedure Info2_Obtain(Printer: THandle; out Info2Handle: THandle; out Info2: PPrinterInfo2W);
  var
    InfoSize: Cardinal;
  begin
    GetPrinterW(Printer, 2, nil, 0, @InfoSize);
    //  Get printer info memory
    Info2Handle := GlobalAlloc(GHND, InfoSize);
    if  Info2Handle = 0 then
      RaiseLastOSError;
    //  Lock printer info memory
    Info2 := GlobalLock(Info2Handle);
    if  Info2 = nil then
      RaiseLastOSError;
    //  Get printer info data
    if  not GetPrinterW(Printer, 2, Info2, InfoSize, @InfoSize)  then
      RaiseLastOSError;
  end;

  procedure Info2_Apply(Printer: THandle; Info2: PPrinterInfo2W);
  begin
    if  not SetPrinterW(Printer, 2, Info2, 0) then
      RaiseLastOSError;
  end;

  //  PRINTER_INFO_8

  procedure Info8_Fetch(Printer: THandle; Settings: PDeviceModeW);
  var
    lBuffer: PPrinterInfo8W;
    lBufferSize: Cardinal;
  begin
    GetPrinterW(Printer, 8, nil, 0, @lBufferSize);
    GetMem(lBuffer, lBufferSize);
    try
      FillChar(lBuffer^, lBufferSize, 0);
      //  Make the call
      lBuffer.pDevMode  := Settings;
      if  not GetPrinterW(Printer, 8, lBuffer, lBufferSize, @lBufferSize) then
        RaiseLastOSError;
    finally
      FreeMem(lBuffer, lBufferSize);
    end;
  end;

  procedure Info8_Apply(Printer: THandle; Settings: PDeviceModeW);
  var
    lPrinterInfo8: TPrinterInfo8W;
  begin
    lPrinterInfo8.pDevMode  := Settings;
    if  not SetPrinterW(Printer, 8, @lPrinterInfo8, 0) then
      RaiseLastOSError;
  end;

  //  PRINTER_INFO_9

  procedure Info9_Fetch(Printer: THandle; Settings: PDeviceModeW);
  var
    lBuffer: PPrinterInfo9W;
    lBufferSize: Cardinal;
  begin
    GetPrinterW(Printer, 9, nil, 0, @lBufferSize);
    GetMem(lBuffer, lBufferSize);
    try
      FillChar(lBuffer^, lBufferSize, 0);
      //  Make the call
      lBuffer.pDevMode  := Settings;
      if  not GetPrinterW(Printer, 9, lBuffer, lBufferSize, @lBufferSize) then
        RaiseLastOSError;
    finally
      FreeMem(lBuffer, lBufferSize);
    end;
  end;

  procedure Info9_Apply(Printer: THandle; Settings: PDeviceModeW);
  var
    lPrinterInfo9: TPrinterInfo9W;
  begin
    lPrinterInfo9.pDevMode  := Settings;
    if  not SetPrinterW(Printer, 9, @lPrinterInfo9, 0) then
      RaiseLastOSError;
  end;

  //  Print jobs

  function  JobCreate(Printer: THandle; FileName: string): Cardinal;
  var
    lBufferSize: Cardinal;
    lAddJobInfo: PAddJobInfo1W;
  begin
    //  Create job
    AddJobW(Printer, 1, nil, 0, lBufferSize);
    GetMem(lAddJobInfo, lBufferSize);
    try
      if  not AddJobW(Printer, 1, lAddJobInfo, lBufferSize, lBufferSize)  then
        RaiseLastOSError;
      Result  := lAddJobInfo.JobId;
      //  Copy the file into place
      CopyFile(PWideChar(FileName), lAddJobInfo.Path, True);
    finally
      FreeMem(lAddJobInfo, lBufferSize);
    end;
  end;

  procedure JobStart(Printer: THandle; JobID: Cardinal);
  begin
    if  not ScheduleJob(Printer, JobID) then
      RaiseLastOSError;
  end;

  //  General cleanup

  procedure ReleaseHandle(Handle: THandle);
  begin
    if  not GlobalUnlock(Handle)  then
      ;//RaiseLastOSError;
    if  GlobalFree(Handle) <> 0 then
      ;//RaiseLastOSError;
  end;

var
  PrinterA{, PrinterB}: THandle;
  Defaults: PPrinterDefaultsW;
  DefaultsHandle: THandle;
  DataType: string;
  Settings: PDeviceModeW;
  SettingsHandle: THandle;
  Info2: PPrinterInfo2W;
  Info2Handle: THandle;
//  DC: HDC;
  JobID: Cardinal;
begin
  if  not FileExists(FileNameXPS)  then
    raise Exception.Create('File not found: ' + FileNameXPS);

  //  Get DataType
  Printer_Open(PrinterA);
  try
    Info2_Obtain(PrinterA, Info2Handle, Info2);
    try
      DataType  := WideCharToString(Info2.pDatatype);
    finally
      ReleaseHandle(Info2Handle);
    end;
  finally
    Printer_Close(PrinterA);
  end;

  Defaults_Obtain(DefaultsHandle, Defaults);
  try
    Defaults.pDatatype      := PWideChar(DataType);
    Defaults.pDevMode       := nil;
    Defaults.DesiredAccess  := PRINTER_ALL_ACCESS;

    Printer_Open(PrinterA, Defaults);
    try
      Info2_Obtain(PrinterA, Info2Handle, Info2);
      try
        Settings_Show(PrinterA, Info2.pDevMode, DM_IN_BUFFER or DM_IN_PROMPT or DM_OUT_BUFFER);
        //  Try according to:
        //  - Remarks section in http://msdn.microsoft.com/en-us/library/windows/desktop/dd145082%28v=vs.85%29.aspx
        //  - Comment on code line 246 in http://www.lessanvaezi.com/changing-printer-settings-using-the-windows-api/
        Info2.pSecurityDescriptor := nil;
        Info2_Apply(PrinterA, Info2);

        JobID := JobCreate(PrinterA, FileNameXPS);
        JobStart(PrinterA, JobID);

      finally
        ReleaseHandle(Info2Handle);
      end;
    finally
      Printer_Close(PrinterA);
    end;
  finally
    ReleaseHandle(DefaultsHandle);
  end;

end;

person Peter Thönell    schedule 27.04.2012    source источник
comment
Поможет ли эта ссылка Как настроить параметры принтера?? В противном случае отправьте код, иллюстрирующий ваши проблемы.   -  person LU RD    schedule 27.04.2012
comment
Нам понадобится код, чтобы воспроизвести проблему. Каково содержание вашей структуры DEVMODE, например?   -  person Arnaud Bouchez    schedule 27.04.2012
comment
Спасибо, ребята, я добавил ответ, а не редактировал исходный вопрос. Я не уверен, что это лучший способ, но я новичок в этом формате форума.   -  person Peter Thönell    schedule 30.04.2012
comment
Если у вас есть правильный ответ, вполне допустимо поместить его в качестве ответа, в противном случае, если вы хотите добавить больше информации к вопросу, отредактируйте вопрос, как вам помог BillTheLizard.   -  person LU RD    schedule 01.05.2012
comment
Проблема, я полагаю, заключается в том, что вы не используете обновленный контроллер домена, ResetDC ясен насчет hdc :*дескриптор контроллера домена для обновления*, но вы удаляете его, как только обновили. Если я вызову, например, StartDoc перед удалением контроллера домена, я увижу в окне очереди задание с обновленным DevMode. Я понятия не имею, как/если можно было бы использовать DC с AddJob.   -  person Sertac Akyuz    schedule 03.05.2012
comment
Спасибо @Sertac Akyuz за то, что заметили это. Хотя все равно никакого эффекта. Я обновил пример кода.   -  person Peter Thönell    schedule 03.05.2012
comment
@Peter - Я думаю, что задание на печать вообще не использует измененный вами контроллер домена. Вы передаете DC в StartPage, StartDoc и т. д., может быть, для этого и нужен ResetDC.   -  person Sertac Akyuz    schedule 03.05.2012
comment
@Sertac Akyuz - Возможно, вы правы, но даже без использования DC это не работает.   -  person Peter Thönell    schedule 25.05.2012