Как создать расширение оболочки Context menu

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

В библиотеке Shell Ace обработчик Context menu реализует следующие интерфейсы:

Для создания проекта расширения необходимо добавить в проект модуль с обработчиком Context menu. Для этого выберите соответствующую иконку на вкладке Shell extension:

Создание обработчика context menu

Появится окно с предложением ввести имя класса и выбрать дополнительные перекрываемые методы:

Методы обработчика context menu

После нажатия на кнопку OK в проект будет добавлен модуль с каркасом обработчика. При включении опции Create sample помимо каркаса обработчика будут создан пример готового расширения оболочки, который можно использовать для изучения библиотеки.

В обработчике можно перекрывать следующие виртуальные процедуры и функции:

procedure Initialize;

Процедура инициализации обработчика, вызывается один раз при создании экземпляра обработчика.

Если создаваемое расширение не содержит пункта меню по умолчанию, то можно оптимизировать работу Windows, установив в этом методе значение свойства MayChangeDefaultMenu в значение False.

procedure Clear;

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

class function GetClassID: TCLSID;

Функция должна возвращать уникальный идентификатор обработчика. Библиотека Shell Ace изначально генерирует уникальный идентификатор при генерации шаблона обработчика, поэтому менять его нет необходимости.

class function GetDescription: UnicodeString;

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

procedure FillProgIDList(AList: TStrings);

В процедуре должно происходить заполнение списка AList ключами реестра, в которые будет прописан обработчик при регистрации расширения. Регистрация происходит в раздел реестра HKEY_CLASSES_ROOT. Например, если будет добавлен ключ .bmp и будет соответствовать всем файлам, имеющим расширение bmp, то расширение будет прописано в ветку HKEY_CLASSES_ROOT\.bmp\ShellEx\ContextMenuHandlers, а если будет добавлен ключ SystemFileAssociations\Audio, то расширение будет прописано в ветку HKEY_CLASSES_ROOT\SystemFileAssociations\Audio\ShellEx\ContextMenuHandlers и будет соответствовать всем файлам, расширения которых имеют PerceivedType Audio.

Примеры ключей реестра:

Ключ реестраПримечание
ProgID
.Расширение
SystemFileAssociations\.Расширение
SystemFileAssociations\PerceivedType
Kind.Kind
UnknownФайл, не имеющий описанного в реестве расширения
*Любой файл
DirectoryФизическая директория
FolderВиртуальная директория

В общем случае рекомендуется регистрировать расширение на ProgID расширения файла.

procedure CreateMenu(AMenu: TdecMenu; AFlags: TdecContextMenuFlags);

Процедура должна создать дерево пунктов меню, включая вложенные в подменю. Процедура вызывается в реализации метода QueryContextMenu интерфейса IContextMenu.

Для формирования меню можно дополнительно анализировать флаги AFlags. Тип TdecContextMenuFlags определен следующим образом:

TdecContextMenuFlag = (cmfDefaultOnly, cmfVerbsOnly, cmfExplorer, cmfNoVerbs, cmfNoDefault, cmfItemMenu, cmfExtendedVerbs, cmfDisabledVerbs, cmfAsyncVerbState, cmfOptinizaForInvoke, cmfSyncCascadeMenu, cmfDoNotPickDefault);
TdecContextMenuFlags = set of TdecContextMenuFlag;

Каждый из элементов множества соответствует соответствующему флагу, переданному в метод QueryContextMenu:

ЗначениеWinAPI флагОписание
cmfDefaultOnlyCMF_DEFAULTONLYОболочка просит добавить только пункт меню по умолчанию. Библиотека Shell Ace самостоятельно обрабатывает этот флаг - независимо от того, сколько пунктов было добавлено, библиотека оставит только один пункт, имеющий значение свойства Default равное True.
cmfVerbsOnlyCMF_VERBSONLYThe shortcut menu is that of a shortcut file
cmfExploreCMF_EXPLOREThe Windows Explorer tree window is present.
cmfNoVerbsCMF_NOVERBSThis flag is set for items displayed in the Send To menu. Shortcut menu handlers should ignore this value.
cmfNoDefaultCMF_NODEFAULTNo item in the menu has been set as the default. A drag-and-drop handler should ignore this flag. A namespace extension should not set any of the menu items as the default.
cmfItemMenuCMF_ITEMMENUThe calling application is invoking a shortcut menu on an item in the view (as opposed to the background of the view). Windows Server 2003 and Windows XP: This value is not available.
cmfExtendedVerbsCMF_EXTENDEDVERBSThe calling application wants extended verbs. Normal verbs are displayed when the user right-clicks an object. To display extended verbs, the user must right-click while pressing the Shift key.
cmfDisabledVerbsCMF_DISABLEDVERBSThe calling application intends to invoke verbs that are disabled, such as legacy menus. Windows Server 2003 and Windows XP: This value is not available.
cmfAsyncVerbStateCMF_ASYNCVERBSTATEThe verb state can be evaluated asynchronously. Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not available.
cmfOptinizaForInvokeCMF_OPTIMIZEFORINVOKEОболочка просит добавить в меню только те пункты, для которых установлено свойство Verb. Библиотека Shell Ace самостоятельно обрабатывает этот флаг - все пункты меню, у которых свойство Verb имеет пустое значение, будут автоматически удалены.
cmfSyncCascadeMenuCMF_SYNCCASCADEMENUPopulate submenus synchronously. Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not available.
cmfDoNotPickDefaultCMF_DONOTPICKDEFAULTWhen no verb is explicitly specified, do not use a default verb in its place. Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not available.

Добавление пункта происходит путем вызова метода Add объекта AMenu, который возвращает объект класса TdecMenuItem:

var MenuItem: TdecMenuItem;
MenuItem := AMenu.Add(CommandID, 'Caption');

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

НаименованиеОписание
Caption: UnicodeString

Определяет текст, который будет выводиться в пункте меню.

Help: UnicodeString

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

Verb: UnicodeString

Определяет сокращенное название действия, например, print или open. Используется при поиске команды при открытии файла программно.

Icon: HICON

Определяет иконку, которая будет отображаться в данном пункте меню.

IconFileName: UnicodeString

В случае, если свойство Icon не определено (равно нулю), то определяет имя файла и индекс ресурса, в котором содержится иконка, которая будет отображаться в данном пункте меню. Для определения иконки рекомендуется использовать данное свойство, а не свойство Icon, поскольку это позволит программе Проводник отображать иконку команды при добавлении ее в панель инструментов.

AutoIconSize: Boolean

Определяет, что размеры иконки элемента меню будут автоматически увеличиваться или уменьшаться до размеров, определенных системой. По умолчанию значение свойства установлено в True, менять его не рекомендуется. Свойство проявляет себя в нестандартных визуальных темах оформления, в которых размер шрифта в меню больше или меньше стандартного, или при увеличенном DPI экрана.

Так выглядит меню с добавленными пунктами Register и Unregister при стандартном DPI, равном 96:

Контекстное меню при DPI 96

Так выглядит меню при увеличенном DPI, равном 150, с AutoIconSize равном False:

Контекстное меню при DPI 150 c AutoIconSize равном False

А так выглядит меню при увеличенном DPI, равном 150, с AutoIconSize равном True:

Контекстное меню при DPI 150 AutoIconSize равном True
Default: Boolean

Определяет, что данный пункт меню является пунктом меню по умолчанию, при прорисовке выделяется утолщенным шрифтом. Только один пункт меню может являться пунктом меню по умолчанию. Если иное расширение оболочки раньше создало пункт меню по умолчанию, то данное свойство будет проигнорировано.

Enabled: Boolean

Определяет, что данный пункт меню разрешен для вызова. По умолчанию имеет значение True.

Visible: Boolean

Определяет, что данный пункт меню является видимым. По умолчанию имеет значение True.

Checked: Boolean

Определяет, что пункт меню будет отмечен галочкой.

RadioCheck: Boolean

Определяет, что в случае, если свойство Checked равно True, то вместо галочки будет использоваться точка.

OwnerDraw: Boolean

Определяет, что пункт меню имеет программную прорисовку, при этом должны быть реализованы методы GetItemSize и DrawItem.

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

Контекстное меню, прорисованное с использованием визуальной темы

А так выглядит контекстное меню с добавленным пунктом, имеющим программную прорисовку:

Контекстное меню с добавленным пунктом, имеющим программную прорисовку

Добавление пунктов по вложенные меню происходит при вызове метода Add свойства SubMenu:

MenuItem2 := MenuItem.SubMenu.Add(CommandID2);

Построение меню может происходить в зависимости от количества или типа файлов, переданных в обработчик. Список переданных файлов можно получить вызовом метода CreateCurrentFileList. Функция возвращает объект класса TStrings, содержащий список файлов. В версиях Delphi вплоть до версии 2009, в которых тип string равен типу AnsiString, имена файлов в списке хранятся в формате UTF8. Объект должен быть уничтожен после его обработки. Также доступны функции GetCurrentFileListCount, которая возвращает количество файлов, и GetFirstOfCurrentFileList, которая возвращает первый элемент списка (с нулевым индексом).

function CreateItemIcon(AMenuItem: TdecMenuItem; const AIconSize: TSize): HICON;

Функция должна вернуть идентификатор (handle) иконки, которая будет ассоциирована с пунктом меню, в случае отсутствия иконки Функция должна вернуть 0. Значение свойства AMenuItem.ID определяет, для какого пункта меню нужно создать иконку.

Параметр AIconSize определяет рекомендуемые размеры иконки.

Для пунктов меню, имеющих свойство AutoIconSize равное True можно создать иконку любого размера, ее размер при прорисовке будет изменет автоматически до рекомендованного Windows.

Необходимости удалять иконку из памяти нет, поскольку по окончанию работы с иконкой библиотека Shell Ace сама удалит все ресурсы, связанные с иконкой.

В ОС Windows XP и более ранних версиях Windows иконка не может быть ассоциирована с пунктами меню, имеющими вложенные меню. В Windows Vista и более современных системах эта проблема отсутствует.

Если у вас в среде разработки установлен пакет Graphics32, то при использовании иконок в контекстном меню рекомендуется добавлять в проект модуль decShellExtensionGraphics32. Это позволит обработчику использовать более плавные процедуры сглаживания иконок при увеличении или уменьшении их размеров. Это проявляется при прорисовках пунктов меню с иконками и свойством AutoIconSize равным True в режимах работы с увеличенным DPI экрана.

Так выглядит меню с добавленными пунктами Register и Unregister при увеличенном DPI, равном 150, с AutoIconSize равном True без использования модуля decShellExtensionGraphics32:

Контекстное меню без использования модуля decShellExtensionGraphics32

А так выглядит меню при увеличенном DPI, равном 150, с AutoIconSize равном True с использованием модуля decShellExtensionGraphics32:

Контекстное меню с использованием модуля decShellExtensionGraphics32

Если библиотека Shell Ace обнаружит установленный пакет Graphics32, то модуль decShellExtensionGraphics32 будет автоматически добавлен в список используемых модулей при создании каркаса обработчика.

function GetItemSize(AMenuItem: TdecMenuItem): TSize;

Метод вызывается для определения размеров пунктов меню, имеющих режим DrawMode равный dmCustom. Значение свойства AMenuItem.ID определяет, размеры какого именно пункта меню нужно определить.

procedure DrawItem(AMenuItem: TdecMenuItem; ADC: HDC; const ARect: TRect; ADrawState: TOwnerDrawState);

Метод вызывается для прорисовки пунктов меню, имеющих режим DrawMode равный dmCustom. Свойство AMenuItem.ID определяет, какой именно пункта меню нужно прорисовать. Библиотека Shell Ace самостоятельно отрисовывает фон пункта меню в соответствии с цветовой схемой, используемой в системе.

Параметр ADC определяет контекст, в котором нужно рисовать. Параметр ARect определяет положение и размеры области, отведенной для пункта меню. Размеры области могут быть больше или меньше, чем полученные в функции GetItemSize. Параметр ADrawState определяет стиль, в котором нужно прорисовать пункт меню.

function ExecuteCommand(AMenuItem: TdecMenuItem; AInfo: PShellExecuteInfoW; ATitle: UnicodeString; AShiftState: TShiftState; APoint: PPoint): Boolean;

Функция вызывается при выборе пользователем пункта меню, свойство AMenuItem.ID определяет, какой именно пункта меню был выбран. В этой функции необходимо реализовать непосредственное выполнение команды. В параметре AInfo передаются ссылка на запись, которая может использоваться при вызове системной функции ShellExecuteExW. Нижеперечисленные поля записи могут быть инициализированы, остальные поля содержат значение 0:

НаименованиеОписание
cbSize: DWORDSizeOf(ЕShellExecuteInfoW)
fMask: ULONGКомбинация флагов SEE_MASK_ICON, SEE_MASK_HOTKEY, SEE_MASK_NOCLOSEPROCESS, SEE_MASK_CONNECTNETDRV, SEE_MASK_NOASYNC, SEE_MASK_FLAG_NO_UI, SEE_MASK_NO_CONSOLE, SEE_MASK_HASLINKNAME, SEE_MASK_HASTITLE, SEE_MASK_ASYNCOK, SEE_MASK_HMONITOR, SEE_MASK_NOZONECHECKS, SEE_MASK_WAITFORINPUTIDLE и SEE_MASK_FLAG_LOG_USAGE. При использовании этого параметра рекомендуется отфильтровывать флаги, которые нужны для обработки команды.
Wnd: HWNDИдентификатор (хендл) окна, которое следует использовать как родительское при UI операциях.
lpParameters: LPCWSTRПараметры
lpDirectory: LPCWSTRРабочая директория
nShow: IntegerSW_SHOW или альтернативный флаг
hkeyClass: HKEY
dwHotKey: DWORDКод горячей клавиши, которая будет ассоциирована с запускаемым приложением. Значение валидно, только если fMask содержит флаг CMIC_MASK_HOTKEY.
hIcon: THandleИдентификатор (handle) иконки, которая может использоваться вызываемым приложением в качестве иконки приложения. Значение валидно, только если fMask содержит флаг CMIC_MASK_ICON.

ATitle определяет строку, которая может использоваться запускаемым приложением в качестве заголовок главного окна, начиная с Windows Vista не используется. AShiftState определяет состояние клавиш Alt, Ctrl и Shift. Указатель APoint указывает на абсолютные координаты мыши в момент вызова команды пользователем, может быть равен nil.

Список файлов для обработки НУЖНО получать методом CreateCurrentFileList, поскольку он может отличаться в большую сторону от того списка, который был получен в методе CreateMenu. Это происходит потому, что при вызове пользователем меню для большого количества файлов Windows для создания меню передает лишь часть списка, а уже при вызове команды передает весь список. Поэтому НЕ кэшируйте список, а всегда вызывайте функцию CreateCurrentFileList повторно!

Обязательными для реализации в обработчике Context menu являются следующие методы:

Смотрите также: