PHP Delphi CSS HTML JavaScript Perl API ASP MySQL XML С++ VBasic WEB разработка *NIX CouchDB Hack Python
Главная Статьи Delphi Меню + TwebBrowser = Проблема?
Главная
 Главная  Контакты
 
Программинг
Статьи Книги ЧаВО
 
xBOOKi
Fresh Books Операционки Сети
 
Поиск
-------
 
Counters
Яндекс цитирования
Rambler's Top100
-------
 
CryptDisk.4h
Программа которая позволяет создать виртуальный шифрованный логический диск.

cryptdisk.4hack.com

-------
 
 

Меню + TwebBrowser = Проблема?
Лозовюк Александр

Эту статью можно рассматривать как продолжение или дополнение к моей первой статье о компоненте TwebBrowser - Как сделать WebBrowser средствами Delphi 5.

В конференции я часто натыкался на вопросы типа – "Как добавить свой пункт меню в контекстное меню IE, как это делает ReGet", "Как запретить появление контекстного меню в TwebBrowser” или "Как показать свое меню вместо стандартного". А вот ответов в большинстве случаев не было, или они советовали попробовать другие компоненты. Но когда мне самому понадобилось в рамках одного проекта сразу, и запретить появление меню, и вставить свой пункт в стандартное меню IE, я решил покопать в этом направлении. И, конечно, MSDN выручила меня в этих поисках. Так что не бойтесь, меню и TwebBrowser – очень даже дружны между собой и то, что с легкостью делают ребята с ReGet Software, не такая уже и неприступная магия…

Запрещение появления меню в TwebBrowser

Хотя в инспекторе и есть такое свойство для этого компонента – PopurMenu, но его использование очень ограничено. Давайте для примера создадим PopurMenu с двумя произвольными пунктами и присвоим свойству PopurMenu TwebBrowser значение PopurMenu1. Запускаем приложение. Щелчок правой кнопкой мыши – ура, меню наше исправно отображаеться. Но радоваться рано. Загружаем любую страницу в браузер, снова щелкае мышкой – вместо нашего меню появляеться стандартное контекстное меню IE. Почему же так?

Компонент TwebBrowser всего лишь оболочка для COM объектов IE, а пока никакая страница не загружена – все сообщения передаются непосредственно вашей программе и, обрабатывая их, программа воспринимает TwebBrowser как обычный VCL-компонент. Поэтому наше меню и появлялось. Когда же вызван метод Navigate, управление идет уже через СОМ интерфейсы, поэтому сообщения обрабатываються не оконным компонентом, а кодом "под оболочкой".

Вообще запретить появление меню можно. Вот некоторые способы.

      ...
      private
      ...
      procedure WMMouseActivate(var Msg: TMessage); message WM_MOUSEACTIVATE;
      end;
      ...      

Ставим обработчик для сообщения WM_MOUSEACTIVATE на уровне головной формы приложения. Потом пишем процедуру:

      procedure TMainForm.WMMouseActivate(var Msg: TMessage);
      begin
      try
      inherited;
      //Анализируем, какая кнопка мыши нажата
      if Msg.LParamHi = 516 then // если правая
      // показываем свое меню
      PopupMenu1.Popup(Mouse.CursorPos.x, Mouse.CursorPos.y);
      Msg.Result := 0;
      except
      end;
      end;      
Но ведь практически всегда у нас на форме есть еще компоненты, для которых нужно выводить или свое меню, или не выводить его вообще. Поэтому в процедуре обработки нужно еще и анализировать координаты, где нажата мышка, и сравнивать их с координатами области, занятой окном браузера.

Что бы просто запретить появление меню можно преобразовать процедуру так:

      procedure TForm1.WMMouseActivate(var Msg: TMessage);
      begin
      try
      inherited;
      if Msg.LParamHi = 516 then
      Msg.Result:= MA_NOACTIVATEANDEAT;
      except
      end;
      end;      
Значение Msg.LparamHi показывает, какая кнопка нажата. 513 - нажата левая, 516 – нажата правая.

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

И еще один недостаток/особенность – полностью, со 100% надежностью перекрыть меню IE своим таким способом почему-то не удается, а вот просто запретить появление – да.

А можно ли управлять отображением меню на уровне самого WebBrowser? Да, отвечает MSDN.

Для этого нужно сперва получить доступ к интерфейсу IDocHostUIHandler и вызвать один из его методов – ShowContextMenu.

Учтите, версия IE – не ниже 4.0. (В С/С++ он описан в файлах Mshtmhst.h; Mshtmhst.idl )

Получить этот интерфейс можно вызывая QueryInterface с параметром IID_IDocHostUIHandler. Он предназначен для управления панелями, меню и контекстными меню WebBrowser-a.

Нас интересует пока только метод ShowContextMenu. Вот его обьявление:

function ShowContextMenu (const dwID: DWORD; const ppt: PPOINT; const pcmdtReserved: IUnknown; const pdispReserved: IDispatch): HRESULT; stdcall;

dwID – идентификатор меню, которое будет отображаться

ppt – указатель на структуру, которая указывает на координаты, где нужно отобразить меню.

pcmdTarget - ссылка на IOleCommandTarget интерфейс, который используется для запроса статуса и команд, которые должны выполняться меню.

рdispObject - ссылка на IDispatch интерфейс объекта, для того, что бы вызывать различные меню для различных объектов.

 

Метод возвращает:

S_OK -Отображается стандартное меню.

S_FALSE - Отображается другое, определенное программой меню.

DOCHOST_E_UNKNOWN - Идентификатор меню неизвестен.

 

В Internet Explorer 4.0 параметр pdispObject не используется, но в IЕ 5 и позже параметр содержит адрес IDispatch интерфейса. Таким способом можно выборочно запрещать появление контекстных меню.

Некоторые другие интересные методы IDocHostUIHandler:

function HideUI: HRESULT; stdcall; - вызываеться, когда удаляеться пункт меню или панели инструментов.

function ShowUI (const dwID: DWORD; const pActiveObject: IOleInPlaceActiveObject; const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame; const pDoc: IOleInPlaceUIWindow): HRESULT; stdcall;

Вызиваеться, когда приложение заменяет меню или панель инструментов.

Добавление пункта в стандартное меню

Иногда, если вы пишете какое-то приложение, которое взаимодействует с браузером, вам необходимо вызвать его непосредственно из IE. Но как добавить свой пункт в меню?

Пункт меню должен быть связан через URL с файлом, который содержит текст сценария, например, на JavaScript. Для того, что бы добавить пункт меню, откройте программно в реестре ключ:

HKEY_CURRENT_USER

Software

Microsoft

Internet Explorer

MenuExt

В этом разделе создайте подраздел, который будет иметь название такое же, как и пункт меню. Значение по умолчанию содержит URL, по которому находиться скрипт. Для подчеркивания вставьте в название перед нужной буквой символ &.

Скрипт будет загружен и выполнен в скрытом окне. В его свойстве external.menuArguments будет содержаться объект window того окна, где был выполнен скрипт меню.

Пример.Вставляет пункт с названием "Демо" в стандартное меню. При нажатии выполняется скрипт, который содержится в файле "С:\demo_script.htm".

 

HKEY_CURRENT_USER

Software

Microsoft

Internet Explorer

MenuExt

Open in new window = "file://c:\demo_script.htm"

 

В файл впишите следующее

 

<SCRIPT LANGUAGE="JavaScript" defer>

open(external.menuArguments.location.href);

</SCRIPT>

 

Действие скрипта заключаеться в следующем:

Он открывает новое окно браузера, и загружает в него документ, который определен в external.menuArguments.location.href – окне, в котором было вызвано меню.


Дополнительные ключи.


Под ключом, где содержится URL скрипта, есть еще несколько величин.Одна из них определяет, в каком из доступных контекстных меню появиться этот новый пункт.Вторая определяет, что сценарий должен выполняться как dialog box.

Ключ "Contexts" имеет тип DWORD, и задает контексты, в которых будет появляться ваше меню. Определяется как применение операции логического ИЛИ над следующими константами:

 

(0x1 << CONTEXT_MENU_DEFAULT) (evaluates to 0x1)

(0x1 << CONTEXT_MENU_IMAGE) (evaluates to 0x2)

(0x1 << CONTEXT_MENU_CONTROL) (evaluates to 0x4)

(0x1 << CONTEXT_MENU_TABLE) (evaluates to 0x8)

(0x1 << CONTEXT_MENU_TEXTSELECT) (evaluates to 0x10)

(0x1 << CONTEXT_MENU_ANCHOR) (evaluates to 0x20)

(0x1 << CONTEXT_MENU_UNKNOWN) (evaluates to 0x40)

 

Так, к примеру, вам нужно, что бы ваше меню появлялось только когда есть выделенный текст. Тогда запишите значение 0x10 (CONTEXT_MENU_TEXTSELECT)

 

Второй ключ- flag с типом DWORD. Если первый бит установлен в 0x1, то сценарий выполняется так, если бы он был вызван методом showModalDialog. Окно, в котором выполняеться скрипт не скрываеться, и не закрываеться после выполнения сценария.

 

Как реализовать все это в Дельфи?

1. Поскольку метод вставки пунктов меню позволяет вставить только ссылку на файл со скриптом, то нужно писать скрипт который будет вызывать вашу программу и передавать ей нужные значения. Для этого нужно еще знать VBScript или JavaScript.

2. Большая проблема состоит в том, что описания интерфейса IDocHostUIHandler нет в файлах.

Но его описание есть в иходниках компонента EmbeddedWB, который можно взять на http://www.euromind.com/iedelphi/

Немножко поразбиравшись, я пришел к таким результатам:

Интерфейс не поддерживаеться стандартным TWebBrowser. Попытка перенести описание интерфейса с EmbeddedWB ник чему не приводит. Я понял с исходников, что при вызове

IUnknown(WebBrowser1) as IDocHostUIHandler происходит обращение к DefaultInterface, а он у TWebBrowser IWebBrowser2. А он не знает о нужном нам интерфейсе.

Может, эта статья и не ответила на все вопросы, а только создала новые – не знаю. Это всегда так – как только с чем-то начинаешь разбираться, сразу к старым вопросам прибавляются новые…



Свежее
Резервное копирование rsync-ом
DNS Amplification (DNS усиление)
Алгоритм Шинглов — поиск нечетких дубликатов текста
Metasploit Framework. Обзор
Использование CouchDB
-------



 
Copyright © 2003-2009   Frikazoid.
Rambler's Top100