Как добавить свой пункт в контекстное меню IE

Автор: Тимофей Чадов
Опубликовано: 05.02.2002
Версия текста: 1.1.1


Введение
Покопаемся в реестре
Скрипт обработки
Использование COM-компонентов
Контекст отображения
Модальный режим
Информация о событии
Примеры, примеры


Введение

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

К примеру, с сайта lingvo.yandex.ru вы можете загрузить модуль для IE, позволяющий получать через сеть перевод с английского или на английский любого выделенного вами в окне браузера слова или словосочетания. Удобно? Несомненно! Выделяете в броузере нужное слово, пара щелчков мышью и перед вами страница с переводом. Кроме этого существует множество модулей различных поисковиков (yandex.ru, google.com, codeproject.com), позволяющих быстро производить поиск по соответствующим сайтам. Также, альтернативные менеджеры загрузки файлов (GetRight, FlashGet и т.п.) считают своим долгом оставить след в контекстном меню IE.

После этого небольшого вступления, я думаю стало понятно в каких приложениях это может пригодиться. Давайте теперь разберемся - как это работает. Чтобы не тренироваться на кошках, попробуем написать компонент, реализующий поиск по нашему любимому сайту (если кто не понял - RSDN.ru)

Покопаемся в реестре

Internet Explorer хранит информацию о различных расширениях в реестре. В частности, "нестандартные" пункты контекстного меню можно найти по следующему адресу:

HKEY_CURRENT_USER
    Software
        Microsoft
            Internet Explorer
                MenuExt


ПРИМЕЧАНИЕ
Добавляя разделы в ветвь HКEY_CURRENT_USER, вы влияете на настройки Internet Explorer'а только для текущего пользователя. Чтобы добавить требуемую функциональность для всех пользователей, следует использовать ветвь HKEY_LOCAL_MACHINE.

Не волнуйтесь, если раздел MenuExt на вашем компьютере отсутствует. Это всего лишь означает, что у вас пока нет дополнительных пунктов. В общем случае, чтобы добавить собственный элемент в контекстное меню, необходимо создать один раздел и пару параметров (выделены жирным). Параметры Contexts и Flags необязательны. Их значение будет пояснено ниже.

HKEY_CURRENT_USER
    Software
        Microsoft
            Internet Explorer
                MenuExt
                    <Текст элемента меню> = <URL скрипта>
                        DWORD Contexts = 0xXX 
                        DWORD Flags = 0xXX 

Таким образом, добавив по указанному пути новый ключ, мы создадим новый пункт контекстного меню. В качестве имени ключа следует указать текст, который будет использован при отображении меню. Также можно использовать символ "&", определяющий горячую клавишу. Значение по умолчанию для этого ключа должно содержать URL страницы, содержащей скрипт, который и будет выполнен при выборе данного элемента.

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

Скрипт обработки

Итак, меню добавлено, осталось написать скрипт. В следующем примере, показано, как по выбору пункта меню открыть новое окно с результатами поиска по сайту RSDN. В качестве строки для поиска передается текст выделения в активном окне.

Через свойство menuArguments объекта external можно обратиться к объекту окна (window), в котором было вызвано контекстное меню. Ну, а через этот объект легко получить доступ ко всей объектной модели Interner Explorer.

rsdnsearch.htm

<SCRIPT LANGUAGE="JavaScript" defer>

var parentwin = external.menuArguments;     // получаем объект окна
var doc = parentwin.document;               // получаем объект документа
var sel = doc.selection.createRange().text; // получаем текст выделения

// открываем новое окно с результатами поиска по сайту
Y=parentwin.open('http://www.rsdn.ru/cgi-bin/search.exe?sArt=On&sQnA=On&sForum=On&sRes=On&sFile=On&pcnt=20&extended=2&query='+sel,'Y','height=480,width=640,screenX=10,screenY=10,scrollbars=yes, toolbar=yes, titlebar=yes, status=yes, menubar=yes, location=yes, resizable');
</SCRIPT>

Использование COM-компонентов

Если по каким-либо причинам возможностей JavaScript/VBScript недостаточно, можно привлечь тяжелую артиллерию. Действительно, достаточно написать простой COM-объект и активизировать его из скрипта.

В следующем примере показан один из вариантов подобной реализации. В данном случае СОМ-объект содержит единственный метод Run, в который передается ссылка на объект окна.

gorsdn.htm

<SCRIPT language="VBScript">
    On Error Resume Next
    Set MyObj=CreateObject("GoRSDN.ContextItem")
    If err<>0 then
        MsgBox("Модуль GoRSDN не установлен!")
    Else        
        MyObj.Run external.menuArgements
    End If
</SCRIPT>

Получив в методе Run указатель на объект окна, можем сделать что-нибудь хорошее. Например, перейти на сайт RSDN.

STDMETHODIMP CMyObj::Run(LPDISPATHC pDispatch)
{
    // получаем объект window
    CComQIPtr<IHTMLWindow2> pWindow =  pDispatch;
    if (pWindow)
    {
        // Переходим на наш любимый сайт
        CComBSTR sUrl = L"http://www.rsdn.ru";
        pWindow->navigate( sUrl);
        return S_OK;
    }
    return E_FAIL;
}

Используя подобную технику, легко реализовать практически любую функциональность. Например, FlashGet использует похожий прием для реализации пунктов "Закачать при помощи FlashGet" и "Закачать все"

ПРИМЕЧАНИЕ
Несмотря на то, что мы создаем и используем COM-объект внутри Internet Explorer'а, помечать его как "безопасный" (CATID_SafeForScripting, CATID_SafeForInitializing ) необязательно.

Контекст отображения

В прошлом примере мы добавили собственный пункт меню к IE. Однако, если задуматься, отображение нашего пункта не всегда разумно. Например, бессмысленно добавлять команду "RSDN Search" в ответ на шелчок правой клавишей на картинке или элементе ActiveX. Следовательно, имеет смысл определить контекст отображения нашего пункта меню, т.е. сообщить IE, в каких случаях следует показывать наш пункт, а в каких нет. Это можно сделать добавив в раздел "&RSDN Search" необязательный параметр Contexts.

Параметр Contexts содержит идентификатор контекстного меню, определяющий в ответ на какое действие отображать даный пункт. Возможна побитовая комбинация следующих вариантов.

ContextsЗначение
По умолчанию0x1
Изображение0x2
Элемент управления0x4
Таблица0x8
Выделенный текст0x10
Ссылка0x20

К примеру, если мы хотим, чтобы наш пункт появлялся только на ссылке или при наличии выделенного фрагмента, необходимо в параметре Contexts прописать значение 0x30 (0x10 | 0x20).

Модальный режим


Задав другой необязательный параметр - Flags = 0x1, можно заставить IE выполнять скрипт в модальном режиме. При этом, создаваемый диалог не будет скрытым, а скрипт будет запущен подобно вызову метода ShowModalDialog. К исходному окну можно обратиться также через external.menuArguments. Позаботится о закрытии диалогового окна, в этом случае, придется самостоятельно.

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

rsdnsearch2.htm

<html style="width: 450px; height: 180px; margin: 10px">
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251">
<head>
    <title>RSDN Search</title>
</head>

<script language="JavaScript">
function BodyLoad()
{
    var parentwin = external.menuArguments;
    var doc = parentwin.document;
    var sel = doc.selection.createRange().text;
    query.value = sel;
}
function SearchClick()
{
    var parentwin = external.menuArguments;
    parentwin.navigate("http://www.rsdn.ru/?cgi-bin/search.exe?sArt=On&sQnA=On&sForum=On&sRes=On&sFile=On&pcnt=20&extended=2&query="+query.value);
    //закрыть диалог
    window.close();
}
</script>

<body bgcolor="#C0C0C0" onload="BodyLoad()">

<center><table style="width:420px">
<tr><td colspan=2>Запрос</td></tr>
<tr>
  <td width=100%><input type=text name="query" style="width:100%;" value=""></input>
  </td><td><input type=submit value="Найти" style="cursor:hand" onclick="SearchClick()"></input>&nbsp;
</td></tr></table></center>
<br>
<hr size="1">
<p align="right">
  <font face="ms sans serif" size="1">&copy;<a href="http://www.rsdn.ru" target="_blank"> 2000 RSDN Team.</a></font>
</p>
</body>
</html>

Как видите ничего сложного. Обычный HTML. Обычный JavaScript. При этом внешний вид HTML-диалога практически не отличается от "стандартного" диалога. В функции BodyLoad передаем выделенный фрагмент текста в строку редактирования. По нажатию кнопки "Найти", запускаем скрипт поиска на RSDN. Да, и не забываем закрыть окно.

Информация о событии

Увлекшись созданием предыдущих примеров, чуть не забыл рассказать про еще одну полезную возможность.

При выборе пункта меню, объект event окна источника (external.menuArguments.event) содержит некоторую полезную информацию, которую можно использовать из скрипта. Например, объект на котором щелкнули мышью, можно получить из свойства event.srcElement. А свойство event.type содержит одну из следующих строк, определяющих тип отображаемого меню:

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

Примеры, примеры

Для демонстрации вышеизложенного приведу четыре примера:

  1. RSDN Search Скрипт поиска по выделенному фрагменту текста
  2. RSDN Search2 HTML-диалог поиска по сайту
  3. Goto RSDN! СОМ-объект, реализующий переход на RSDN.ru
  4. RSDN Search СОМ-объект, реализующий поиск по выделенному фрагменту

1 и 2-ой примеры содержат скрипты WSH (Windows Script Host) избавляющие от ручного копания в реестре. Чтобы добавить новый пункт достаточно запустить файл install.vbs. Убедитесь, что каталоги файлов со скриптами указаны верно:

...
REG_NEWMENU = "HKCU\Software\Microsoft\Internet Explorer\MenuExt\&RSDN Search\"
' Записываем в реестр требуемые значения
shell.RegWrite REG_NEWMENU, "c:\rsdn\rsdnsearch.htm", "REG_SZ"
...

В третьем примере добавление соответствующего пункта меню происходит одновременно с регистрацией COM-объекта, поэтому запускать дополнительные скрипты не требуется. Убедитесь только, что скрипт GoRSDN.dll.htm лежит в том же каталоге, что и GoRSDN.dll.

ПРИМЕЧАНИЕ
Вы, наверное, обратили внимание на странное имя скрипта - GoRSDN.dll.htm. К сожалению, это обусловлено производственной необходимостью. Язык скриптов реестра в ATL (RGS - ReGistry Script) поддерживает простой и элегантный способ общения с реестром. Чтобы задать местоположение файла c COM-объектом, в нем используется предопределенная метка-заполнитель - %MODULE%. При вызове функции регистрации, эта метка заменяется на результат вызова функции GetModuleFileName. Существует возможность определять собственные метки-заполнители. Однако в данном случае, можно поступить проще, написав в RGS-файле следующее: '%MODULE%.htm'. При этом, в качестве URL скрипта будет прописан путь к файлу GoRSDN.dll.htm. Что, собственно, и требовалось.

Элегантное решение проблемы предложил Алексей Кирюшкин. Его способ продемонстрирован в четвертом примере. Воспользуемся тем обстоятельством, что Internet Explorer умеет загружать HTM-страницы из ресурсов. При этом можно импортировать скрипт в ресурсы DLL содержащей COM-объект. В этом случае соответствующий RGS-скрипт будет выглядеть так:

NoRemove MenuExt
    {
        '&RSDN Search' = s 'res://%MODULE%/GORSDN.DLL.HTM'
        {
           val Contexts = d '48'
        }
    }

Здесь, "GORSDN.DLL.HTM" - строковый идентификатор ресурса htm-скрипта в DLL.


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