如何获得在VB.NET中编写的用于Excel的COM服务器安装并注册在自动化服务器列表中?

版本

Excel 2007,Windows Vista,VB.NET,带有.NET 3.5 sp2的Visual Studio 2008,MSI安装程序包。

我正在尝试做什么

我有一个用VB.NET编写的Excel UDF。 它是作为COM服务器公开的,因为您无法直接在.NET语言中创buildExcel UDF。 安装是一个真正的痛苦,因为没有任何安装设置似乎得到它是正确的; 他们都没有给你一个安装包,把COM服务器放到客户端机器上,注册了服务器,注册了types库,以及在Excel 2007的自动化服务器列表中可见的组件。

我试过了

以下是types库的安装设置,它们的缺陷在编译时和安装时显而易见:

vsdrfComSelfReg

  • 在编译安装项目时没有任何警告
  • 模块xxx.tlb注册失败。 HRESULT -2147024703
  • 组件的ProgID和GUID在registry中设置,但该组件不出现在自动化服务器列表中

vsdrfDoNotregister

  • 编译期间没有警告
  • 安装工作,但当然TLB没有注册

vsdrfCOM

  • 编译时警告:警告:无法为名为“xxx.tlb”的文件创build注册信息
  • types库在安装过程中未被注册

正确的设置应该是vsdrfCOM,如下所述:

问:任何人都可以告诉vsdrfCOM在Visual Studio的安装项目中的含义吗? 当我在安装项目中检查添加文件的属性中的“注册”属性时可用。

答:这意味着Visual Studio将在构build时提取COM注册数据,并将其放入MSI文件(主要是MSI文件的registry表,还有类表)。 所以当你安装它的时候,你的代码不需要自行注册,因为文件被复制到磁盘并且registry项被创build。 它还将通过向MSI的TypeLib表添加条目来创buildtypes库注册。

许多困难似乎是Vista特有的。 尤其是,使用REGCAP实用程序从.TLB文件生成.REG文件在Vista中不起作用。 如果不是这样的话,那么这个build议可能是有用的 。 相反,它完全可以产生空的.REG文件。

我已经尝试了这个StackOverflow后的所有build议。 这篇文章对技术问题有很好的描述:

“引用”对话框中的条目来自HKCR \ TypeLibregistry项,而不是来自HKCR \ CLSID。 如果你的程序集没有显示在引用对话框中,但编译后的DLL仍然可以使用你的COM程序集,这意味着你的程序集正确地注册了类和接口,但是types库本身不是。

这个问题

任何人都有如何使安装注册组件和types库的想法? 我无法访问Windows XP计算机。


详细说明为什么这很糟糕

.TLB对于任何编译的代码来说都不是必需的。 我还没有尝试部署一个Excel自动化加载项,但是我的猜测是UDF应该加载并运行得很好。

这不像在Excel中那样。

  • 用户打开工作表并尝试引用UDF。 没有find,因为DLL没有加载。 失败
  • 用户转到“首选项”|“Excel选项”|“加载项”|“Excel加载项”,然后在“加载项”对话框中未列出COM服务器。 失败
  • 用户然后按自动化服务器以获取可用的自动化服务器的列表。 该DLL不在那里。 失败
  • 用户返回到加载macros对话框并select浏览,导航到安装目录,并selectDLL(“XXX不是有效的加载项”)或types库(“您select的文件不包含新的自动化服务器,或者你没有足够的权利……“)。 失败

据我所知,用户必须从命令行运行regasm.exe使Excel UDF / COM服务器可用。 你觉得告诉人们从命令行运行regasm来安装一个加载项到Excel?


编辑2009-10-04

迈克的评论和下面的方向真棒。 我不知道的关键是安装程序有一个内置的registry编辑器,用于添加registry项 。 哦,并且具有属性ComRegisterFunctionAttribute安装函数没有被Microsoft安装程序调用 。 我已经有了从他引用的来源编写安装程序function的指示。

我在周末部署了一个自动加载程序, 事实certificate,这是非常复杂的(对你来说不是一个惊喜!),我完全没有在互联网上find正确的做法。 没有。

有些资源描述了如何使用RegAsm ,但没有一个如何正确使用安装项目来注册一个自动化加载项,这与标准的COM加载项有点不同。

幸运的是,我能够解决这个问题。 这是我发现的:

如果您阅读了一些关于如何创build和注册C#自动化插件的文章,您将看到您需要在HKEY\_CLASSES\_ROOT\CLSID\\{GUID}处添加一个名为Programmable的registry项,其中{GUID}是COM可见类的GUID。

这通常是通过添加一对由ComRegisterFunctionAttribute和ComUnregisterFunctionAttribute标记的方法来完成的 。 一个很好的例子是来自Gabhan Berry 在C#中编写自定义Excel工作表函数的文章:

 // C#: [ComRegisterFunctionAttribute] public static void RegisterFunction(Type type) { Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type)); } [ComUnregisterFunctionAttribute] public static void UnregisterFunction(Type type) { Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type), false); } private static string GetSubKeyName(Type type) { string s = @"CLSID\{" + type.GUID.ToString().ToUpper() + @"}\Programmable"; return s; } 

翻译成VB.NET,这可以解决:

 'VB.NET: <ComRegisterFunctionAttribute()> _ Public Shared Sub RegisterFunction(ByVal type As Type) Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type)) End Sub <ComUnregisterFunctionAttribute()> _ Public Shared Sub UnregisterFunction(ByVal type As Type) Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type), false) End Sub Private Shared Function GetSubKeyName(ByVal type As Type) As String Dim s As String = ("CLSID\{" _ + (type.GUID.ToString.ToUpper + "}\Programmable")) Return s End Function 

ComRegisterFunctionAttribute标记的方法在注册该类的程序集时由RegAsm自动调用。 当通过/u开关取消注册该类的程序集时,由RegAsm自动调用由ComUnregisterFunctionAttribute标记的方法。

问题是通过Visual Studio安装项目安装时ComRegisterFunctionAttributeComUnregisterFunctionAttribute 完全被忽略。

这似乎令人惊讶,因为Visual Studio安装项目运行RegAsm使用/regfile开关为了生成包含所有必需的registry项的.REG文件。 正是这个.REG文件被使用然后.MSI包在客户端运行。

从Phil Wilson 构build和部署一个.NET COM组件 :

Visual Studio如何处理COM类注册条目? 那么,如果你已经configuration了Fusion Log Viewer(.NET 2.0 SDK中的Fuslogvw.exe)来logging程序集加载,那么在安装完成之后运行它,你会注意到Regasm.exe在构build设置项目。 但是,它不执行任何注册。 会发生什么情况是Visual Studio使用/regfile选项运行Regasm来创build.reg文件,该文件包含获取步骤1所需信息所需的registry项,并将此.reg文件内部导入到安装项目中。 因此,如果您想查看Visual Studio将在MSI设置中创build哪些类注册条目,则可以使用/regfile选项/regfile运行Regasm

但是,在使用/regfile开关自己运行RegAsm时,我注意到Programmable开关没有被包含在内。 然后我把日志logging到由我的方法标记为ComRegisterFunctionAttributeComUnregisterFunctionAttribute ,发现它们在运行RegAsm没有/regfile开关都调用,但是在用/regfile开关运行时不会被调用,也不会在通过运行时被调用。由Visual Studio安装项目创build的MSI包。

Regasm.exe的帮助文件证实了这一点(强调添加):

您可以使用/regfile选项来生成包含registry项的.reg文件,而不是直接对registry进行更改。 您可以通过使用registry编辑器工具(Regedit.exe)导入.reg文件来更新计算机上的registry。 请注意该.reg文件不包含可由用户定义的registry函数进行任何registry更新。

那么解决scheme就是自己添加Programmable密钥。 这可以如下完成:

  1. 在安装项目中,打开registry编辑器。 通过右键单击HKEY_CLASSES_ROOT文件夹,然后select“新build”,然后select“密钥”,在HKEY_CLASSES_ROOT下创build一个名为CLSID的新密钥。
  2. CLSID项下,添加一个为GUID命名的新键,包括大括号。
  3. 在您添加的新GUID密钥下,添加一个名为Programmable的密钥。 你不需要在这个键里input任何值。 但是,我们确实需要强制它被创build。 因此,右击Programmable键并select“属性窗口”。 然后将AlwaysCreate属性更改为True

一旦你完成了这个工作,你不再需要使用ComRegisterFunctionAttribute和ComUnregisterFunctionAttribute标记的方法,但是当你通过RegAsm进行安装而不是通过安装项目进行安装的时候,我仍然会留下这些方法。

此时您已准备好部署。 build立您的解决scheme,然后右键单击您的安装项目,然后select“构build”。 然后,您可以使用创build的Setup.exe和.MSI文件部署到客户端计算机。

另外需要考虑的是,当通过Excel的加载项对话框添加自动化加载项时,会显示一条错误消息,指出“找不到Mscoree.dll,是否要删除加载项? “ 或者非常相似的东西。 这个错误信息可以被忽略,并且无论你回答什么,你的加载项都会运行,但是安装你的加载项的客户端可能会很惊人。

这种情况以及如何解决这个问题的解释, 在 Eric Carter 编写的用.NET编写用户自定义函数的文章中有详细描述。

问题是, InprocServer32键的默认值只是mscorree.dll ,这足以让.NETfind它,但导致Excel抱怨。 解决scheme是确保InprocServer32项的默认值包含系统目录的完整path。 例如,在32位窗口上,它应该读取C:\Windows\system32\mscoree.dll 。 这个path需要改变,但是,取决于它所安装的系统。 所以这个path不应该是硬编码的。

Eric Carter通过修改ComRegisterFunctionAttributeComUnregisterFunctionAttribute标记的方法来处理以下内容:

 // C#: [ComRegisterFunctionAttribute] public static void RegisterFunction(Type type) { Registry.ClassesRoot.CreateSubKey( GetSubKeyName(type, "Programmable")); RegistryKey key = Registry.ClassesRoot.OpenSubKey( GetSubKeyName(type, "InprocServer32"), true); key.SetValue("", System.Environment.SystemDirectory + @"\mscoree.dll", RegistryValueKind.String); } [ComUnregisterFunctionAttribute] public static void UnregisterFunction(Type type) { Registry.ClassesRoot.DeleteSubKey( GetSubKeyName(type, "Programmable"), false); } private static string GetSubKeyName(Type type, string subKeyName) { System.Text.StringBuilder s = new System.Text.StringBuilder(); s.Append(@"CLSID\{"); s.Append(type.GUID.ToString().ToUpper()); s.Append(@"}\"); s.Append(subKeyName); return s.ToString(); } 

翻译成VB.NET,这相当于:

 'VB.NET: <ComRegisterFunctionAttribute()> _ Public Shared Sub RegisterFunction(ByVal type As Type) Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable")) Dim key As RegistryKey = Registry.ClassesRoot.OpenSubKey(GetSubKeyName(type, "InprocServer32"), true) key.SetValue("", (System.Environment.SystemDirectory + "\mscoree.dll"), RegistryValueKind.String) End Sub <ComUnregisterFunctionAttribute()> _ Public Shared Sub UnregisterFunction(ByVal type As Type) Registry.ClassesRoot.DeleteSubKey(GetSubKeyName(type, "Programmable"), false) End Sub Private Shared Function GetSubKeyName(ByVal type As Type, ByVal subKeyName As String) As String Dim s As System.Text.StringBuilder = New System.Text.StringBuilder s.Append ("CLSID\{") s.Append(type.GUID.ToString.ToUpper) s.Append ("}\") s.Append (subKeyName) Return s.ToString End Function 

这可以工作,但是在本地机器上运行RegAsm时,程序集正确注册的问题与在Visual Studio安装项目中尝试使用此程序时出现的问题相同。

解决scheme,再次,是添加我们自己的registry项。 但是,这次我们必须创build一个使用[SystemFolder]属性的默认值,该属性等同于上面的Eric Carter代码中使用的System.Environment.SystemDirectory调用。

为此,请在我们之前创build的CLSID\\{GUID}键下添加一个名为InprocServer32的Key。 然后,右键单击新的InprocServer32项,然后select“新build”,然后select“string值”。 结果将是一个新的值,名为New Value #1 ,但您将处于编辑模式,允许您重新命名它。 你想要做的是删除所有的字符,然后按回车 。 通过删除名称中的所有字符,您正在创build一个默认值,registry值的图标将被自动更名为“(默认)”。 然后右键单击此默认值图标并select“属性窗口”。 在属性窗口中,将Value属性设置为"[SystemFolder]mscoree.dll" (不带引号)。

然后,您可以右键单击您的安装项目并select“Build”,然后就可以部署了。

还有最后一件事要担心。 如果您正在安装到Excel 2007或更高版本,上述将100%。 如果您在Excel 2003或更低版本上安装,则需要包含以下内容:

FIX:使用Microsoft Visual Studio 2005创build的加载项,智能文档或智能标记不能在Office中运行

如何部署它的详细说明在这里由Divo给出。

如果您不应用此修复程序,则所有内容都将正确注册,并且您甚至可以成功添加自动化加载项 – 一切看起来都正常 – 但是工作表函数将失败,并且仍然会获得#NAME? 作为结果的错误。 (但是,再次,您不需要Excel 2007及更高版本。)

所以,最后,TLB并不重要。 在我所有的testing中,我使用了RegAsm而不是/ TLB开关,并且在通过安装项目注册时没有包含任何TLB。 所以我没有从Vista这样做, 尝试将TLB文件添加到安装项目时遇到问题 。

我希望这会有所帮助,休,还有希望在未来可能绊倒这个线索的任何人…

麦克风