Печать из TWebBrowser

Я использую следующий модуль для отображения и печати HTML-кода через TWebBrowser, который отображается в немодальном диалоговом окне. В моей производственной программе следующий код работает под Windows-XP, но не работает с Windows-7 (сообщение об ошибке всегда - Внешнее исключение C015D00F). Чтобы изолировать проблему, я написал простую тестовую программу, которая также имеет немодальный диалог, содержащий TWebBrowser; Сама по себе эта тестовая программа корректно работает с Windows-7, но когда я подключаю немодальный диалог из тестовой программы к производственной программе, я получаю внешнее исключение.

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

В чем может быть проблема? Код для печати взят с сайта Embarcadero.

unit Test4;

interface
uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, OleCtrls, SHDocVw, MSHTML;

type
 THTMLPreview = class(TForm)
  web: TWebBrowser;
  procedure FormClose(Sender: TObject; var Action: TCloseAction);
  procedure webDocumentComplete(Sender: TObject; const pDisp: IDispatch;
                                var URL: OleVariant);
  private
   options: word;
   fn: string;
   procedure DoPrint;
  public
   Constructor Create (const afn, acapt: string; opts: word);
  end;

implementation

{$R *.dfm}

constructor THTMLPreview.Create (const afn, acapt: string; opts: word);                
begin
 inherited create (nil);
 caption:= acapt;
 fn:= afn;
 options:= opts;
 web.Navigate (fn);
end;

procedure THTMLPreview.webDocumentComplete(Sender: TObject;
                 const pDisp: IDispatch; var URL: OleVariant);
begin
 DoPrint
end;

procedure THTMLPreview.DoPrint;
var
 HTMLDoc: IHTMLDocument2;
 HTMLWnd: IHTMLWindow2;
 HTMLWindow3: IHTMLWindow3;

begin
 if options and 4 = 4 then
  begin
   HTMLDoc:= web.Document as IHTMLDocument2;
   if HTMLDoc <> nil then
    begin
     HTMLWnd:= HTMLDoc.parentWindow;
     HTMLWindow3:= HTMLWnd as IHTMLWindow3;
     HTMLWindow3.print;
    end
  end
end;

procedure THTMLPreview.FormClose(Sender: TObject; var Action: TCloseAction);
begin
 if options and 1 = 1 then deletefile (fn);
 action:= caFree
end;

end.

Использование оператора Web.ControlInterface.ExecWB (OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, vaIn, vaOut) дает ту же ошибку.


Отредактируйте несколько дней спустя:

Я попробовал совершенно другой подход к проблеме. В HTML-код я добавил фрагмент javascript, который отображает кнопку «печать» и добавляет событие «отпечаток». Еще раз, это отлично работает на моей машине разработки (XP), но не на моих клиентских машинах (Win7), где программа зависает с объявлением Внешнее исключение C015D00F (тот же адрес, что и раньше).


После большого количества поисков в Google я обнаружил, что код исключения C015000F вызван тем, что «деактивируемый контекст активации не является последним активированным». Что это значит для плохого программиста на Delphi?


person No'am Newman    schedule 29.03.2013    source источник
comment
Какая строка вызывает исключение? А как закрыть форму? вручную?   -  person kobik    schedule 29.03.2013
comment
@Noam, я не голосующий против, но вы все равно не предоставили достаточно информации. пожалуйста, ответьте на мой комментарий.   -  person kobik    schedule 29.03.2013
comment
@kobik: Я думал, что представил более чем достаточно информации. Я также опубликовал комментарий, который относится к вашему комментарию, но, похоже, он исчез. Я не знаю, какая строка в подпрограмме DoPrint вызывает исключение, но строка ControlInterface имеет точно такую ​​же проблему. Форма будет закрыта вручную, но вылетит, прежде чем ее можно будет закрыть.   -  person No'am Newman    schedule 29.03.2013
comment
@Noam, мне нравится твой код. Однако я бы указал Application в качестве владельца формы. и сделайте web.Navigate при показе формы вместо onCreate.   -  person kobik    schedule 29.03.2013
comment
@kobik: Я сделал, как вы предложили, но без изменений. Я добавил простые операторы регистрации после каждой строки в процедуре DoPrint, включая строку после HTMLWindow3.print - эта строка появляется в журнале. Другими словами, HTMLWindow3.print вызывает некоторый код библиотеки, и сбой происходит внутри этого внешнего кода.   -  person No'am Newman    schedule 30.03.2013


Ответы (3)


Если я правильно помню, метод IHTMLWindow3.print открывает системное диалоговое окно по умолчанию «отправлено на принтер». Вы хотите это? Для приложения я однажды искал способ избежать этого, а затем нашел этот код.

var
  r:TRect;
  sh,ph:HDC;
begin
  OleInitialize(nil);
  WebBrowser1.Navigate('file://'+HtmlFilePath);
  while WebBrowser1.ReadyState<>READYSTATE_COMPLETE do Application.HandleMessage;

  //Printer.PrinterIndex:=//set selected printer here
  Printer.BeginDoc;
  try
    Printer.Canvas.Lock;
    try
      sh:=GetDC(0);
      ph:=Printer.Canvas.Handle;

      //TODO: make rect a bit smaller for a page margin
      //TODO: get page size from printer settings, assume A4 here (210x297mm)
      r.Left:=0;
      r.Top:=0;
      r.Right:=2100 * GetDeviceCaps(sh,LOGPIXELSX) div 254;
      r.Bottom:=2970 * GetDeviceCaps(sh,LOGPIXELSY) div 254;
      WebBrowser1.BoundsRect:=r;

      SetMapMode(ph,MM_ISOTROPIC);
      SetWindowExtEx(ph,r.Right,r.Bottom,nil);
      SetViewportExtEx(ph,r.Right,r.Bottom,nil);
      r.Right:=GetDeviceCaps(ph,HORZRES)-1;
      r.Bottom:=GetDeviceCaps(ph,VERTRES)-1;

      (WebBrowser1.ControlInterface as IViewObject).Draw(
        DVASPECT_CONTENT,
        1,
        nil,nil,0,ph,@r,nil,nil,0);
    finally
      Printer.Canvas.Unlock;
    end;
    Printer.EndDoc;
  except
    Printer.Abort;
    raise;
  end;

SetWindowExtEx и SetViewportExtEx правильно устанавливают масштабирование, поэтому вы можете использовать единицу измерения «мм» в HTML / CSS.

person Stijn Sanders    schedule 29.03.2013
comment
бит 'and 1 и 1' был опечаткой - должен был быть 'options и 1 = 1 then' - исправлен. - person No'am Newman; 29.03.2013
comment
Веб-браузер содержит текст, таблицу (содержащую текст), а затем текст под таблицей. Весь текст в кодировке UTF-8, как и на иврите. Используя приведенный выше код, на выходе появилась только пустая таблица HTML; ни один текст не сделал. - person No'am Newman; 29.03.2013
comment
Этот код вызывает сбой программы на компьютере с Windows-7. Это нормально работает с XP, но, как я уже отмечал выше, текст отсутствует. Так что это не жизнеспособное решение. - person No'am Newman; 29.03.2013
comment
Иврит? Может быть, это проблема со шрифтом принтера? Используете ли вы шрифт по умолчанию или у вас есть конкретный локальный шрифт TTF / OTF, в котором, как вы знаете, определены все символы иврита? - person Stijn Sanders; 29.03.2013
comment
Я никогда не указывал шрифт и не думаю, что он актуален. Диалоговое окно «выбрать принтер» не появляется в Windows-7, в отличие от XP (с кодом, который я использую). Я могу обратить внимание на шрифт, как только знаю, что что-то напечатано. Код, который вы представили, вызывает сбой компьютера - даже хуже, чем просто сбой моей программы. - person No'am Newman; 29.03.2013

Попробуй это.

var
    vIn, vOut: OleVariant;
begin
    WebBrowser_mail.ControlInterface.ExecWB(OLECMDID_PRINT,
                    OLECMDEXECOPT_PROMPTUSER, vIn, vOut) ;
person simasware    schedule 01.04.2013
comment
Я включил вариант этого в свой пост - прочтите до конца. Я отказался от идеи печати из веб-браузера под Windows7, пока кто-нибудь не объяснит, как избежать внешнего исключения C015D00F. - person No'am Newman; 02.04.2013

Когда у меня возникают проблемы с WB, это происходит по двум причинам: а) документ загружен не полностью; б) компонент WB не «полностью зарегистрирован», потому что он находится в скрытой форме и т. Д.

Для а) я много лет использую этот код:

var
  CurDispatch: IDispatch;
  DocLoaded: boolean;


procedure TForm3.WebBrowser1DocumentComplete(ASender: TObject;
  const pDisp: IDispatch; const URL: OleVariant);
begin
  if (pDisp = CurDispatch) then begin
    CurDispatch := nil; {clear the global variable }
    DocLoaded:=true;
  end;
end;

procedure TForm3.WebBrowser1NavigateComplete2(ASender: TObject;
  const pDisp: IDispatch; const URL: OleVariant);
begin
  if CurDispatch = nil then
    CurDispatch := pDisp; { save for comparison }
end;

procedure TForm3.Button1Click(Sender: TObject);
var
  vIn, vOut: OleVariant;

begin
  DocLoaded:=false;
  WebBrowser1.Navigate(EdLink.Text);
  repeat
    Application.ProcessMessages;
  until DocLoaded;

  WebBrowser1.ControlInterface.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, vIn, vOut)
end;

Если проблемы все еще существуют, попробуйте поместить это в FormShow - он решает б):

WebBrowser1.HandleNeeded;

Больше никаких проблем с WB.

Не забудьте установить WebBrowser1.Silent: = true, чтобы скрыть ошибки JavaScript на странице.

person Vladimír Klaus    schedule 23.02.2015