我有一些应用程序(一些本机,一些.NET)使用清单文件,因此它们可以是deployed in complete isolation,而无需任何全局COM注册。例如,在myapp.exe.manifest文件中声明对dbgrid32.ocx com服务器的依赖关系如下,该文件与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服务器列表(本机和.NET)的依赖关系,然后自动生成其余部分。有可能吗?
答案 0 :(得分:58)
看起来完美的解决方案还不存在。总结一些研究:
Make My Manifest (link)
此工具扫描VB6项目以查找COM依赖项,但它也支持手动声明后期绑定的COM依赖项(即通过CreateObject使用的那些)。
有趣的是,此工具将有关依赖项的所有信息放在应用程序清单中。应用程序exe及其依赖项被描述为由多个文件组成的单个程序集。我之前没有意识到这是可能的。
看起来是一个非常好的工具,但从版本0.6.6开始,它有以下限制:
我没有测试它是否支持.NET com库。
regsvr42 (codeproject link)
此命令行工具为本机COM库生成清单文件。它调用DllRegisterServer,然后在向注册表添加信息时监视自注册。它还可以为应用程序生成客户端清单。
此实用程序不支持.NET COM库,因为它们不会公开DllRegisterServer例程。
该实用程序是用C ++编写的。源代码可用。
<强> mt.exe 强>
部分Windows SDK(可以从MSDN下载),如果您安装了Visual Studio,则已经拥有了该SDK。它是documented here。 您可以使用它生成本机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
但是,此工具存在一些问题:
<runtime>
和<mvid>
个元素
以前需要剥离的
清单实际上有效。也许未来的SDK版本会改进这个工具,我测试了Windows SDK 6.0a(vista)中的那个。
答案 1 :(得分:25)
使用MSBuild任务GenerateApplicationManifest我在命令行生成了一个与Visual Studio生成的清单相同的清单。我怀疑Visual Studio在构建期间使用GenerateApplicationManifest。下面是我的构建脚本,可以使用msbuild“msbuild build.xml”
从命令行运行感谢Dave Templin和他的post that pointed me the the GenerateApplicationManifest task以及MSDN的further documentation of the task。
的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>
答案 2 :(得分:9)
Make My Manifest (MMM)是一个很好的工具。也可以使用mt.exe编写一个脚本来处理所有DLL / OCX文件,为每个文件生成一个清单,然后将它们合并在一起。 MMM通常更好/更容易,因为它还处理许多特殊/奇怪的情况。
答案 3 :(得分:7)
您可以使用Unattended Make My Manifest spin off直接在自动构建中生成清单。它使用脚本文件添加依赖的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上运行。
答案 4 :(得分:0)
要填写mt.exe不包含的ProgID,您可以调用ProgIDFromCLSID
从注册表中查找它们。这需要在完成清单文件之前进行传统的COM注册,但随后清单文件将是自给自足的。
此C#代码将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;