在询问Visual Studio如何注册一个COM库之后 ,很明显,VS为COM注册做了两件事情:
Visual Studio似乎使用regasm.exe执行此注册。 对于第一部分(直接COM注册)使用tallow
或heat
(WiX 2.0或WiX 3.0)似乎得到正确的所有基本COM注册信息。
然而,牛油/热似乎没有做的是build立一个types库安装。 使用WiX安装程序和regasm.exe创build自定义操作是可能的,但是对于基于Microsoft安装程序的安装程序,调用自定义操作不是最佳实践。
经过进一步的研究,看起来像msi有能力在安装时生成types库。 实际上,WiX似乎对它有直接的支持! 在一个文件元素中,你可以添加一个Typelib元素。 实际上, wix上的一篇文章有一个用Interface元素填充TypeLib元素的例子。
看起来界面元素至less有两个必需的属性:
Larry Osterman谈到接口的其他部分,一般需要注册TypeLib ,而这个接口条目似乎负责单独的部分。 拉里说,我们需要指定ProxyStubClassId32为“{00020424-0000-0000-C000-000000000046}”,所以我们可以很容易地添加。
从哪里去,什么填写各种界面元素让我难住。 我已经提前将TypeLib元素添加到我的wix文件,并成功编译。 对于如何设置界面元素,我有点无知。 我们需要做些什么来正确地填写TypeLib元素,以及我可以使用哪些应用程序或工具来获取它?
wcoenen下面的答案看起来很有希望…我会试试看。
更新:发表我的最终解决scheme作为答案。
这是懒人解决这个问题的方法:使用WiX 3.0的heat
。
如果你有一个自动生成的类型库,并通过regasm进行安装, heat
可以把.tlb作为参数
heat file c:\my\path\to\my.tlb -out tlb.wxs
它将生成所有需要注册的类型库和界面元素。 这并不能解决提前知道它们的问题,而且也不能解决程序集版本改变时GUID改变的问题(即使接口不是这样,应该改变它),但它会让你在那里。
下面的技巧可以帮助收集任何注册表更改并将其转换为wxs文件,包括您之后的typelib元素。
首先,将注册表重新置于类型库未注册的状态:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb /u mylib.dll
将注册表的这个干净的状态导出到hklm-executedreg:
c:\WINDOWS\system32\reg.exe export HKLM hklm-before.reg
再次注册类型库:
c:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regasm.exe /tlb mylib.dll
将注册表的新状态导出到hklm-after.reg:
c:\WINDOWS\system32\reg.exe export HKLM hklm-after.reg
现在我们有两个文本文件,hklm-previouslyreg和hklm-after.reg。 创建一个diff.reg文件 ,只保留这些文件之间的相关差异。 您可以通过差异化工具轻松找到差异。 我喜欢使用TortoiseSVN中包含的diff工具,因为我已经每天都在使用它。 (由于文本编码的问题,WinDiff在这种情况下似乎不起作用。)
我们现在可以通过使用reg
命令调用heat.exe
将diff.reg转换成.wxs。 (需要wix 3.5或更新版本。)
heat reg diff.reg -out typelib.wxs
它看起来像注册一个类型库, 最好的办法是生成自己的IDL或ODL文件,其中将包含您的GUID。 直接从程序集生成的类型库在程序集版本号上是[i]依赖的[/ i]:即使接口没有改变,GUID也是基于这些信息生成的。 Visual Studio使用regasm来注册并生成typelib。 在它下面,它使用RegisterTypeLib,一个win32调用。 使用typelib元素似乎做了类似的事情。 不好。
然而! 手工创建类型库是痛苦的。 可以通过另一种方式获得这些GUID:从类型库中挖掘出来并自己创建元素。
Larry Osterman提供了所需的信息:需要设置某些注册表项。 你可以使用Registry表(在Wix3中,这意味着RegistryValue元素)。这里的技巧是获取GUID:任何旧的GUID都不起作用。 通常情况下,获取GUID仅仅是为你的库查找IDL(你自己编写了IDL,对不对?:))。
如果你没有写一个IDL或ODL文件编译成一个typelib,他们仍然存在,在文件中。 微软提供了几个方便的工具: LoadTypeLibEx和ITypeLib接口。 通过这些接口,您可以浏览类型库并获取各种信息。 我们如何浏览图书馆?
我只是看看Regasm是如何做到的! 稍后快速拆解,我们发现regasm也是用C#编写的。 荣耀的一天。 我启动了一个项目,稍后使用一些声明和一个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);
呼! 一旦我们有了这个,我们必须导航结构。 这是与非托管资源相互作用,所以准备好要组装东西。
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的规定,将这些信息填入注册表项中。
当然,您可以通过简单编写自己的IDL文件来避免这一步骤。 痛苦的选择:取决于你!