25

询问了 Visual Studio 为注册 COM 库做了什么之后,很明显 VS 为 COM 注册做了两件事:

  1. 注册 COM 库
  2. 创建并注册类型库

Visual Studio 似乎使用 regasm.exe 进行此注册。对于第一部分(直接 COM 注册),使用tallowor heat(WiX 2.0 或 WiX 3.0)似乎可以正确获取所有基本 COM 注册信息。

但是,tallow/heat 似乎没有做的是设置类型库安装。可以使用 WiX 安装程序和 regasm.exe 创建自定义操作来执行此操作,但对于基于 Microsoft 安装程序的安装程序,调用自定义操作并不是最佳实践。

经过进一步研究,看起来 msi 具有在安装时生成类型库的能力。事实上,WiX 似乎对它有直接的支持!在文件元素中,您可以添加Typelib元素。事实上,这里关于 wix 的一篇文章有​​一个使用Interface元素填充 TypeLib 元素的示例。

Interface 元素似乎至少有两个必需的属性:

  1. ID
  2. 姓名

Larry Osterman 谈到了需要为 TypeLib 注册的接口的其他部分,这个接口条目似乎照顾了各个部分。Larry 说我们需要将 ProxyStubClassId32 指定为“{00020424-0000-0000-C000-000000000046}”,因此我们可以轻松添加它。

从那里去哪里以及为各种界面元素填写什么让我很难过。我已经继续并将 TypeLib 元素添加到我的 wix 文件中,并且它成功编译。不过,我对如何设置界面元素有点无能为力。我们需要做什么才能正确填写 TypeLib 元素,我可以使用哪些应用程序或工具来获取它?

wcoenen 下面的答案看起来很有希望......我要试一试。

更新:在下面发布我的最终解决方案作为答案。

4

3 回答 3

17

这是懒人解决这个问题的方法:heat从 WiX 3.0 开始使用。

如果您有一个自动生成并通过 regasm 安装的类型库,heat可以将 .tlb 作为参数

heat file c:\my\path\to\my.tlb -out tlb.wxs

它将生成您需要注册的所有类型库和接口元素。这不会解决需要提前知道它们的问题,也不会解决在程序集版本更改时 GUID 更改的问题(即使界面没有 - 这是您唯一的一次)重新应该改变它)但它会让你中途到达那里。

于 2009-03-06T01:32:22.667 回答
8

以下技巧可以帮助收集任何注册表更改并将它们转换为 wxs 文件,包括您所追求的 typelib 元素。

  1. 首先,将您的注册表恢复到未注册类型库的状态:

    c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb /u mylib.dll
    
  2. 将注册表的这种干净状态导出到 hklm-before.reg:

    c:\WINDOWS\system32\reg.exe export HKLM hklm-before.reg
    
  3. 再次注册类型库:

    c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb mylib.dll
    
  4. 将注册表的新状态导出到 hklm-after.reg:

    c:\WINDOWS\system32\reg.exe export HKLM hklm-after.reg
    
  5. 现在我们有两个文本文件,hklm-before.reg 和 hklm-after.reg。创建一个 diff.reg 文件,该文件仅包含这些之间的相关差异。您可以使用差异工具轻松找到差异。我喜欢使用 TortoiseSVN 中包含的差异工具,因为我已经每天都在使用它。(由于文本编码问题,WinDiff 在这种情况下似乎无法正常工作。)

  6. 我们现在可以通过调用命令将 diff.reg 转换为heat.exe.wxs reg。(需要 wix 3.5 或更高版本。)

    heat reg diff.reg -out typelib.wxs
    
于 2009-02-20T10:42:10.420 回答
3

它看起来像注册一个类型库,最好的方法是生成您自己的 IDL 或 ODL 文件,其中将包含您的 GUID。直接从程序集生成的类型库 [i] 依赖于[/i] 程序集版本号:GUID 是基于该信息生成的,即使接口没有更改。Visual Studio 使用 regasm 注册和生成类型库。在此之下,它使用了一个 Win32 调用 RegisterTypeLib。使用 typelib 元素似乎做了类似的事情。不好。

然而!手动创建类型库很痛苦。可以通过另一种方式获得这些 GUID:将它们从类型库中挖掘出来并自己创建元素。

Larry Osterman 拥有所需的信息:需要设置某些注册表项。您可以使用 Registry 表执行这些操作(在 Wix3 中,这意味着 RegistryValue 元素。)这里的技巧是获取 GUID:任何旧的 GUID 都不起作用。通常,获取 GUID 只需查看库的 IDL(您编写了自己的 IDL,对吗?:))。

如果您没有编写 IDL 或 ODL 文件来编译到类型库中,它们仍然存在于文件中。Microsoft 提供了几个方便的工具:LoadTypeLibEx和 ITypeLib 接口。通过这些接口,您可以浏览类型库并获取各种信息。我们如何浏览图书馆?

我只是看看 Regasm 是如何做到的!稍后快速反汇编,我们发现 regasm 也是用 C# 编写的。荣耀日。我启动了一个项目,稍后使用一些 using 语句和一个 PInvoke,我们有:

using System.Runtime.InteropServices;          // for struct marshaling 
using System.Runtime.InteropServices.ComTypes; // for the ITypeLib + related types

// TYPELIBATTR lives in two places: Interop and ComTypes, but the one
// in Interop is deprecated.
using TYPELIBATTR = System.Runtime.InteropServices.ComTypes.TYPELIBATTR; 

/// <summary>
/// The registry kind enumeration for LoadTypeLibEx.  This must be made
/// here, since it doesn't exist anywhere else in C# afaik.  This is found
/// here: http://msdn.microsoft.com/en-us/library/ms221159.aspx
/// </summary>
enum REGKIND
{
    REGKIND_DEFAULT,
    REGKIND_REGISTER,
    REGKIND_NONE
}

// and this is how we get the library.
[DllImport("oleaut32.dll", CharSet = CharSet.Unicode, PreserveSig = false)]
  private static extern void LoadTypeLibEx(string strTypeLibName, REGKIND regKind, out ITypeLib TypeLib);

哇!一旦我们有了这个,我们必须导航结构。这是与非托管资源的交互,所以准备好Marshal周围的东西。

ITypeLib lib = null;
LoadTypeLibEx(Value, REGKIND.REGKIND_NONE, out lib);
IntPtr libInfoPtr = IntPtr.Zero;
lib.GetLibAttr(out libInfoPtr);
TYPELIBATTR libInfo = 
    (TYPELIBATTR) Marshal.PtrToStructure(libInfoPtr, typeof(TYPELIBATTR));
int typeCount = lib.GetTypeInfoCount();
for (int i = 0; i < typeCount; ++i)
{
    ITypeInfo info;
    lib.GetTypeInfo(i, out info);
    IntPtr typeDescrPtr = IntPtr.Zero;
    info.GetTypeAttr(out typeDescrPtr);
    TYPELIBATTR type =
        (TYPELIBATTR)Marshal.PtrToStructure(typeDescrPtr, typeof(TYPELIBATTR));
    // get GUID, other info from the specific type
}

lib.ReleaseTLibAttr(libInfoPtr);
libInfoPtr = IntPtr.Zero;

唷。因此,您必须编写一些代码来提取信息。完成后,您必须按照 Larry Osterman的规定将该信息填写到 Registy Entries 中。

当然,您可以通过简单地编写自己的 IDL 文件来避免该步骤。痛苦中的选择:由你决定!

于 2009-02-26T21:28:13.950 回答