Posted on 12. May 2017

Поддержка COM-сервера и OLE-документа для Desktop Bridge

Read this article in your language IT | EN | DE | ES

Windows 10 Creators Update добавляет поддержку автономного (OOP) COM и OLE для приложений на Desktop Bridge - так называемого Packaged COM. В прошлом, приложения Win32 создавали COM-расширения, которые могли бы использовать другие приложения. Например, Microsoft Excel раскрывает Excel.Application, поэтому сторонние приложения могут автоматизировать операции в Excel, используя свою богатую объектную модель. Но в первоначальном выпуске Desktop Bridge с Windows 10 Anniversary Update  приложение не может выставлять точки расширения COM, поскольку все записи реестра находятся в его частном улье и публично не отображаются в системе. Packaged COM предоставляет механизм для записей COM и OLE, объявляемых в манифесте,  чтобы они могли использоваться внешними приложениями. Базовая система обрабатывает активацию объектов, чтобы их можно было потреблять клиентами COM - все это еще невыполненные обещания Universal Install Platform (UWP): не оказывать никакого воздействия на установку и удаление.

Как это работает

Packaged COM считываются из манифеста и сохраняются в новом каталоге, которым управляет система развертывания UWP. Это решает одну из главных проблем в COM, в которой любое приложение или установщик может писать в реестр и повреждать систему, например, перезаписывая существующие регистрации COM или оставляя записи реестра при удалении. 

Во время выполнения COM-вызова, то есть вызова CLSIDFromProgID() или CoCreateInstance(), система сначала просматривается в каталоге Packaged COM, и если она не найдена, возвращается обратно в системный реестр. Затем, COM-сервер активируется и запускает ООP из клиентского приложения.

Когда использовать Packaged COM

Packaged COM очень полезен для приложений, которые предоставляют сторонние точки расширения, однако не все приложения нуждаются в этом. Если ваше приложение использует COM только для личного использования, вы можете полагаться на записи COM в частном улье (Registry.dat), чтобы поддержать ваше приложение. Все бинарные файлы в одном и том же пакете имеют доступ к этому реестру, но любые другие приложения в системе не могут видеть их в вашем частном улье. Packaged COM позволяет вам чётко контролировать, какие серверы могут быть опубликованы и использованы сторонними разработчиками.

Ограничения

Поскольку записи Packaged COM хранятся в отдельном каталоге, приложения, которые непосредственно читают реестр (например, вызывая RegOpenKeyEx () или RegEnumKeyEx ()), не будут видеть никаких записей и следовательно - не сработают. В этих сценариях приложениям, которые предоставляют расширения, необходимо будет работать со своими партнерами, чтобы выполнить вызовы COM API или предоставить другой механизм связи между приложениями.

Поддержка распространяется на серверы OOP, что позволяет выполнять два ключевых требования. Во-первых, поддержка сервера OOP означает, что Desktop Bridge может сохранить свою работоспособность. Запустив расширения OOP, диспетчер обновлений может закрыть COM-сервер и обновить все двоичные файлы, потому что в нем нет используемых библиотек DLL, загружаемых другими процессами. Во-вторых, OOP допускает более надежный механизм расширения. Если внутрипроцессный COM-сервер зависает, также зависнет и приложение; для OOP размещенное приложение будет по-прежнему функционировать и сможет решить, как обращаться с неподобающим ООP-сервером.

Мы не поддерживаем все записи регистрации COM и OLE, полный список поддерживаемых нами компонентов можно найти в иерархии элементов манифеста пакета приложения Windows 10 на MSDN: https://docs.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/root-elements

Рассмотрим внимательнее

Ключами к включению этой функциональности являются новые категории расширений манифеста «windows.comServer» и «windows.comInterface». Расширение «windows.comServer» соответствует типичным регистрационным записям, найденным в CLSID (т.е. HKEY_CLASSES_ROOT \ CLSID \ {MyClsId} ) Для приложения, поддерживающего исполняемые серверы и их классы COM (включая их записи регистрации OLE), суррогатные серверы, классы ProgID и TreatAs. Расширение «windows.comInterface» соответствует типичным регистрационным записям как в HKCR \ Interface \ {MyInterfaceID}, так и в HKCR \ Typelib \ {MyTypelibID}, и поддерживает интерфейсы, ProxyStubs и Typelibs.

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

Пример # 1: Регистрация .exe COM-сервера

В этом первом примере мы упакуем ACDual для Desktop Bridge. ACDual - это пример MFC OLE, который поставляется в более ранних версиях Visual Studio. Данное приложение является .exe COM-сервером, ACDual.exe, с Document CoClass, который реализует интерфейс IDualAClick. Затем он может использоваться пользователем. Ниже предоставлено изображение сервера ACDual и простого клиентского приложения WinForms, которое его использует:

Рис. 1 приложение Client WinForms, автоматизирующее AutoClick COM-сервер

Ссылка Store: https://www.microsoft.com/store/apps/9nm1gvnkhjnf

Ссылка GitHub: https://github.com/Microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/PackagedComServer

 

Отличие реестра от AppxManifest.xml

Чтобы понять, как работает Packaged COM, важно сравнить типичные записи в реестре с вложенными COM-записями манифеста. Обычно для минимального COM-сервера требуется CLSID с ключом LocalServer32, а также интерфейс, указывающий на ProxyStub для обработки перекрестного процесса маршалинга. ProgIDs и TypeLibs облегчают чтение и программирование. Давайте посмотрим на каждый раздел и сравним, как выглядит системный реестр, по сравнению с фрагментами Packaged COM. Прежде всего, посмотрим на следующие записи ProgID и CLSID, которые регистрируют сервер в системном реестре:

; ProgID registration
[HKEY_CLASSES_ROOT\ACDual.Document]
@="AClick Document"
[HKEY_CLASSES_ROOT\ACDual.Document\CLSID]
@="{4B115281-32F0-11CF-AC85-444553540000}"
[HKEY_CLASSES_ROOT\ACDual.Document\DefaultIcon]
@=”C:\\VCSamples\\MFC\\ole\\acdual\\Release\\ACDual.exe,1”
 
; CLSID registration
[HKEY_CLASSES_ROOT\CLSID\{4B115281-32F0-11CF-AC85-444553540000}]
@="AClick Document"
[HKEY_CLASSES_ROOT\CLSID\{4B115281-32F0-11CF-AC85-444553540000}\InprocHandler32]
@="ole32.dll"
[HKEY_CLASSES_ROOT\CLSID\{4B115281-32F0-11CF-AC85-444553540000}\LocalServer32]
@="\"C:\\VCSamples\\MFC\\ole\\acdual\\Release\\ACDual.exe\""
[HKEY_CLASSES_ROOT\CLSID\{4B115281-32F0-11CF-AC85-444553540000}\ProgID]
@="ACDual.Document"

 

Для сравнения перевод в манифест пакета прост. ProgID и CLSID поддерживаются через расширение windows.comServer, которое должно находиться под элементом приложения вашего приложения вместе со всеми вашими другими расширениями. Что касается ProgID, вы можете иметь несколько регистраций ProgID для вашего сервера. Обратите внимание, что по умолчанию значение ProgID не указано для предоставления дружественного имени, так как эта информация сохраняется с регистрацией CLSID, и одна из целей схемы манифеста заключается в уменьшении дублирования информации. Регистрация CLSID включена через элемент ExeServer с атрибутом Executable, который представляет собой относительный путь к .exe, содержащемуся в пакете. Относительные пути пакетов решают одну общую проблему: декларативно регистрировать COM-серверы: в файле .REG вы не знаете, где находится ваш исполняемый файл. Часто все файлы в пакете помещаются в корень пакета. Элемент регистрации Class находится в элементе ExeServer. Вы можете указать один или несколько классов для ExeServer.

    
<Applications>
    <Application Id="ACDual" Executable="ACDual.exe" EntryPoint="Windows.FullTrustApplication">
      <uap:VisualElements DisplayName="ACDual" .../>
    <Extensions>      
      <com:Extension Category="windows.comServer">
        <com:ComServer>
          <!-- CLSID -->
          <com:ExeServer Executable="ACDual.exe" DisplayName="AutoClick">
            <com:Class Id ="4B115281-32F0-11cf-AC85-444553540000" DisplayName="AClick Document" ProgId="AutoClick.Document.1" VersionIndependentProgId="AutoClick.Document">
            </com:Class> 
          </com:ExeServer>      
          <!-- ProgId -->
          <com:ProgId Id="AutoClick.Document" CurrentVersion="AutoClick.Document.1" />
          <com:ProgId Id="AutoClick.Document.1" Clsid="4B115281-32F0-11cf-AC85-444553540000" />
        </com:ComServer>
      </com:Extension>
 <!-- Продолжение ниже -->

Следующим шагом будет TypeLib и регистрация интерфейса. В этом примере TypeLib является частью основного исполняемого файла, и интерфейс использует стандартный маршалер (oleaut32.dll) для своего ProxyStub, поэтому регистрация происходит следующим образом:

 

[HKEY_CLASSES_ROOT\Interface\{0BDD0E81-0DD7-11CF-BBA8-444553540000}]
@="IDualAClick"
[HKEY_CLASSES_ROOT\Interface\{0BDD0E81-0DD7-11CF-BBA8-444553540000}\ProxyStubClsid32]
@="{00020424-0000-0000-C000-000000000046}"
[HKEY_CLASSES_ROOT\Interface\{0BDD0E81-0DD7-11CF-BBA8-444553540000}\TypeLib]
@="{4B115284-32F0-11CF-AC85-444553540000}"
"Version"="1.0"
 
 
;TypeLib registration
[HKEY_CLASSES_ROOT\TypeLib\{4B115284-32F0-11CF-AC85-444553540000}]
[HKEY_CLASSES_ROOT\TypeLib\{4B115284-32F0-11CF-AC85-444553540000}\1.0]
@="ACDual"
[HKEY_CLASSES_ROOT\TypeLib\{4B115284-32F0-11CF-AC85-444553540000}\1.0\0]
[HKEY_CLASSES_ROOT\TypeLib\{4B115284-32F0-11CF-AC85-444553540000}\1.0\0\win32]
@=" C:\\VCSamples\\MFC\\ole\\acdual\\Release\\AutoClik.TLB"
[HKEY_CLASSES_ROOT\TypeLib\{4B115284-32F0-11CF-AC85-444553540000}\1.0\FLAGS]
@="0"
[HKEY_CLASSES_ROOT\TypeLib\{4B115284-32F0-11CF-AC85-444553540000}\1.0\HELPDIR]
@=""

 

При переводе этого в манифест пакета расширение windows.comInterface поддерживает одну или несколько регистраций TypeLib, ProxyStub и интерфейса. Как правило, он помещается в элемент «Приложение», поэтому для удобства чтения его легче связать с регистрацией классов, но он также может находиться в элементе «Пакет». Также обратите внимание, что нет необходимости запоминать CLSID универсального маршалера (ключ, где ProxyStubClsid32 = {00020424-0000-0000-C000-000000000046}). Это просто флаг: UseUniversalMarshaler = "true".

 

         <!-- Начало выше-->
         <com:Extension Category="windows.comInterface">
          <com:ComInterface>
          <!-- Interfaces -->
          <!-- IID_IDualAClick -->
          <com:Interface Id="0BDD0E81-0DD7-11cf-BBA8-444553540000" UseUniversalMarshaler="true">
            <com:TypeLib Id="4B115284-32F0-11cf-AC85-444553540000" VersionNumber="1.0" />
          </com:Interface>
 
          <!-- TypeLib -->
          <com:TypeLib Id="4B115284-32F0-11cf-AC85-444553540000">
            <com:Version DisplayName = "ACDual" VersionNumber="1.0" LocaleId="0" LibraryFlag="0">
              <com:Win32Path Path="AutoClik.tlb" />
            </com:Version>
          </com:TypeLib>
        </com:ComInterface>
      </com:Extension>
    </Extensions>
    </Application>
  </Applications>
  

 

Теперь вы можете инициализировать и использовать сервер с любого языка, поддерживающего COM-серверы и OLE-серверы с двумя интерфейсами.

Пример # 2: поддержка OLE

В следующем примере мы упакуем существующий сервер документов OLE, чтобы продемонстрировать возможности Desktop Bridge и Packaged COM. Пример, который мы будем использовать, - это пример приложения MFC Scribble, который предоставляет вставляемый тип документа Scribb Document. Scribble - простой сервер, который позволяет OLE-контейнеру, например WordPad, вставлять документ Scribb.

Рис. 2. WordPad, на котором размещен встроенный Scribb Document

Ссылка Store: https://www.microsoft.com/store/apps/9n4xcm905zkj

Ссылка GitHub: https://github.com/Microsoft/DesktopBridgeToUWP-Samples/tree/master/Samples/PackagedOleDocument

 

Отличие реестра от AppxManifest.xml

Существует много ключей для указания различных атрибутов OLE. Опять же, магия здесь заключается в том, что платформа была обновлена для работы с Packaged COM, и все, что вам нужно сделать, это перевести эти ключи в ваш манифест. В этом примере записи для Scribble включают ProgID, его ассоциации типов файлов и CLSID с записями.

 

;SCRIBBLE.REG
;
;FileType Association using older DDEExec command to launch the app
[HKEY_CLASSES_ROOT\.SCB]
@=”Scribble.Document”
[HKEY_CLASSES_ROOT\Scribble.Document\shell\open\command]
@=”SCRIBBLE.EXE %1”
 
 
;ProgId
[HKEY_CLASSES_ROOT\Scribble.Document]
@= Scribb Document
[HKEY_CLASSES_ROOT\Scribble.Document\Insertable]
@=””
[HKEY_CLASSES_ROOT\Scribble.Document\CLSID]
@= “{7559FD90-9B93-11CE-B0F0-00AA006C28B3}}”
 
 
;ClsId with OLE entries
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}]
@="Scribb Document"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\AuxUserType]
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\AuxUserType\2]
@="Scribb"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\AuxUserType\3]
@="Scribble"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\DefaultIcon]
@="\"C:\\VC2015Samples\\scribble\\Release\\Scribble.exe\",1"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\InprocHandler32]
@="ole32.dll"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\Insertable]
@=""
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\LocalServer32]
@="\"C:\\VC2015Samples\\scribble\\Release\\Scribble.exe\""
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\MiscStatus]
@="32"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\ProgID]
@="Scribble.Document"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\Verb]
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\Verb\0]
@="&Edit,0,2"
[HKEY_CLASSES_ROOT\CLSID\{7559FD90-9B93-11CE-B0F0-00AA006C28B3}\Verb\1]
@="&Open,0,2"

 

 

Прежде всего, стоит обсудить ассоциацию типов файлов. Это расширение, которое поддерживалось в первой версии расширений Desktop Bridge. Обратите внимание, что указание здесь ассоциации типов файлов автоматически добавляет поддержку открытой командной  оболочки.

Далее, подробнее расмотрим записи ProgID и CLSID. В данном случае простой пример имеет только ProgID, а не VersionIndependentProgID.

Большая часть волнений в этом примере находится под CLSID, где находятся все ключи OLE. Ключи реестра обычно сопоставляются с атрибутами класса, например:

 

Вставляемый ключ под ProgID или CLSID, сопоставляется с атрибутом InsertableObject = "true"

Если ключ InprocHandler32 является Ole32.dll, используйте атрибут EnableOleDefaultHandler = "true"

AuxUserType\ 2 отображается в ShortDisplayName

AuxUserType\ 3 сопоставляется с приложением DisplayName

 

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

 

  
 <Applications>
  <Application Id="Scribble" Executable="Scribble.exe" EntryPoint="Windows.FullTrustApplication">
    <uap:VisualElements DisplayName="Scribble App" .../>
    <Extensions>
      <uap:Extension Category="windows.fileTypeAssociation">
        <uap3:FileTypeAssociation Name="scb" Parameters="%1">
          <uap:SupportedFileTypes>
            <uap:FileType>.scb</uap:FileType>
          </uap:SupportedFileTypes>
        </uap3:FileTypeAssociation>
      </uap:Extension>
 
      <com:Extension Category="windows.comServer">
        <com:ComServer>
          <com:ExeServer Executable="Scribble.exe" DisplayName="Scribble">
            <!-- ClsId Registration -->
            <com:Class Id="7559FD90-9B93-11CE-B0F0-00AA006C28B3" DisplayName="Scribb Document" ShortDisplayName="Scribb" ProgId="Scribble.Document.1" VersionIndependentProgId ="Scribble.Document" EnableOleDefaultHandler="true" InsertableObject="true">
              <com:DefaultIcon Path="Scribble.exe" ResourceIndex="1" />
              <com:MiscStatus OleMiscFlag="32"/>
              <com:Verbs>
                <com:Verb Id="0" DisplayName="&Edit" AppendMenuFlag="0" OleVerbFlag="2" />
                <com:Verb Id="1" DisplayName="&Open" AppendMenuFlag="0" OleVerbFlag="2" />
              </com:Verbs>
            </com:Class>
          </com:ExeServer>
          <!-- ProgId Registration -->
          <com:ProgId Id="Scribble.Document" CurrentVersion="Scribble.Document.1" />
          <com:ProgId Id="Scribble.Document.1" Clsid="7559FD90-9B93-11CE-B0F0-00AA006C28B3" />
        </com:ComServer>
      </com:Extension>
    </Extensions>
  </Application>
</Applications>

 

Дополнительная поддержка

 

В двух приведенных выше примерах рассмотрены наиболее распространенные случаи использования COM-сервера и поддержки документов OLE. Packaged COM также поддерживает дополнительные серверы, такие как Surrogates и TreatAs классы. Более подробную информацию вы можете найти в иерархии элементов манифеста пакета Windows 10 приложений на MSDN: https://docs.microsoft.com/uwp/schemas/appxpackage/uapmanifestschema/root-elements

 

Заключение

С UWP и Windows 10 приложения могут использовать несколько новых интересных функций, используя существующие вложения кода в таких областях, как COM. Благодаря платформе Desktop Bridge и усовершенствованиям инструментов, существующее программное обеспечение ПК теперь может быть частью экосистемы UWP и использовать тот же набор новых функций платформы и возможностей операционной системы.

Для получения дополнительной информации о Desktop Bridge посетите Windows Dev Center.

Готовы ли вы отправить свое приложение в Windows Store? Дайте знать!



Exception: Stack empty.

Add comment


(Will show your Gravatar icon)

  Country flag

biuquote
  • Comment
  • Preview
Loading