Создание файлов манифеста для бесплатного COM-входа

У меня есть некоторые приложения (некоторые native, некоторые .NET), которые используют файлы манифеста, чтобы они могли развернуты в полной изоляции, без необходимости любая глобальная регистрация COM. Например, зависимость от сервера dbgrid32.ocx com объявляется следующим образом в файле myapp.exe.manifest, который находится в той же папке, что и myapp.exe:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="myapp.exe" version="1.2.3.4" />
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="dbgrid32.ocx" version="5.1.81.4" />
    </dependentAssembly>
  </dependency>
</assembly>

dbgrid32.ocx развертывается в той же папке вместе с собственным файлом dbgrid32.ocx.manifest:

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity type="win32" name="dbgrid32.ocx" version="5.1.81.4" />
  <file name="dbgrid32.ocx">
     <typelib
        tlbid="{00028C01-0000-0000-0000-000000000046}"
        version="1.0"
        helpdir=""/>
    <comClass progid="MSDBGrid.DBGrid"
       clsid="{00028C00-0000-0000-0000-000000000046}"
       description="DBGrid  Control" />
  </file>
</assembly>

Все это прекрасно работает, но сохранение этих файлов манифеста вручную немного больно. Есть ли способ сгенерировать эти файлы автоматически? В идеале я просто хотел бы объявить зависимость приложения от списка COM-серверов (как native, так и .NET), а затем позволить остальным создавать автоматически. Возможно ли это?

Ответ 1

Похоже, что идеальное решение еще не существует. Подведем итог нескольким исследованиям:

Сделать мой манифест (ссылка)

Этот инструмент просматривает проект VB6 для поиска зависимостей COM, но также поддерживает ручное декларирование поздних связанных зависимостей COM (т.е. используемых через CreateObject).

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

Похож на очень хороший инструмент, но с версии 0.6.6 он имеет следующие ограничения:

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

Я не тестировал, поддерживает ли он библиотеки .NET com.

regsvr42 (ссылка codeproject)

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

Эта утилита не поддерживает библиотеки .NET COM, поскольку они не выставляют подпрограмму DllRegisterServer.

Утилита написана на С++. Исходный код доступен.

mt.exe

Часть окна SDK (можно загрузить из MSDN), который у вас уже есть, если у вас установлена ​​визуальная студия. Это описанный здесь. Вы можете сгенерировать файлы манифеста для собственных COM-библиотек следующим образом:

mt.exe -tlb:mycomlib.ocx -dll:mycomlib.ocx -out:mycomlib.ocx.manifest

Вы можете создавать файлы манифеста для .NET COM-библиотек следующим образом:

mt.exe -managedassemblyname:netlib.dll -nodependency -out:netlib.dll.manifest

Однако есть некоторые проблемы с этим инструментом:

  • Первый фрагмент не будет генерировать атрибуты progid, нарушение клиентов которые используют CreateObject с progids.
  • Второй фрагмент будет генерировать Элементы <runtime> и <mvid> которые необходимо удалить до манифесты действительно работают.
  • Генерация клиентских манифестов для приложения не поддерживаются.

Возможно, будущие версии SDK улучшат этот инструмент, я протестировал его в Windows SDK 6.0a (vista).

Ответ 2

С задачей MSBuild GenerateApplicationManifest Я сгенерировал манифест в командной строке, идентичный манифесту Visual Studio. Я подозреваю, что Visual Studio использует GenerateApplicationManifest во время сборки. Ниже приведена моя сборка script, которую можно запустить из командной строки с помощью msbuild "msbuild build.xml"

Благодаря Дейву Темплину и его сообщению, которые указали мне задачу GenerateApplicationManifest, и MSDN дальнейшая документация задачи.

build.xml

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <Target Name="Build">
        <ItemGroup>
            <File Include='MyNativeApp.exe'/>
            <ComComponent Include='Com1.ocx;Com2.ocx'/>
        </ItemGroup>
        <GenerateApplicationManifest
            AssemblyName="MyNativeApp.exe"
            AssemblyVersion="1.0.0.0"
            IsolatedComReferences="@(ComComponent)"
            Platform="x86"
            ManifestType="Native">
            <Output
                ItemName="ApplicationManifest"
                TaskParameter="OutputManifest"/>
        </GenerateApplicationManifest>
    </Target>   
</Project>

Ответ 3

Make My Manifest (MMM) - хороший инструмент для этого. Также можно написать script для обработки всех ваших файлов DLL/OCX с помощью mt.exe для создания манифеста для каждого из них, а затем слияния все они вместе. MMM обычно лучше/проще, потому что он также обрабатывает множество особых/странных случаев.

Ответ 4

Вы можете использовать Unattended Make My Manifest для создания манифестов непосредственно в автоматических сборках. Он использует файл сценария для добавления зависимых компонентов COM. Это отрывок из примера ini с доступными командами:

# Unattended MMM script
#
# Command names are case-insensitive. Reference of supported commands:
#
# Command: Identity
#
#   Appends assemblyIdentity and description tags.
#
#   Parameters       <exe_file> [name] [description]
#      exe_file      file name can be quoted if containing spaces. The containing folder 
#                    of the executable sets base path for relative file names
#      name          (optional) assembly name. Defaults to MyAssembly
#      description   (optional) description of assembly
#
# Command: Dependency
#
#   Appends dependency tag for referencing dependent assemblies like Common Controls 6.0, 
#     VC run-time or MFC
#
#   Parameters       {<lib_name>|<assembly_file>} [version] [/update]
#     lib_name       one of { comctl, vc90crt, vc90mfc }
#     assembly_file  file name of .NET DLL exporting COM classes
#     version        (optional) required assembly version. Multiple version of vc90crt can
#                    be required by a single manifest
#     /update        (optional) updates assembly_file assembly manifest. Spawns mt.exe
#
# Command: File
#
#   Appends file tag and collects information about coclasses and interfaces exposed by 
#     the referenced COM component typelib.
#
#   Parameters       <file_name> [interfaces]
#     file_name      file containing typelib. Can be relative to base path
#     interfaces     (optional) pipe (|) separated interfaces with or w/o leading 
#                    underscore
#
# Command: Interface
#
#   Appends comInterfaceExternalProxyStub tag for inter-thread marshaling of interfaces
#
#   Parameters       <file_name> <interfaces>
#     file_name      file containing typelib. Can be relative to base path
#     interfaces     pipe (|) separated interfaces with or w/o leading underscore
#
# Command: TrustInfo
#
#   Appends trustInfo tag for UAC user-rights elevation on Vista and above
#
#   Parameters       [level] [uiaccess]
#     level          (optional) one of { 1, 2, 3 } corresponding to { asInvoker, 
#                    highestAvailable, requireAdministrator }. Default is 1
#     uiaccess       (optional) true/false or 0/1. Allows application to gain access to 
#                    the protected system UI. Default is 0
#
# Command: DpiAware
#
#   Appends dpiAware tag for custom DPI aware applications
#
#   Parameters       [on_off]
#     on_off         (optional) true/false or 0/1. Default is 0
#
# Command: SupportedOS
#
#   Appends supportedOS tag
#
#   Parameters       <os_type>
#     os_type        one of { vista, win7 }. Multiple OSes can be supported by a single 
#                    manifest
#

Он будет работать на 32- или 64-битной Windows.

Ответ 5

Чтобы заполнить ProgID, которые mt.exe не включает, вы можете вызвать ProgIDFromCLSID чтобы просмотреть их из реестра. Для этого требуется традиционная регистрация COM до завершения файла манифеста, но впоследствии файл манифеста будет самодостаточным.

Этот код С# добавляет ProgID ко всем классам COM в манифесте:

var manifest = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("s", "urn:schemas-microsoft-com:asm.v1");
foreach (var classElement in manifest.XPathSelectElements("s:assembly/s:file/s:comClass", namespaceManager)) {
    var clsid = Guid.Parse(classElement.Attribute("clsid").Value);
    int result = ProgIDFromCLSID(ref clsid, out string progId); if (result != S_OK) throw new COMException($"ProgID lookup failed for {clsid}.", result);
    classElement.SetAttributeValue("progid", progId);
}
manifest.Save(fileName);

Код основан на следующих определениях взаимодействия:

[DllImport("ole32.dll")] static extern int ProgIDFromCLSID([In] ref Guid clsid, [MarshalAs(UnmanagedType.LPWStr)] out string lplpszProgID);
const int S_OK = 0;