Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: ActiveX/СОМ/CORBA > Отключить сообщения сайта (IWebBrowser2)


Автор: M1ndAction 3.6.2012, 16:33
Использую IWebBrowser2 для отправки запросов на сайт. На сайтах активно используется JavaScript, поэтому проще было использовать WebBrowser, а не Indy, Synapse. Ищем на форму на странице, заполняем все параметры и нажимаем кнопку методом Click.
Все обрабатывается без прямого участия пользователя, то есть окно WebBrowser не отображается, работает все в DLL.
Проблема в том, что на некоторых сайтах выводятся различные сообщения, например, "Ваше сообщение успешно добавлено!" и тому подобные. Установка свойства silent не помогает.

Как запретить вывод таких (да и любых других) сообщений?

Автор: Чучмек 3.6.2012, 16:51
Если никак не удается  отключить сообщения - то можно воспользоваться вставкой скрипта с подменой функций.
Например вставка в страницу следующего кода избавит от alertа 
Код

<script>
window.alert=function(){};
</script>

Автор: M1ndAction 3.6.2012, 18:12
Интересно. То есть на лету, при загрузке, меняем страницу. А так как POST-запрос будет идти уже с этой измененной страницы, то вставленный JavaScript-код будет срабатывать и блокировать все сообщения, генерируемые другими JavaScript?

Автор: M1ndAction 5.6.2012, 11:39
Не получается сделать вставку скрипта. Испробовал несколько вариантов - ни один не сработал.

Код

wb: IWebBrowser2;

function GetWB(link: String): String;
  var
    head, scriptEl: IHTMLElement;
    element: IHTMLScriptElement;

    //WebDoc, WebBody: OleVariant;

    {Range: IHTMLTxtRange;
    be: IHTMLBodyElement;}

    Range: IHTMLTxtRange;

  begin
    wb.Silent := True;
    wb.Navigate(link, Flags, EmptyParam, pData, Headers) ;
    t1 := GetTickCount;
    t2 := GetTickCount;
    while (wb.ReadyState <> READYSTATE_COMPLETE) and (t2-t1<MaxTicks) do
    begin
      t2 := GetTickCount;
      while PeekMessage(MSG, 0, 0, 0, PM_REMOVE) do
        DispatchMessage(MSG);
      //Sleep(1);
    end;
    if t2-t1<MaxTicks then
    begin
      //JavaScript injection

      //WebDoc := WB.Document;
      //WebBody := WebDoc.body{ as IHTMLElement};
      //WebBody.insertAdjacentHTML('BeforeEnd', '<script type="text/javascript"> window.alert=function(){}; </script>');

      {be := (wb.Document as IHTMLDocument2).Body as IHTMLBodyElement;
      Range := be.CreateTextRange;
      Range.Collapse(True);
      Range.PasteHTML('<p><b>test</b></p>');}



      //head := wb.Document.GetElementsByTagName('head');
      //scriptEl := wb.Document.CreateElement('script');
      //element := (scriptEl as IHTMLScriptElement).DomElement;
      //element.text = 'function sayHello() { alert(''hello'') }';
      //head.AppendChild(scriptEl);
      //wb.Document.InvokeScript('sayHello');


      {Range := ((WB.Document AS IHTMLDocument2).body AS IHTMLBodyElement).createTextRange;
      Range.Collapse(False);
      Range.PasteHTML('<b>test</b>');}


      //


      HTMLDocument := (wb.Document) as IHTMLDocument2;
      PersistStream := HTMLDocument as IPersistStreamInit;
      ss := TStringStream.Create;
      try
        Stream := TStreamAdapter.Create(ss, soReference) as IStream;
        PersistStream.Save(Stream, True);
        //TODO: конвертация UTF8
        Result := ss.DataString;
        {Result := StringOf(TEncoding.Convert(TEncoding.UTF8,
          TEncoding.GetEncoding(1251), BytesOf(ss.DataString)));}
      finally
        ss.Free;
      end;
    end
    else
    begin
      Result := '';
    end;
  end;


Функция загружает страницу и возвращает ее исходник. В чем может быть проблема?

Автор: MetalFan 5.6.2012, 12:42
Цитата(M1ndAction @  3.6.2012,  16:33 Найти цитируемый пост)
На сайтах активно используется JavaScript, поэтому проще было использовать WebBrowser, а не Indy, Synapse.

И в чем же простота? как из пушки по воробьям.
Все, что делается через WB можно сделать(проэмулировать) и через HTTP-клиента. инфа 100%.

Автор: M1ndAction 5.6.2012, 13:18
Цитата(MetalFan @  5.6.2012,  12:42 Найти цитируемый пост)
Цитата(M1ndAction @  3.6.2012,  16:33 )
На сайтах активно используется JavaScript, поэтому проще было использовать WebBrowser, а не Indy, Synapse.

И в чем же простота? как из пушки по воробьям.
Все, что делается через WB можно сделать(проэмулировать) и через HTTP-клиента. инфа 100%. 

Можно-то можно, но далеко не всегда это удобно. В данном случае при отправлении POST-запроса посредством JavaScript генерируются некоторые нефиксированные параметры. Переносить эти алгоритмы в свою программу? Но где гарантия, что на другом сайте не будут добавлены еще какие-то JavaScript? Пусть и рассматривается одна CMS, но иногда их кастомизируют. Поэтому для меня удобнее было воспользоваться WebBrowser. Да и в целом тогда код получается лаконичнее (в мое случае).

Хотя не спорю, работать с Synapse, Indy гораздо приятнее. Вокруг WebBrowser целый ореол всего-всего и лично я не вижу особой логики: например, чем отличаются IWebBrowser от IWebBrowser2, или, к примеру, Navigate от Navigate2 плюс куча других интерфейсов. Из-за этого одну и ту же задачу можно решать сотней способов, но работать будет один smile Банальная задача - получить исходники загруженной страницы. Поиск решения в свое время занял приличное количество времени. Пишут, что да, рабочее решение, но у меня этот же самый код не работает. Так что Synapse мне больше по сердцу, но приходится пользоваться разными инструментами smile

Автор: Чучмек 5.6.2012, 14:19
Используй лучше TWebBrowser.
Сейчас проверил D7/WinXPsp3/IE6
Вставка скрипта
Код

var
 wb:iwebbrowser2;
 el:IHTMLElement;
...
...
...
begin
el:=(wb.Document as  IHTMLDocument2 ).createElement('script');
el.setAttribute('text','function alert(){document.location="http://rambler.ru"};',0);
((wb.Document as  IHTMLDocument2 ).body as MSHTML.IHTMLDOMNode).appendChild(el as IHTMLDOMNode);
end;


Работает, но только, если, скрипт вставляется после того, как в окне IE разрешается "отображение активного содержимого".
Цитата

Чтобы помочь обеспечению безопасности, Internet Explorer ограничил отображение активного содержимого, могущего получить доступ к компьютеру, этим файлом. Щелкните здесь для выбора параметров

Автор: Чучмек 5.6.2012, 17:37
Вот сейчас проверил на XE2/Win7
Код

uses
 ...,mshtml,  SHDocVw;

procedure TForm1.FormCreate(Sender: TObject);
var
 v1,v2,v3,v4:olevariant;
 el:IHTMLElement;
 wb:iwebbrowser2;
begin
wb:=SHDocVw.coInternetExplorer.Create;
wb.Navigate2('http://javascript.ru/alert',v1,v2,v3,v4);
wb.Visible:=true;

while wb.ReadyState<>READYSTATE_COMPLETE do
 begin
   sleep(100);
   application.ProcessMessages;
 end;


el:=(wb.Document as  IHTMLDocument2 ).createElement('script');
el.setAttribute('text','function alert(){document.location="http://rambler.ru"};',0);
((wb.Document as  IHTMLDocument2 ).body as MSHTML.IHTMLDOMNode).appendChild(el as IHTMLDOMNode);

application.Terminate;


end;

работает

Автор: M1ndAction 6.6.2012, 06:48
Спасибо, Чучмек, работает. Только в тексте исходников, который возвращает моя функция GetWB, нет скрипта. Но это не важно, главное конечный результат достигается (просто сначала подумал, что способ не работает).

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

Автор: Чучмек 6.6.2012, 14:01
Интересует конкретный сайт или вообще для всех?
Для конкретного - можно.
Нужно смотреть html код, и искать чем и как выполняется проигрывание.
 

Автор: M1ndAction 7.6.2012, 12:16
В идеале, конечно для всех smile
Но скорее всего это просто исключение, и нет смысла из-за одного сайта что-то делать. Но с другой стороны, я посмотрел и, насколько понял, звук воспроизводится через флеш. Для дополнительной проверки воспроизвел на браузере без флеша - никакого звука не было. Поэтому было бы полезно отключить воспроизведение флеша, не подскажете как?

Для примера, ссылка http://www.alphagm.ru/news.html/predlagaem-torgovoe-pomeschenie-v-akademgorodke?forum_ext=1

И еще вопрос. В примере со вставкой скрипта вы привели полный код - включая создание IWebBrowser2. У меня сделано совсем по-другому:

Код

library Amiro;

uses
  ..., SHDocVw, MSHTML;

{$R *.res}

var
  Stop: Boolean;

const
  CLSID_InternetExplorer: TGUID = '{8856F961-340A-11D0-A96B-00C04FD705A2}';

function AtlAxAttachControl(const pControl: IUnknown;
  hWnd: HWND; ppUnkContainer: IUnknown): DWORD; stdcall; external 'ATL.DLL';


procedure MainProc(params...); stdcall; 
var
  wb: IWebBrowser2; // бразуер
  wHandle: HWND; // handle созданного окна
  WndClass: TWndClassEx;
  pData, Headers, Flags: OleVariant;
  Msg: tagMSG;
  HTMLDocument: IHTMLDocument2;
  //PersistFile: IPersistFile;

  PersistStream: IPersistStreamInit;
  Stream: IStream;
  res: String;
  t1, t2: Cardinal;  

  function GetWB(link: String): String;//Загрузка страницы и возврат html-исходника через WebBrowser
  var
    el:IHTMLElement;
  begin
    wb.Silent := True;
    wb.Navigate(link, Flags, EmptyParam, pData, Headers) ;
    t1 := GetTickCount;
    t2 := GetTickCount;
    while (wb.ReadyState <> READYSTATE_COMPLETE) and (t2-t1<MaxTicks) do
    begin
      t2 := GetTickCount;
      while PeekMessage(MSG, 0, 0, 0, PM_REMOVE) do
        DispatchMessage(MSG);
      //Sleep(1);
    end;
    if t2-t1<MaxTicks then
    begin
      //JavaScript injection
      el:=(wb.Document as  IHTMLDocument2 ).createElement('script');
      el.setAttribute('text','window.alert=function(){};',0);
        ((wb.Document as  IHTMLDocument2 ).body as MSHTML.IHTMLDOMNode).appendChild(el as IHTMLDOMNode);

      HTMLDocument := (wb.Document) as IHTMLDocument2;
      PersistStream := HTMLDocument as IPersistStreamInit;
      ss := TStringStream.Create;
      try
        Stream := TStreamAdapter.Create(ss, soReference) as IStream;
        PersistStream.Save(Stream, True);
        Result := ss.DataString;
      finally
        ss.Free;
      end;
    end
    else
    begin
      Result := '';
    end;
  end;

begin
  ...

  //Инициализация WebBrowser
  FillChar(WndClass, SizeOf(WndClass), 0);
  with WndClass do
  begin
    cbSize := SizeOf(WndClass);
    lpszClassName := 'MESSAGE_ONLY_WINDOW';
    lpfnWndProc := @DefWindowProc;
  end;
  RegisterClassEx(WndClass);
  wHandle := CreateWindowEx(0, WndClass.lpszClassName, nil,
    0, 0, 0, 0, 0, DWORD(HWND_MESSAGE), 0, 0, nil);

  if (wHandle = 0) then    
    raise Exception.Create('CreateWindowEx');

  http := THttpSend.Create;
  try
    CoInitializeEx(nil, COINIT_APARTMENTTHREADED);

    if (CoCreateInstance(CLSID_InternetExplorer, nil, CLSCTX_INPROC_SERVER,
      IID_IWebBrowser2, wb) <> S_OK) then
      raise Exception.Create('CoCreateInstance');
    try
      AtlAxAttachControl(wb, wHandle, nil);      
      wb.Silent := True;
      
      resp := GetWB(url);
      
      ...
    finally
      wb := nil;
    end;
  finally
    DestroyWindow(wHandle);
    CoUnInitialize;
  end;
end;

...


Это часть кода библиотеки. Приложение работает в многопоточном режиме, и каждый поток вызывает одну из функций библиотеки. Все работает нормально, за одним исключением. Когда все потоки отработали, я заново их запускаю, но возникает ошибка на этапе создания класса окна(wHandle := CreateWindowEx) - wHandle равен нулю, возвращает ошибку "Класс уже существует". Здесь непонятны два момента:
1. Ведь потоки потоки работают, то есть у нас создаются эти оконные классы раз за разом (точнее поток за потоком smile) никакой ошибки не возникает, но как программа отработает, запускаю заново задачу, возникает ошибка, хотя принципиально ничего не менятся.
2. И опять же, все что создается - освобождается: и wb, и wHandle.
Так как, код, связанный с WebBrowser - не мой, вполне возможно, я что-то упустил.

А если создавать IWebBrowser2 как у вас в примере, то видно, что появляются процессы IE (в моем способе их нет) и они остаются даже после закрытия программы (wb := nil не помогает). Да и как мне показалось, потоки стали очень медленно работать, как в однопоточном режиме.

Поэтому первый способ мне кажется предпочтительнее, осталось только разобраться с этой ошибкой smile

Автор: Чучмек 7.6.2012, 12:31
Я пока особо не вчитывался, но... Регистрация класса происходит при создании каждого потока?
По-моему RegisterClassEx нужно вызывать только один раз.

Добавлено через 11 минут и 52 секунды
Цитата(M1ndAction @  7.6.2012,  12:16 Найти цитируемый пост)
и они остаются даже после закрытия программы (wb := nil не помогает)

iwebbrowser2 в данном случае "ручка" к окну IE, который выполняется в своем процессе.
Код

wb.Quit;


Автор: MetalFan 7.6.2012, 17:49
Цитата(M1ndAction @  7.6.2012,  12:16 Найти цитируемый пост)
Поэтому было бы полезно отключить воспроизведение флеша, не подскажете как?

Пробежаться по DOM структуре и "грохнуть" все <embed> и <object>

Автор: M1ndAction 8.6.2012, 07:34
Цитата(MetalFan @  7.6.2012,  17:49 Найти цитируемый пост)
Цитата(M1ndAction @  7.6.2012,  12:16 )
Поэтому было бы полезно отключить воспроизведение флеша, не подскажете как?

Пробежаться по DOM структуре и "грохнуть" все <embed> и <object>

А как это сделать? smile Одно дело уже в загруженный документ добавить свой код, а в данном случае если мы будем править загруженный, то уже весь флеш к тому времени будет работать. Нет такой настройки, что-нибудь вроде "отключить медиа-объекты"?

Что касается ошибки "Класс уже существует": нашел решение. Модифицируем код
Код

  ...
  finally
    DestroyWindow(wHandle);
    CoUnInitialize;
  end;


на 

Код

  ...
  finally
    DestroyWindow(wHandle);
    UnregisterClass(WndClass.lpszClassName, WndClass.hInstance);
    CoUnInitialize;
  end;


Самое смешное, что это решение первым пришло мне в голову, ведь это так логично: если натыкаемся на "уже зарегистрированный" класс, то очевидно, что его нужно "разрегистрировать", то бишь UnregisterClass. Но в справке я нашел лишь procedure UnRegisterClass(AClass: TPersistentClass);, и начались длительные поиски. Как я смотрел - непонятно smile

А решение, кстати, нашел здесь же, на форуме http://forum.vingrad.ru/forum/topic-313129/kw-registerclassex.html

Автор: Чучмек 8.6.2012, 10:23
Зачем регистрировать класс при создании каждого окна?
Зарегистрируй один раз при запуске программы (загрузке dll),  и по завершении один раз  вызывай Unregister. 

Автор: M1ndAction 8.6.2012, 10:39
Если регистрировать класс в основной программе (один раз), то нужно полученную переменную (WndClass: TWndClassEx) передавать в библиотеку, чтобы там можно было создать окно на базе этого класса, верно? Если так, то нужно передавать в виде параметра основной функции, что мне не подходит из-за наличия других библиотек с таким же объявлением функции. Есть ли какие-то альтернативы здесь?

Автор: Чучмек 8.6.2012, 10:56
Можно добавить в dll функции Initialize/UnInitialize
А можно использовать dllMain...
Концовка dll должна выглядеть примерно так
Код

procedure MyFirstDLLProc(Reason: Integer);
begin
        if Reason = DLL_PROCESS_DETACH then UnregisterClass...       
end;
begin
DLLProc := @MyDLLProc;
RegisterClassEx...
end.


Автор: MetalFan 8.6.2012, 18:05
По поводу отключения звука, советую скачать и поковырять, или даже заюзать http://www.bsalsa.com/.
У него есть опция, отключающая звук... можно посмотреть, как это в нем реализовано.

Не знаю, те ли это звуки....
Код

procedure TEmbeddedWB.DisableNavSound(bDisable: Boolean);
begin
  if GetIEVersionMajor >= 7 then
    CoInternetSetFeatureEnabled(FEATURE_DISABLE_NAVIGATION_SOUNDS, FEATURE_FROM_PROCESS, bDisable)
  else
    EwbTools.DisableNavSound(bDisable);
end;


А еще там есть DownloadOptions. там много чего можно поотключать)

Во. 
DLCTL_DOWNLOADONLY: The page will only be downloaded, not displayed.

Наверное то, что нужно. не?

Автор: M1ndAction 14.6.2012, 12:40
Цитата(Чучмек @  8.6.2012,  10:56 Найти цитируемый пост)
Можно добавить в dll функции Initialize/UnInitialize
А можно использовать dllMain...
Концовка dll должна выглядеть примерно так

ok, спасибо, посмотрю. Сейчас программа на общем тестировании, после реализую ваш вариант.

Цитата(MetalFan @  8.6.2012,  18:05 Найти цитируемый пост)
По поводу отключения звука, советую скачать и поковырять, или даже заюзать EmbeddedWB.
У него есть опция, отключающая звук... можно посмотреть, как это в нем реализовано.

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

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)