Интеграция упакованного классического приложения с Проводником
Некоторые приложения для Windows определяют расширения Проводника, которые добавляют строки контекстного меню, позволяющие клиентам выполнять команды, связанные с приложением. Более старые технологии развертывания приложений Windows, такие как MSI и ClickOnce, определяют расширения проводника с помощью реестра. Реестр содержит ряд кустов, которые управляют расширениями Проводника и другими типами расширений оболочки. Эти установщики обычно создают набор разделов реестра для настройки различных элементов, включаемых в контекстное меню.
При упаковке приложения Windows с помощью MSIX реестр виртуализируется, поэтому приложение не может зарегистрировать расширения Проводника в реестре. Вместо этого необходимо определить расширения Проводника с помощью расширений пакета, которые определяются в манифесте пакета. Здесь описываются несколько способов этого.
Полные примеры кода, используемые в этой статье, можно найти на GitHub.
Добавление строки контекстного меню, поддерживающей параметры запуска
Одним из самых простых способов интеграции с Проводником является определение расширения пакета, которое добавляет приложение в список доступных приложений в контекстном меню, когда пользователь щелкает правой кнопкой мыши файл определенного типа в Проводнике. Когда пользователь открывает приложение, расширение может передавать параметры в приложение.
Этот сценарий имеет несколько ограничений.
- Он работает только в сочетании с функцией сопоставления типов файлов. В контекстном меню можно отобразить дополнительные команды только для типов файлов, связанных с основным приложением (например, чтобы приложение поддерживало открытие файла двойным щелчком по этому файлу в Проводнике).
- Пункты контекстного меню будут отображаться, только если приложение ассоциировано по умолчанию с этим типом файлов.
- Единственное поддерживаемое действие — запуск основного исполняемого файла приложения (то есть того же исполняемого файла, который связан с элементом меню "Пуск"). Однако каждое действие может сопровождаться различными параметрами, которые можно использовать, чтобы приложения могли понимать, какое действие активировало выполнение, и выполнять разные задачи.
Несмотря на эти ограничения, этот подход работает во множестве сценариев. Например, при создании редактора изображений можно легко добавить в контекстное меню элемент для изменения размера изображения, который непосредственно вызывает редактор изображений с мастером для изменения размера изображения.
Реализация элемента контекстного меню
Для поддержки этого сценария добавьте в манифест пакета элемент Extension с категорией windows.fileTypeAssociation
. Этот элемент следует добавлять в качестве дочернего элемента для элемента Extensions в элементе Application.
В следующем примере демонстрируется регистрация приложения, которая реализует контекстное меню для файлов с расширением .foo
. В этом примере определяется расширение .foo
, так как это не настоящее расширение, которое обычно не зарегистрировано в других приложениях на данном компьютере. Если необходимо управлять типом файла, который уже может быть занят (например, TXT или JPG), помните, что вы не сможете увидеть этот параметр, пока приложение не будет назначено по умолчанию для этого типа файлов. Этот пример является выдержкой из файла Package.appxmanifest в соответствующем примере на GitHub.
<Extensions>
<uap3:Extension Category="windows.fileTypeAssociation">
<uap3:FileTypeAssociation Name="foo" Parameters=""%1"">
<uap:SupportedFileTypes>
<uap:FileType>.foo</uap:FileType>
</uap:SupportedFileTypes>
<uap2:SupportedVerbs>
<uap3:Verb Id="Resize" Parameters=""%1" /p">Resize file</uap3:Verb>
</uap2:SupportedVerbs>
</uap3:FileTypeAssociation>
</uap3:Extension>
</Extensions>
В этом примере предполагается, что следующие пространства имен и псевдонимы объявляются в корневом элементе <Package>
манифеста.
<Package
xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
IgnorableNamespaces="uap uap2 uap3 rescap">
...
</Package>
Элемент FileTypeAssociation связывает приложение с типами файлов, которые требуется поддерживать. Дополнительные сведения см. в разделе Создание связи вашего упакованного приложения с набором типов файлов. Ниже приведены наиболее важные объекты, связанные с этим элементом.
Атрибут или элемент | Description |
---|---|
Атрибут Name |
Соответствует имени расширения, которое вы хотите зарегистрировать, за исключением точки (в предыдущем примере — foo ). |
Атрибут Parameters |
Содержит параметры, которые необходимо передать в приложение, когда пользователь дважды щелкает файл с таким расширением. Как правило, по меньшей мере вы передаете %1 , который является специальным параметром, содержащим путь к выбранному файлу. Таким образом, при двойном щелчке по файлу приложение знает полный путь и может загрузить его. |
Элемент SupportedFileTypes | Указывает имена для регистрируемого расширения, включая точку (в этом примере — .foo ). Можно указать несколько строк <FileType> , которые будут поддерживать больше типов файлов. |
Чтобы определить интеграцию контекстного меню, необходимо также добавить дочерний элемент SupportedVerbs. Этот элемент содержит один или несколько элементов Verb, определяющих параметры, которые будут показаны, когда пользователь щелкнет правой кнопкой мыши файл с расширением .foo в Проводнике. Дополнительные сведения см. в разделе Добавление параметров в контекстное меню файлов определенного типа. Ниже приведены наиболее важные пункты, связанные с элементом Verb.
Атрибут или элемент | Description |
---|---|
Атрибут Id |
Указывает уникальный идентификатор для действия. |
Атрибут Parameters |
Как и элемент FileTypeAssociation, этот атрибут для элемента Verb содержит параметры, которые передаются в приложение, когда пользователь щелкает строку контекстного меню. Как правило, кроме специального параметра %1 для получения пути к выбранному файлу можно передать один или несколько параметров для получения контекста. Это позволяет приложению понять, что оно было открыто из команды в контекстном меню. |
Значение элемента | Значение элемента Verb содержит метку, отображаемую в строке контекстного меню (в этом примере — Изменить размер). |
Доступ к параметрам запуска в коде приложения
Способ получения параметров приложением зависит от типа созданного приложения. Например, приложение WPF обычно обрабатывает аргументы события запуска в методе OnStartup
класса App
. Можно проверить наличие параметров запуска и, исходя из результата, выполнить наиболее подходящее действие (например, открыть специальное окно приложения вместо основного).
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
if (e.Args.Contains("Resize"))
{
// Open a specific window of the app.
}
else
{
MainWindow main = new MainWindow();
main.Show();
}
}
}
На снимке ниже экрана показана строка контекстного меню Изменить размер, созданная в предыдущем примере.
Поддержка обычных файлов или папок и выполнение сложных задач
Хотя использования расширения FileTypeAssociation в манифесте пакета, как описано в предыдущем разделе, достаточно для многих сценариев, его может оказаться недостаточно. Причинами этого могут оказаться две большие проблемы.
- Вы можете работать только с ассоциированными типами файлов. Например, нельзя управлять обычной папкой.
- Приложение можно запустить только с помощью набора параметров. Вы не можете выполнять сложные операции, такие как запуск другого исполняемого файла или выполнение задачи, не открывая основное приложение.
Для достижения этих целей необходимо создать расширение оболочки, которое предоставляет более эффективные способы интеграции с Проводником. В этом сценарии создается библиотека DLL, содержащая все необходимое для управления контекстным меню файла, в том числе метку, значок, состояние и выполняемые задачи. Поскольку эта функция реализована в библиотеке DLL, вы можете делать практически все, что можно делать с обычным приложением. После реализации библиотеки DLL необходимо зарегистрировать ее с помощью расширений, определенных в манифесте пакета.
Примечание.
Процесс, описанный в этом разделе, имеет одно ограничение. После установки пакета MSIX, содержащего расширение, на целевом компьютере необходимо перезапустить Проводник, прежде чем можно будет загрузить расширение оболочки. Для этого пользователь может перезагрузить компьютер или перезапустить процесс explorer.exe с помощью Диспетчера задач.
Реализация расширения оболочки
Расширения оболочки основаны на модели COM. Библиотека DLL предоставляет один или несколько COM-объектов, зарегистрированных в реестре системы. Windows обнаруживает эти COM-объекты и интегрирует расширение с Проводником. Так как вы интегрируете код с оболочкой Windows, нужно учитывать производительность и объем используемой памяти. Поэтому эти типы расширений обычно создаются на C++.
Пример кода, демонстрирующий способы реализации расширений оболочки, см. в проекте ExplorerCommandVerb в соответствующем примере на сайте GitHub. Этот проект основывается на этом примере в примерах для настольных систем Windows, и содержит несколько редакций, чтобы упростить его использование с последними версиями Visual Studio.
Этот проект содержит много типовых кодов для различных задач, таких как динамические и статические меню, а также ручная регистрация библиотеки DLL. Большая часть этого кода не требуется, если вы упаковываете приложение с помощью MSIX, так как этими задачами будет заниматься поддержка пакетов. Файл ExplorerCommandVerb.cpp содержит реализацию контекстного меню, и это основной файл кода, которому посвящено это пошаговое руководство.
Основная функция — это CExplorerCommandVerb::Invoke
. Это функция, которая вызывается, когда пользователь щелкает строку в контекстном меню. В этом примере, чтобы уменьшить снижение производительности, операция выполняется в другом потоке, поэтому фактическую реализацию можно найти в CExplorerCommandVerb::_ThreadProc
.
DWORD CExplorerCommandVerb::_ThreadProc()
{
IShellItemArray* psia;
HRESULT hr = CoGetInterfaceAndReleaseStream(_pstmShellItemArray, IID_PPV_ARGS(&psia));
_pstmShellItemArray = NULL;
if (SUCCEEDED(hr))
{
DWORD count;
psia->GetCount(&count);
IShellItem2* psi;
HRESULT hr = GetItemAt(psia, 0, IID_PPV_ARGS(&psi));
if (SUCCEEDED(hr))
{
PWSTR pszName;
hr = psi->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pszName);
if (SUCCEEDED(hr))
{
WCHAR szMsg[128];
StringCchPrintf(szMsg, ARRAYSIZE(szMsg), L"%d item(s), first item is named %s", count, pszName);
MessageBox(_hwnd, szMsg, L"ExplorerCommand Sample Verb", MB_OK);
CoTaskMemFree(pszName);
}
psi->Release();
}
psia->Release();
}
return 0;
}
Когда пользователь щелкает правой кнопкой мыши файл или папку, эта функция отображает окно сообщения с полным путем к выбранному файлу или папке. Если вы хотите настроить расширение оболочки другими способами, можно расширить следующие функции в примере:
- Чтобы настроить метку записи в контекстном меню, можно изменить функцию GetTitle.
- Функцию GetIcon можно изменить, чтобы настроить значок, отображаемый рядом со строкой в контекстном меню.
- Функцию GetTooltip можно изменить, чтобы настроить подсказку, которая отображается при наведении указателя мыши на строку в контекстном меню.
Регистрация расширения оболочки
Так как расширение оболочки основано на модели COM, реализация DLL должна быть представлена как сервер COM, чтобы Windows могла интегрировать ее с Проводником. Как правило, это делается путем присвоения серверу COM уникального идентификатора (называемого CLSID) и его регистрации в определенном кусте системного реестра. В проекте ExplorerCommandVerb идентификатор CLSID для расширения CExplorerCommandVerb
определяется в файле DLL.h.
class __declspec(uuid("CC19E147-7757-483C-B27F-3D81BCEB38FE")) CExplorerCommandVerb;
Упаковка библиотеки DLL расширения оболочки в пакет MSIX реализуется с тем же подходом. Однако идентификатор GUID должен быть зарегистрирован в манифесте пакета, а не в реестре, как описано здесь.
В манифесте пакета начните с добавления следующих пространств имен в элемент Package.
<Package
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10"
IgnorableNamespaces="desktop desktop4 desktop5 com">
...
</Package>
Чтобы зарегистрировать CLSID, добавьте элемент com.Extension с категорией windows.comServer
в манифест пакета. Этот элемент следует добавлять в качестве дочернего элемента для элемента Extensions в элементе Application. Этот пример является выдержкой из файла Package.appxmanifest в соответствующем примере на GitHub.
<com:Extension Category="windows.comServer">
<com:ComServer>
<com:SurrogateServer DisplayName="ContextMenuSample">
<com:Class Id="CC19E147-7757-483C-B27F-3D81BCEB38FE" Path="ExplorerCommandVerb.dll" ThreadingModel="STA"/>
</com:SurrogateServer>
</com:ComServer>
</com:Extension>
Существует два критических настраиваемых атрибута в элементе com:Class.
Атрибут | Description |
---|---|
Атрибут Id |
Он должен совпадать с идентификатором CLSID объекта, который необходимо зарегистрировать. В этом примере это CLSID, объявленный в файле Dll.h , связанном с классом CExplorerCommandVerb . |
Атрибут Path |
Он должен содержать имя библиотеки DLL, предоставляющей объект COM. В этом примере библиотека DLL содержится в корне пакета, поэтому можно просто указать имя библиотеки DLL, созданной проектом ExplorerCommandVerb . |
Затем добавьте еще одно расширение, которое регистрирует контекстное меню файла. Для этого добавьте элемент desktop4:Extension с категорией windows.fileExplorerContextMenus
в манифест пакета. Этот элемент также следует добавлять в качестве дочернего элемента для элемента Extensions в элементе Application.
<desktop4:Extension Category="windows.fileExplorerContextMenus">
<desktop4:FileExplorerContextMenus>
<desktop5:ItemType Type="Directory">
<desktop5:Verb Id="Command1" Clsid="CC19E147-7757-483C-B27F-3D81BCEB38FE" />
</desktop5:ItemType>
</desktop4:FileExplorerContextMenus>
</desktop4:Extension>
Существует два критических настраиваемых атрибута в элементе desktop4:Extension.
Атрибут или элемент | Description |
---|---|
Атрибут Type в desktop5:ItemType |
Определяет тип элементов, которые необходимо связать с контекстным меню. Это может быть звездочка (* ), если вы хотите отобразить ее для всех файлов. Это также может быть определенное расширение файла (.foo ) или он может быть доступен для папок (Directory ). |
Атрибут Clsid в desktop5:Verb |
Он должен соответствовать идентификатору CLSID, который ранее был зарегистрирован как сервер COM в файле манифеста пакета. |
Настройка библиотеки DLL в пакете
Добавьте библиотеку DLL, которая реализует расширение оболочки (в этом примере ExplorerCommandVerb.dll) в корневую папку пакета MSIX. Если вы используете проект упаковки приложений Windows, самым простым решением является копирование и вставка библиотеки DLL в проект и проверка того, что для параметра Копировать в выходной каталог свойств DLL-файла задано значение Копировать, если новее.
Чтобы гарантировать, что пакет всегда содержит последнюю версию библиотеки DLL, в проект расширения оболочки можно добавить событие после сборки таким образом, чтобы при каждой сборке библиотека DLL копировалась в проект упаковки приложений Windows.
Перезапуск Проводника
После установки пакета расширения оболочки необходимо перезапустить Проводник, прежде чем можно будет загрузить расширение оболочки. Это ограничение расширений оболочки, которые разворачиваются и регистрируются с помощью пакетов MSIX.
Чтобы проверить расширение оболочки, перезапустите компьютер или перезапустите процесс explorer.exe с помощью Диспетчера задач. После этого вы сможете увидеть эту строку в контекстном меню.
Если щелкнуть ее, будет вызвана функция CExplorerCommandVerb::_ThreadProc
для вывода окна сообщения с путем к выбранной папке.
Windows developer