生成清单文件以用于免注册的COM


87

我有一些使用清单文件的应用程序(某些本机应用程序,.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服务器列表(本机和.NET)的依赖关系,然后让其余部分自动生成。可能吗?


+1也:重新标记了regfreecom,因为该标记在无注册中心的COM中更常见
-MarkJ

我可以通过清单文件在自己的安装文件夹中使用更高版本的mstscax.dll吗?
Acewind

@acewind是的。(您可能想发布一个具有更多详细信息的新问题。)
UuDdLrLrSs

@UuDdLrLrSs好消息!:我在这里发表一个新的问题stackoverflow.com/questions/63575746/...
Acewind

Answers:


63

似乎还没有完美的解决方案。总结一些研究:

做我的清单链接

该工具扫描VB6项目以查找COM依赖关系,但它也支持手动声明后期绑定的COM依赖项(即,通过CreateObject使用的依赖项)。

有趣的是,此工具将有关依赖项的所有信息放入应用程序清单中。应用程序exe及其依赖项描述为包含多个文件的单个程序集。在此之前,我还没有意识到。

看起来像一个非常好的工具,但是从0.6.6版本开始,它具有以下限制:

  • 仅用于VB6应用程序,从VB6项目文件开始。感到遗憾,因为它所做的很多事情实际上与VB6无关。
  • 向导样式的应用程序,不适合集成到构建过程中。如果您的依存关系变化不大,这不是一个大问题。
  • 没有源的免费软件,冒险依赖它,因为它随时可能成为废弃软件。

我没有测试它是否支持.NET com库。

regsvr42codeproject链接

此命令行工具为本地COM库生成清单文件。它调用DllRegisterServer,然后在将信息添加到注册表中时监视自我注册。它还可以为应用程序生成客户端清单。

该实用程序不支持.NET COM库,因为它们不公开DllRegisterServer例程。

该实用程序是用C ++编写的。源代码可用。

程序

Windows SDK的一部分(可以从MSDN下载),如果已安装Visual Studio,则已经拥有。它记录在这里。您可以像这样为本地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)中对其进行了测试。


1
我认为您错过了一个选择:mazecomputer.com,但我对此一无所知,但网站并未对此进行描述。
鲍勃

MMM还将重定向非COM(标准)DLL。我不确定其他工具是否可以这样做。
鲍勃

只是一个紧张的提示:MMM的来源已发布。不利的一面似乎是因为作者决定停止对此进行研究。仍然是一个积极的信号。
加文

2
MMM的站点已不再可用,但是放置源代码的位置仍可用于v0.9v0.12
Scott Chamberlain 2015年

1
如上所述,仅尝试对.NET COM库使用mt.exe,并且使用v7.1A无需修改清单即可工作。另外,MMM的链接不起作用,但“无心制作清​​单”似乎做得不错。
bzuillsmith'7

28

通过MSBuild任务GenerateApplicationManifest,我在命令行生成了与Visual Studio生成的清单相同的清单。我怀疑Visual Studio使用GenerateApplicationManifest在构建过程中。下面是我的构建脚本,可以使用msbuild“ msbuild build.xml”从命令行运行

感谢Dave Templin和他的帖子向我指出了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>

我认为这确实应被标记为该问题的答案。我现在正在使用它来自动化所有清单生成。谢谢@mcdon,您为我节省了很多工作。
Pete Magsig

我同意这是使用Visual Studio构建时的最佳解决方案。它可能不仅仅因为它的发布比其他答案晚得多而被评为更高
dschaeffer 2014年

如何将其添加到csproj?
jle

@jle我认为您可以将其添加到AfterBuild目标中的csproj中。这是msdn链接,以及有关prebuild和postbuild事件的另一篇文章请注意,我尚未测试过将​​其包含在csproj中,但我怀疑它会起作用。
麦当劳

可以为C#COM DLL生成清单,以便任何exe都可以使用它(而不是在每个exe上生成清单,并且包括DLL吗?)
GilesDMiddleton

9

做我的清单(MMM)是执行此操作的好工具。也可以编写一个脚本来使用mt.exe处理所有DLL / OCX文件,以为每个文件生成一个清单,然后将它们全部合并在一起。MMM通常更好/更容易,因为它也处理很多特殊/怪异的情况。


3
我对MMM的事情有点紧张;它只是一个博客,免费软件,但没有源代码,只有指向“自解压exe”的链接,我看到有关该实用程序的评论,导致XP崩溃。嗯...
Wim Coenen

这些“崩溃”是MMM实用程序本身即将消亡的原因。该问题已在0.6.5版中修复,但无论如何您都希望使用0.6.6,因为尽管仍然是beta版,但它不再过期。尽管已经建议您始终可以使用MT.EXE代替。
鲍勃”

当我在dbgrid32.ocx等本地com服务器上使用mt.exe时,它不会生成progid
Wim Coenen

将无注册
商标的

8

您可以使用“无人参与制作我的清单”来直接在自动构建中生成清单。它使用脚本文件来添加依赖的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上运行。


+1有趣,尤其是因为源代码可用。我对名称相似性有些困惑,显然,“作者出示”和“无人值守”是不同作者使用的不同工具。
Wim Coenen

2
注意-自2017年起(8年...),该项目仍处于活动状态,并且需要进行偶尔的维护更新。github.com/wqweto/UMMM/commits/master。它运作良好,我经常使用它。
UuDdLrLrSs

0

要填写mt.exe不包含的ProgID,可以调用ProgIDFromCLSID从注册表中查找它们。这要求在完成清单文件之前进行传统的COM注册,但是清单文件将是自给自足的。

此C#代码将ProgIDs添加到清单中的所有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;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.