Расширение оболочки Windows с помощью С#

Я хотел написать простое расширение оболочки Windows для добавления в контекстное меню, а С# - это язык, который я больше всего использую в эти дни. Это достойный выбор для расширения оболочки? Доступны ли интерфейсы для этого? Есть ли дополнительные накладные расходы, которые заставляют меню медленнее всплывать?

У кого-нибудь есть хорошие указатели для начала?

Ответ 1

Пост Раймонда: не пишите внутрипроцессные расширения оболочки в управляемом коде.


Недавнее продолжение: теперь, когда версия .NET Framework 4 поддерживает параллельные среды выполнения в процессе, можно ли писать расширения оболочки в управляемом коде?

Суть в том, нет, это не хорошо:

Руководство по реализации внутрипроцессных расширений было пересмотрено, и в нем продолжаются рекомендации по написанию расширений оболочки и расширений Internet Explorer (и других типов внутрипроцессных расширений) в управляемом коде, даже если вы используете версию 4 или выше.

Ответ 2

Рискуя выглядеть как шилл, EZShellExtensions - замечательная (но несвободная) структура для расширения оболочки в С#. Вы можете написать простое расширение контекстного меню с примерно 20 строками кода и, самое главное, никогда не придется возиться с COM-интерфейсами. Моя компания использует его (и их инфраструктуру расширения пространства имен) для набора расширений, которые в настоящее время используются десятками тысяч клиентов, и для чего мы стоим, у нас никогда не было проблемы с конфликтом CLR, описанным выше.

Вот простой пример, чтобы показать, насколько это просто:

[Guid("00000000-0000-0000-0000-000000000000"), ComVisible(true)]
[TargetExtension(".txt", true)]
public class SampleExtension : ContextMenuExtension
{
   protected override void OnGetMenuItems(GetMenuitemsEventArgs e)
   {
      e.Menu.AddItem("Sample Extension", "sampleverb", "Status/help text");
   }

   protected override bool OnExecuteMenuItem(ExecuteItemEventArgs e)
   {
      if (e.MenuItem.Verb == "sampleverb")
         ; // logic
      return true;
   }

   [ComRegisterFunction]
   public static void Register(Type t)
   {
      ContextMenuExtension.RegisterExtension(typeof(SampleExtension));
   }

   [ComUnregisterFunction]
   public static void UnRegister(Type t)
   {
      ContextMenuExtension.UnRegisterExtension(typeof(SampleExtension));
   }
}

Ответ 3

Руководство по внедрению внутрипроцессорных расширений

Конфликты версий

Конфликт версий может возникать через среду выполнения, которая не поддерживает загрузку нескольких версий исполнения во время одного процесса. Версии CLR до версии 4.0 относятся к этой категории. Если загрузка одной версии среды выполнения исключает загрузку других версий той же среды выполнения, это может создать конфликт, если хост-приложение или другое внутреннее расширение использует конфликтующую версию. В случае конфликта версий с другим внутренним расширением, конфликт может быть трудно воспроизвести, поскольку для отказа требуются правильные конфликтующие расширения, а режим отказа зависит от порядка загрузки конфликтующих расширений.

Рассмотрим внутреннее расширение, написанное с использованием версии CLR до версии 4.0. Каждое приложение на компьютере, которое использует диалоговое окно "Открыть файл", потенциально может иметь управляемый диалогом код и связанную с ним зависимость CLR, загруженную в процесс приложения. Приложение или расширение, которое вначале загружает версию CLR до 4.0, в прикладной процесс ограничивает, какие версии CLR могут использоваться впоследствии этим процессом. Если управляемое приложение с диалоговым окном Open построено на конфликтующей версии CLR, расширение может не работать правильно и может привести к сбоям в приложении. И наоборот, если расширение является первым для загрузки в процессе, и после этого начинает запускаться конфликтующая версия управляемого кода (возможно, управляемое приложение или работающее приложение загружает CLR по требованию), операция завершается с ошибкой. Пользователю кажется, что некоторые функции приложения случайным образом перестают работать, или приложение загадочно сбой.

Обратите внимание, что версии CLR, равные или более поздние, чем версия 4.0, обычно не чувствительны к проблеме с версией, поскольку они предназначены для совместного сосуществования друг с другом и с большинством версий 4.0 CLR (за исключением версии 1.0, который не может сосуществовать с другими версиями). Однако могут возникнуть проблемы, отличные от конфликтов версий, как обсуждалось в оставшейся части этого раздела.

Проблемы с производительностью

Проблемы с производительностью могут возникать с временем выполнения, которое накладывает значительное снижение производительности при загрузке в процесс. Снижение производительности может быть в виде использования памяти, использования ЦП, прошедшего времени или даже использования пространства адресов. Известно, что CLR, JavaScript/ECMAScript и Java являются высокоэффективными. Поскольку внутрипроцессные расширения могут быть загружены во многие процессы и часто выполняются так в моменты чувствительности к производительности (например, при подготовке меню, которое будет отображаться пользователем), высокопроизводительные сеансы могут негативно влиять на общую отзывчивость.

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

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

Проблемы, связанные с .NET Framework

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

  • Re-entrancy
    Когда CLR блокирует поток однопоточной квартиры (STA), например, из-за действия Monitor.Enter, WaitHandle.WaitOne или заявленной блокировки, CLR в своей стандартной конфигурации входит в замкнутый контур сообщения во время ожидания. Многим методам расширения запрещено обрабатывать сообщения, и это непредсказуемое и неожиданное возвращение может привести к аномальному поведению, которое трудно воспроизвести и диагностировать.

  • Многопользовательская квартираCLR создает Runtime Callable Wrappers для объектов объектной модели объекта (COM). Эти же Runtime Callable Wrappers уничтожаются позже с помощью финализатора CLR, который является частью многопоточной квартиры (MTA). Перемещение прокси из STA в MTA требует маршалинга, но не все интерфейсы, используемые расширениями, могут быть отсортированы.

  • Недетерминированные объекты
    CLR обладает более слабыми гарантиями жизни объекта, чем собственный код. Многие расширения имеют требования к счетчикам ссылок на объекты и интерфейсы, а модель сбора мусора, используемая CLR, не может выполнить эти требования.

    • Если объект CLR получает ссылку на COM-объект, ссылка на объект COM, хранящаяся в Runtime Callable Wrapper, не освобождается до тех пор, пока сборщик данных Runtime Callable Wrapper не будет собран. Недетерминированное поведение релиза может конфликтовать с некоторыми контрактами интерфейса. Например, метод IPersistPropertyBag:: Load требует, чтобы ссылка на мешок свойств не сохранялась объектом при возврате метода Load.
    • Если ссылка на объект CLR возвращается в собственный код, Runtime Callable Wrapper отказывается от ссылки на объект CLR, когда завершается окончательный вызов Runtime Wableper для Release, но основной объект CLR не завершен до тех пор, пока он не станет мусором, собраны. Недетерминированная финализация может конфликтовать с некоторыми контрактами интерфейса. Например, обработчики эскизов должны немедленно освобождать все ресурсы, когда их количество ссылок падает до нуля.

Допустимое использование управляемого кода и других временных рядов

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

  • Обработчики предварительного просмотра
  • Действия на основе командной строки, такие как зарегистрированные в разделе оболочки \verb\command.
  • COM-объекты, реализованные на локальном сервере, для точек расширения Shell, которые позволяют активировать вне процесса.

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

  • IExecuteCommand, связанный с элементом DelegateExecute, зарегистрированным в разделе оболочки \verb \.
  • IDropTarget, связанный с CLSID, зарегистрированным в разделе оболочки \verb\DropTarget.
  • IExplorerCommandState, связанный с записью CommandStateHandler, зарегистрированной в разделе оболочки \verb.

SharpShell

SharpShell упрощает создание расширений оболочки Windows с помощью .NET Framework.

Исходный код размещен на https://github.com/dwmkerr/sharpshell - вы можете размещать вопросы и запрос функции здесь или там. Поддерживаемые расширения

Вы можете использовать SharpShell для построения любого из следующих расширений:

  • Контекстные меню оболочки
  • Обработчики иконок
  • Информационные обработчики подсказок
  • Обработчики кадров
  • Обработчики предварительного просмотра
  • Обработчики наложения значков
  • Thumbnail Hanlders
  • Расширения листов свойств

Проекты, использующие SharpShell
1. Контекстное меню Trello
2. REAL Shuffle Player 2.0

Серия статей в CodeProject