托管注册免费COM服务器不会启动 [英] Managed Reg-Free COM Server Won't Activate

查看:206
本文介绍了托管注册免费COM服务器不会启动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我开始与客户机和服务器COM引用和其他东西的一个非常复杂的系统,直到我意识到我不能连得微软的代码示例为一个注册免费COM激活工作,我剪了下来,下来。托管C#编写的COM服务器



服务器代码:

 使用系统; 
使用System.Collections.Generic;
使用System.Linq的;
使用System.Text;使用System.Runtime.InteropServices.ComTypes
;使用System.Runtime.InteropServices
;
使用System.ComponentModel;

命名空间ClassLibrary1的
{
[的Guid(A7AC6D8C-FF17-4D2C-A3B1-2C8690A8EA04)
,标记有ComVisible特性(真)]
公共接口IClass1
{
[DISPID(1)]
串DummyFunction(inputValue的字符串)
}

[的Guid(81723475-B5E3-4FA0-A3FE-6DE66CEE211C),
ClassInterface(ClassInterfaceType.None),
ComDefaultInterface(typeof运算(ICla​​ss1) ),
标记有ComVisible特性(真)]
公共类的Class1:IClass1
{
公共字符串DummyFunction(inputValue的字符串)
{
返回inputValue.Substring( 0,1)+插入+ inputValue.Substring(1);
}
}
}



客户端VB6代码:



<预类=朗动prettyprint-覆盖> 尺寸C作为ClassLibrary1.Class1
设置c = 1类新的
MSGBOXç .DummyFunction(本)

客户端C ++代码:



 的#includestdafx.h中

#进口< ClassLibrary1的。 TLB> raw_interfaces_only

使用的命名空间ClassLibrary1的;

INT _tmain(INT ARGC,_TCHAR *的argv [])
{
IClass1Ptr磷;

HRESULT HR =的CoInitializeEx(NULL,COINIT_MULTITHREADED);
HR = CoCreateInstance的(__ uuidof(1级),NULL,CLSCTX_INPROC_SERVER,__uuidof(ICla​​ss1),(无效**)及P);
如果(FAILED(HR))
{
_tprintf_s(_T(错误%x\\\
),小时);
CoUninitialize();
返回1;
}
_bstr_t B = _T(巴比);
BSTR B2;
P-> DummyFunction(B,&安培; B2);
wprintf_s(L%s\\\
,B2);
P->发行();
CoUninitialize();
返回0;
}



这两个客户的正常工作,当我删除所有注册免费的COM代码和与regasm /基本代码注册ClassLibrary1.dll。



然后我注销ClassLibrary1的,并尝试引入REG-COM免费与文件Project1.exe.manifest VB6的客户端:

 < XML版本=1.0编码=UTF-8独立?=YES> 
<装配的xmlns =瓮:架构 - 微软COM:asm.v1manifestVersion =1.0>
< assemblyIdentity TYPE =win32的NAME =PROJECT1版本=1.0.0.0/>
<&依赖性GT;
< dependentAssembly>
< assemblyIdentity名称=ClassLibrary1的版本=1.0.0.0/>
< / dependentAssembly>
< /依赖性>
< /装配>

和ClassLibrary1.manifest:

 <?XML版本=1.0编码=UTF-8>?; 
<装配manifestVersion =1.0的xmlns =瓮:架构 - 微软COM:asm.v1>
< assemblyIdentity版本=1.0.0.0NAME =ClassLibrary1的/>
< clrClass CLSID ={81723475-B5E3-4FA0-A3FE-6DE66CEE211C}NAME =ClassLibrary1.Class1tlbid ={F8A2D334-5BBB-4007-8308-A1417052E6D6}>< / clrClass> ;
<文件名=ClassLibrary1.dll>< /文件>
< /装配>

现在我得到错误429(ActiveX组件不能创建对象)有时,和(莫名)的自动化错误等次:




运行时错误'-2146234304(80131040)':
自动化错误




然后我尝试引入COM隔离到C ++客户端:



现在,当我运行C ++客户端,输出仅仅是




错误800401f9



解决方案

在很多通过各种样品与微软合作支持的试验,我已经确定的许多的在试图实现与非托管C ++ COM客户端管理的COM服务器中出现的陷阱。这里是信息的关键部分,我记得,它可以应用到示例代码中的问题,以确保它的工作原理。




  1. 客户端不应在C ++项目中使用隔离COM设置。我的记忆正在消失,但微软的支持告诉我,这是别的东西 - 我想开发一个孤立的COM接口至未管理的COM服务器,而不是管理的COM服务器。虽然这不是在所有从它的描述清楚在 HTTPS ://msdn.microsoft.com/en-us/library/zzbcs3x5(v = vs.120)的.aspx

  2. 客户端可以选择是或。无为嵌入清单的设置,但如果是选择,则包含依赖程序集信息清单必须被作为一个输入清单文件中提供。如果嵌入清单不包含依赖程序集信息,那么任何外部清单文件将被忽略。此外,还要确保配置(调试,例如)正在编辑匹配被测试的配置!

  3. ,则assemblyIdentity元素而服务器清单的必须的含公钥,否则CoCreateInstance的过程中会发生HRESULT错误0x80131040。

  4. 嵌入RT_MANIFEST资源作为托管代码Win32资源是不容易使用Visual Studio 2013,因为C#和VB.NET项目往往要嵌入资源,管理资源的.NET,而不是Win32的资源(您可以通过资源浏览器打开该DLL的输出文件验证这一点,并注意.NET可执行普遍得到版本资源和别的不多,即使项目有一个清单文件包括在内)。要解决这个问题的方法之一是创建这样一个RC文件:



-

 的#define RT_MANIFEST 24 
的#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST ClassLibrary1.manifest

然后添加一个预生成步骤是这样的:

 C: \Program文件(x86)\Windows Kits\8.1\bin\x86\rc.exe$(PROJECTDIR)ClassLibrary1.rc

然后在项目设置应用程序选项卡,将资源使用ClassLibrary1.res代替图标和明显。但这种带有问题:首先,路径RC.EXE不易没有硬编码它来定义;其次,从AssemblyInfoCommon的版本信息将被忽略,因为在RC文件中的资源完全替代,将在.NET编译器生成所有的Win32资源。



另一种可能性是简单地保持服务器COM DLL清单文件分开,而不是把它嵌入作为一种资源。我已阅读,这未必是可靠的,但它适用于Windows 7企业版64位SP1。




  • 要确保管理的客户端载入正确的.NET运行时,它需要定义如何加载.NET一个配置文件( ConsoleApplication1.exe.config )。对于.NET 4.5,我已经看到这项工作:



  • -

     <结构> 
    <启动useLegacyV2RuntimeActivati​​onPolicy =真正的>
    < supportedRuntime版本=V4.0/>
    < /启动>
    < /结构>



    虽然.NET 3.5,似乎useLegacyV2RuntimeActivati​​onPolicy需要被切换:



     <结构> 
    <启动useLegacyV2RuntimeActivati​​onPolicy =false的>
    < supportedRuntime版本=V3.5/>
    < /启动>
    < /结构>




  • 要检查所有的这一点很重要框架和CLR版本同步。而要明白,CLR版本是不一样的框架的版本是很重要的。例如,如果你想建立在.NET 3.5的托管COM服务器的CLR版本(runtimeVersion)应该是2.0.50727,但.NET版本(supportedRuntime)应该是V3.5。


  • 请确保COM服务器的.NET框架的目标版本的客户supportedRuntime匹配。如果它被命令行构建,则可能无法从Visual Studio项目文件,拿起框架版本(例如,如果运行VB.NET编译的C#直接而不是调用的MSBuild),确保该版本是针对框架的正确版本。




  • 我没有验证所有尚未之上,但打算走过这整个过程很快,以验证我抓住一切。以下是我结束了,我还没有提到:



    ConsoleApplication1.exe.manifest (源目录,被复制或在构建时镶嵌成的输出目录)

     <?XML版本=1.0编码=UTF-8独立= ?是> 
    <装配的xmlns =瓮:架构 - 微软COM:asm.v1manifestVersion =1.0>

    < assemblyIdentity
    型=win32的
    NAME =ConsoleApplication1
    版本=1.0.0.0/>
    <&依赖性GT;
    < dependentAssembly>
    < assemblyIdentity
    型=win32的
    NAME =ClassLibrary1的
    版本=1.0.0.0
    公钥=541b4aff0f04b60a/>
    < / dependentAssembly>
    < /依赖性>
    < /装配>



    ClassLibrary1.manifest



     <?XML版本=1.0编码=UTF-8>?; 
    <装配manifestVersion =1.0的xmlns =瓮:架构 - 微软COM:asm.v1>
    < assemblyIdentity TYPE =win32的NAME =ClassLibrary1的版本=1.0.0.0公钥=541b4aff0f04b60a/>
    < clrClass CLSID ={81723475-B5E3-4FA0-A3FE-6DE66CEE211C}进程id =ClassLibrary1.Class1的ThreadingModel =两者NAME =ClassLibrary1.Class1runtimeVersion =V2.0.50727> < / clrClass>
    < /装配>



    编辑:



    现在经历和验证具有完全错误信息资讯等每一个细节。



    我开始通过创建一个包含两个项目与所有默认值和代码的单一解决方案在问题中。我开始不带清单的文件,也没有任何的问题中提及的项目设置,并明确调用出来的时候我正在下面的过程中这些变化。这些是路径使这个项目工作的步骤和错误




    1. 错误:1类:未声明的标识符。需要运行一个开发人员命令提示符,为了得到一个TLB执行以下命令行文件,该文件的C ++代码可以导入: tlbexp ClassLibrary1.dll

    2. 将在TLB文件到上ConsoleApplication1项目目录和重新构建。同样的错误

    3. 在替换尖括号#进口< ClassLibrary1.tlb> raw_interfaces_only 与报价,所以读#进口ClassLibrary1.tlbraw_interfaces_only 。重建:成功

    4. 在这一点上,如果我们运行我们得到错误80040154 (没有注册类),因为我们还没有注册组件也设立免注册COM。

    5. 明知试图建立隔离COM客户端将提出错误800401f9 我们' LL跳过,只是尝试创建一个客户清单。创建一个包含以下内容的文本文件,并将其保存为ConsoleApplication1.exe.manifest在上ConsoleApplication1项目目录:



    -

     <?XML版本=1.0编码=UTF-8独立=YES>?; 
    <装配的xmlns =瓮:架构 - 微软COM:asm.v1manifestVersion =1.0>
    < assemblyIdentity TYPE =win32的NAME =ConsoleApplication1版本=1.0.0.0/>
    <&依赖性GT;
    < dependentAssembly>
    < assemblyIdentity名称=ClassLibrary1的版本=1.0.0.0/>
    < / dependentAssembly>
    < /依赖性>
    < /装配>



    <醇开始=6>

  • 在这点上,出现前面提到的步骤在此解决方案是有点过分复杂。你可以简单地包含在项目清单文件通过显示隐藏文件,并使用包括项目清单文件命令。

  • 运行在这一点上会出现错误信息该应用程序未能启动,因为其副作用并排配置不正确,请查看应用程序事件日志,或使用命令行工具sxstrace.exe更多详细信息。这部分是因为我们还没有把ClassLibrary1.dll任何地方ConsoleApplication1.exe中可以找到它。在这一点上分析sxstrace输出看起来是这样的:



  • -

     信息:解析清单文件C:\Users\bmarty\Documents\Visual工作室2013\Projects\RegFreeCOM\Debug\ConsoleApplication1.exe。 
    产品介绍:清单定义身份是ConsoleApplication1,TYPE =win32的版本=1.0.0.0。
    产品介绍:参考:ClassLibrary1的,版本=1.0.0.0
    产品介绍:解决参考ClassLibrary1的,版本=1.0.0.0。
    产品介绍:为的ProcessorArchitecture ClassLibrary1的解决参考,版本=1.0.0.0。
    产品介绍:解决文化中性参考。
    产品介绍:应用绑定策略。
    产品介绍:没有找到绑定策略重定向。
    产品介绍:开始组装探测。
    产品介绍:没有找到组件WinSxS文件。
    产品介绍:尝试在C到探测清单:\Users\bmarty\Documents\Visual工作室2013\Projects\RegFreeCOM\Debug\ClassLibrary1.DLL。
    产品介绍:尝试在C到探测清单:\Users\bmarty\Documents\Visual工作室2013\Projects\RegFreeCOM\Debug\ClassLibrary1.MANIFEST。
    产品介绍:尝试在C到探测清单:\Users\bmarty\Documents\Visual工作室2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.DLL。
    产品介绍:尝试在C到探测清单:\Users\bmarty\Documents\Visual工作室2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.MANIFEST。
    产品介绍:没有找到明显的文化中性。
    产品介绍:端组件探测。
    错误:无法解析引用ClassLibrary1的,版本=1.0.0.0。
    错误:激活上下文生成失败。
    端激活上下文生成。




  • 复制ClassLibrary1.dll文件到同一目录ConsoleApplication1.exe中,因为我们没有为通过该COM依赖可识别的文件提供的任何清单不改变任何东西。因此,下一步是创建ClassLibrary1的清单。 ClassLibrary1.manifest的一个版本已经存在于问题。让我们尝试一个创造与内容的文本文件并将其保存在ClassLibrary1项目目录ClassLibrary1.manifest。要包含在项目中,让我们尝试相同的简单项目中包括命令(再次,打开隐藏文件的知名度,使之成为可能)。将新的ClassLibrary1.dll与ConsoleApplication1.exe中的目录,并运行时,现在会发生什么?

  • 同样的错误和sxstrace的结果发生,因为在一个托管的DLL清单文件没有得到嵌入作为Win32资源,你可以通过使用Visual Studio,这显示了文件的Win32的资源打开DLL文件验证。它显示了版本资源而已。因此,让我们排除在ClassLibrary1的清单,只是清单文件复制到ConsoleApplication1.exe中的位置,作为外部文件来代替。

  • 成功!程序运行和正常完成。但是,如果我们想要使用不同版本的.NET框架的构建一个组成部分。或者,也许你的测试是不是在这一点上工作,因为你的Visual Studio默认为一个不同的版本?现在,我看到我的ClassLibrary1的项目默认为.NET 3.5。会发生什么,如果我将其更改为4.0,重建,复制并再次运行?

  • 发生错误8013101b。对应(根据谷歌搜索),以COR_E_NEWER_RUNTIME这也意味着清单中指定的模块没有被发现。发生这种情况时,例如,加载.NET 2.0的EXE尝试引用与.NET 4.0内置的DLL。所以,现在我们要告诉联合国管理的客户端EXE的.NET framework版本,加载它,因为它解决了COM引用。这是一个名为ConsoleApplication1.exe.config配置文件完成的。只要创建一个新的文本文件,并在ConsoleApplication1.exe中目录名称保存它。它具有以下内容:



  • -

     <结构> 
    <启动useLegacyV2RuntimeActivati​​onPolicy =真正的>
    < supportedRuntime版本=V4.0/>
    < /启动>
    < /结构>

    如果useLegacyV2RuntimeActivati​​onPolicy被排除在这种情况下仍然会出现同样的错误。不幸的是,我不完全理解为什么,但我怀疑它是与较新的4.0版运行时激活策略默认为加载CLR 2.0版,如果要加载的可执行文件并没有明确引用.NET 4.0(其中非托管代码不会因为它不明确引用.NET期)。




  • 再次成功!别急,还有更精彩的。如果你的COM DLL与密钥签名(有很强的名称)?让我们一键添加到ClassLibrary1的,其配置为签署该项目的签名选项卡上的DLL使用,并且看到,当我们更新的DLL复制到ConsoleApplication1.exe中的目录会发生什么。

  • 现在,我们得到错误80131040(以下简称位于集清单定义不匹配的程序集引用)。和sxstrace和fuslogvw是令人沮丧的无助在这里产生的关于正在发生的事情的任何的信息。幸运的是,现在我知道,在这个特殊的免费REG-泡沫的情况下,它是由缺少在描述(在两个清单文件)ClassLibrary1的所述assemblyIdentity元素公钥属性造成的。但你如何得到公钥价值?运行 SN-T ClassLibrary1.dll 从开发人员的命令提示符。更新ClassLibrary1.manifest和ConsoleApplication1.exe.manifest后,记得重建ConsoleApplication1.exe中,如果清单是嵌入式的,并ClassLibrary1.manifest复制到ConsoleApplication1.exe中的目录。再次运行和?

  • 我通过误差sxstrace的帮助下解决了一些更多的回旋去了,但是这是由于愚蠢的错误。对于其他的谁做出傻事错误的利益,这里有更多的事情需要注意,如果你得到sxstrace错误:一)确保你使用属性公钥而不是其他一些可笑的名称,如 privateKeyToken ;二)请确保您在服务器端清单匹配的客户端上的清单中的assemblyIdentity中指定的所有属性,并且你没有键入=win32的指定在一个而不是其他。

  • 成功!输出是 B插入obby



  • 我也应该注意到,VB6客户端也适用使用下列文件与VB6客户一起:



    Project1.exe.config:

     <结构> 
    <启动useLegacyV2RuntimeActivati​​onPolicy =真正的>
    < supportedRuntime版本=V4.0/>
    < /启动>
    < /结构>



    Project1.exe.manifest:



     <?XML版本=1.0编码=UTF-8独立=YES>?; 
    <装配的xmlns =瓮:架构 - 微软COM:asm.v1manifestVersion =1.0>
    < assemblyIdentity TYPE =win32的NAME =PROJECT1版本=1.0.0.0/>
    <&依赖性GT;
    < dependentAssembly>
    < assemblyIdentity名称=ClassLibrary1的版本=1.0.0.0公钥=541b4aff0f04b60a/>
    < / dependentAssembly>
    < /依赖性>
    < /装配>



    不过,似乎是报告ActiveX组件不能创建对象的倾向(在罕见的情况下运行时错误429)可执行文件时建成并创建配置和清单文件之前运行。重建EXE文件似乎解决它,但我不能回来的问题,所以很难确定具体原因。



    我以为我是一个相当不错的解决问题的,但一些关于免费REG-COM配置问题报道众多的运动部件和大量无用的错误代码和错误信息使这几乎不可能找出没有一些扎实的经验,知识或微软的源代码中。希望这个答案可以帮助别人获得类似的经历。如果您了解更多请延长这个答案!



    附录1



    托管COM服务器的清单中的即可的,如果你使用添加适当和方便嵌入 - >新项...命令,在项目中添加一个应用程序清单文件。这增加了app.manifest叫到项目文件。但真正棘手的部分是,它在不能被复制通过Visual Studio的UI任何其他方式,除了通过一个扭曲变通的方式这样做。由于项目设置窗口的应用程序选项卡上的清单字段为类库型项目,清单,这通常在此处设置禁用,不能为一个类库进行设置。但是你可以暂时更改项目一个Windows应用程序,改变这里的清单选择,然后将其还原到一个类库。该设置将坚持这样选择的清单得到正确嵌入。您可以通过查看项目文件验证在文本编辑器的设置。寻找:

     <&的PropertyGroup GT; 
    < ApplicationManifest> app.manifest< / ApplicationManifest>
    < /&的PropertyGroup GT;



    附录2



    错误0x80131040尚可与采取所有上述措施出现。为了帮助缩小这个原因,它有助于使用融合日志查看器查看有关的组件都被加载并解决所发生的事情的更多信息。谷歌Fuslogvw有关如何查看这个日志的详细信息(fuslogvw.exe是安装Visual Studio时提供的工具)。同样重要的是要认识到这个应用程序,默认情况下,显然不显示任何信息,直到您将其配置到信息记录到文件中,重现该问题,然后重新启动应用程序,他们产生后读取日志文件。而且,根据MSDN文档,也很重要要记住运行此实用程序管理员。



    一旦你通过了所有的障碍跑fuslogvw.exe,你可以看到像这样在日志中:

     警告:比较程序集名称时发生不匹配:主版本
    ERR :程序集引用不匹配发现大会的定义。
    错误:未能完成组装(HR = 0x80131040)的设置。探测终止。



    尽管COM服务器的清单文件中列出的版本为1.0.0.0的事实,这是不是从COM客户参考服务器绑定时使用(只)版本。我的客户EXE文件试图引用1.0.0.0,这完全匹配​​的COM服务器的清单文件中的版本,但它不匹配的DLL的.NET版本。在.NET服务器DLL纠正客户端和服务器清单文件,以反映该版本实际上后,再错误0x80131040走了,fuslogvw.exe的关键是识别,作为问题的根源。



    如果客户清单是符合实际的.NET的DLL版本同步,但是服务器DLL的清单文件并不反映这个版本中,将出现一个不同的错误:




    应用程序未能启动,因为它并排侧
    配置不正确。请参阅应用程序事件日志或
    使用命令行工具sxstrace.exe更多详细信息。




    附录3



    错误0xc0150002或下面的消息可能会报告:




    应用程序无法正常启动(0xc0150002),点击确定
    ,来关闭应用程序。




    我已经看到这种情况发生在一个情况下客户清单包埋在非托管的DLL,而不是一个非托管的EXE,的清单的 assemblyIdentity 元素没有服务器的<$ C $完全匹配C> assemblyIdentity 。客户端有一个额外的的ProcessorArchitecture =86在它的服务器没有指定,造成不匹配。不幸的是,我不知道如何去学习这个幸运的是,没有思想,检查清单文件,看它们是否匹配(或阅读这篇文章)。该错误不明确指向一个清单文件作为问题的根源,所以你只需要知道,有这么错误消息,这项事业之间可能存在的关联。



    附录4



    我所看到的外部清单文件被完全忽略产生一个完全空sxstrace日志,即使所涉及的可执行文件没有嵌入清单。这显然​​可以发生,因为激活上下文缓​​存的结果(在的 http://csi-windows.com/blog/all/27-csi-news-general/245-find-out-why-您-外部表现,是幸福,忽略)。要解决这个问题,你可以使用下面的命令来触摸的清单被忽略的文件的日期戳:

     复制/ b myfile.exe + ,, 



    附录5



    我看到另外一个难以解释类未注册错误( 0x80040154的 - REGDB_E_CLASSNOTREG )时,当调用CoCreateInstance在下列条件下:




    1. 一个CPP文件包含在全局范围内实例化的类的构造函数,以便动态初始化将调用在的DllMain 如果 / CLR 开关的不可以应用到CPP文件,或构造在 .cctor 如果 / CLR 开关的应用到文件。

    2. 的DLL有一个嵌入式的清单,使其能够引用的COM类通过注册免费COM创建。

    3. 的COM DLL在管理实施代码(有一个COM可调用包装又名CCW)在.NET 2.0。

    4. 装入的DLL确实的不可以有一个REG-免费清单引用的EXE创建COM类。

    5. 的COM DLL是的不可以 regasm 注册。

    6. 的CPP文件调用的CoCreateInstance 已应用到C ++编译器设置了 / CLR 开关。



    如果任何的最后3个条件被改变,问题消失。 (另外,如果最后一个条件被改变,你可能会得到一个加载程序锁,由于1 - 阅读加载程序锁,它是相对于在CLR的混合组件)的初始化。所以,如果你遇到类似情况一类未注册的错误,考虑是否可以改变任何的那最后3个条件来纠正错误。
    注意:我有一个很难钉下来#6的行为。看来切换这个效果也取决于#1的状态。它看起来像调用构造函数(包括其的CoCreateInstance )后,DLL是满载还是引起类未注册,而调用DLL初始化过程中构造会成功,如果<$ C未指定$ C> / CLR 开关。我暂时的解决办法是在托管C ++,因为它是COM组件和非托管代码的其余部分之间的相对简单的接口类重码客户端CPP文件。所以,现在没有更多的COM在这个客户端,只是一个.NET参考。


    I started with a very sophisticated system of clients and servers with COM references and other things, and I've cut down and down until I realized I can't even get Microsoft sample code to work for registration free COM activation of a managed COM server written in C#.

    Server code:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices.ComTypes;
    using System.Runtime.InteropServices;
    using System.ComponentModel;
    
    namespace ClassLibrary1
    {
       [Guid("A7AC6D8C-FF17-4D2C-A3B1-2C8690A8EA04")
       ,ComVisible(true)]
       public interface IClass1
       {
          [DispId(1)]
          string DummyFunction(string inputValue);
       }
    
       [Guid("81723475-B5E3-4FA0-A3FE-6DE66CEE211C"),
       ClassInterface(ClassInterfaceType.None),
       ComDefaultInterface(typeof(IClass1)),
       ComVisible(true)]
       public class Class1 : IClass1
       {
          public string DummyFunction(string inputValue)
          {
             return inputValue.Substring(0, 1) + " Inserted " + inputValue.Substring(1);
          }
       }
    }
    

    Client VB6 Code:

    Dim c As ClassLibrary1.Class1
    Set c = New Class1
    MsgBox c.DummyFunction("Ben")
    

    Client C++ Code:

    #include "stdafx.h"
    
    #import <ClassLibrary1.tlb> raw_interfaces_only
    
    using namespace ClassLibrary1;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
       IClass1Ptr p;
    
       HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
       hr = CoCreateInstance(__uuidof(Class1), NULL, CLSCTX_INPROC_SERVER, __uuidof(IClass1), (void **)&p);
       if (FAILED(hr))
       {
          _tprintf_s(_T("Error %x\n"), hr);
          CoUninitialize();
          return 1;
       }
       _bstr_t b = _T("Bobby");
       BSTR b2;
       p->DummyFunction(b, &b2);
       wprintf_s(L"%s\n", b2);
       p->Release();
       CoUninitialize();
        return 0;
    }
    

    Both of the clients work fine when I remove all Reg-Free COM code and register the ClassLibrary1.dll with regasm /codebase.

    Then I unregister ClassLibrary1, and try to introduce Reg-Free COM for the VB6 client with the file Project1.exe.manifest:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
       <assemblyIdentity type="win32" name="Project1" version="1.0.0.0" />
       <dependency>
       <dependentAssembly>
       <assemblyIdentity name="ClassLibrary1" version="1.0.0.0" />
       </dependentAssembly>
       </dependency>
    </assembly>
    

    And ClassLibrary1.manifest:

    <?xml version="1.0" encoding="utf-8"?>
    <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
       <assemblyIdentity version="1.0.0.0" name="ClassLibrary1" />
       <clrClass clsid="{81723475-B5E3-4FA0-A3FE-6DE66CEE211C}" name="ClassLibrary1.Class1" tlbid="{F8A2D334-5BBB-4007-8308-A1417052E6D6}"></clrClass>
       <file name="ClassLibrary1.dll" ></file>
    </assembly>
    

    Now I get Error 429 (ActiveX Component can't create object) sometimes, and (inexplicably) an automation error other times:

    Run-time error '-2146234304 (80131040)': Automation Error

    then I try to introduce COM Isolation into the C++ client:

    Now when I run the C++ client, the output is merely

    Error 800401f9

    解决方案

    After many trials working through various samples with Microsoft support, I have identified many pitfalls that arise in attempting to implement a managed COM server with an unmanaged C++ COM client. Here are the key pieces of information that I recall, which can be applied to the sample code in the question to make sure it works.

    1. The client should not use the Isolated COM settings in the C++ project. My memory is fading, but Microsoft support tells me this is for something else -- I think for developing an isolated COM interface to an un-managed COM server instead of a managed COM server. Although this is not at all clear from its description at https://msdn.microsoft.com/en-us/library/zzbcs3x5(v=vs.120).aspx.
    2. The client may select "Yes" or "No" for the "Embed Manifest" setting, but if "Yes" is selected, then the manifest that includes the dependent assembly information must be provided as an input manifest file. If the embedded manifest does not contain dependent assembly information, then any external manifest file would be ignored. Also, make sure that the configuration (Debug, for example) being edited matches the configuration being tested!
    3. If the COM server is signed with a key (is strongly named), then the assemblyIdentity element in both the client manifest and the server manifest must contain a publicKeyToken, otherwise HRESULT error 0x80131040 will occur during CoCreateInstance.
    4. Embedding the RT_MANIFEST resource as a Win32 resource in managed code is not easy with Visual Studio 2013 because C# and VB.NET projects tend to want to embed resources as managed .NET resources, not Win32 resources (you can verify this by opening the DLL output file with the resource viewer and notice that .NET executables generally get a version resource and not much else, even if the project has a manifest file included). One way to get around this is to create a RC file like this:

    -

    #define RT_MANIFEST 24
    #define MANIFEST_RESOURCE_ID 1
    MANIFEST_RESOURCE_ID RT_MANIFEST ClassLibrary1.manifest
    

    Then add a pre-build step like this:

    "C:\Program Files (x86)\Windows Kits\8.1\bin\x86\rc.exe"  "$(ProjectDir)ClassLibrary1.rc"
    

    Then in the project settings "Application" tab, change the "Resources" to use ClassLibrary1.res instead of "Icon and manifest". But this comes with problems: firstly, the path to RC.EXE is not easy to define without hard-coding it; secondly, the version information from AssemblyInfoCommon will be ignored because the resources in the RC file totally replace all Win32 resources that would be generated by the .NET compiler.

    Another possibility is to simply keep the server COM DLL manifest file separate and not embed it as a resource. I have read that this may not be reliable, but it works on Windows 7 Enterprise 64-bit SP1.

    1. To ensure that the unmanaged client loads the proper .NET runtime, it needs a config file (ConsoleApplication1.exe.config) that defines how to load .NET. For .NET 4.5, I have seen this work:

    -

    <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0"/>
      </startup>
    </configuration>
    

    While for .NET 3.5, it seems useLegacyV2RuntimeActivationPolicy needs to be switched:

    <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="false">
        <supportedRuntime version="v3.5"/>
      </startup>
    </configuration>
    

    1. It's important to check that all the framework and CLR versions are in sync. And it's important to understand that the CLR versions are not the same as the framework versions. For example, if you want to build a managed COM server on .NET 3.5, the CLR version (runtimeVersion) should be "2.0.50727", but the .NET version (supportedRuntime) should be "v3.5".

    2. Make sure that the COM server's .NET Framework target version matches the client's supportedRuntime. If it is being built from the command line, it may not be picking up the framework version from the Visual Studio project file (for example, if you are running the C# of VB.NET compiler directly instead of calling MSBuild), make sure that the build is targeting the right version of the framework.

    I have not validated all the above yet, but intend to walk through this whole process soon to verify that I caught everything. Here is what I ended up with that I haven't mentioned yet:

    ConsoleApplication1.exe.manifest (in source directory, gets copied or embeded into output directory at build time)

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
    
    <assemblyIdentity
                type = "win32"
                name = "ConsoleApplication1"
                version = "1.0.0.0" />
    <dependency>
                <dependentAssembly>
                            <assemblyIdentity
                                        type="win32"
                                        name="ClassLibrary1"
                                        version="1.0.0.0"
                                        publicKeyToken="541b4aff0f04b60a"/>
                </dependentAssembly>
    </dependency>
    </assembly>
    

    ClassLibrary1.manifest

    <?xml version="1.0" encoding="utf-8"?>
    <assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
       <assemblyIdentity type="win32" name="ClassLibrary1" version="1.0.0.0" publicKeyToken="541b4aff0f04b60a" />
       <clrClass clsid="{81723475-B5E3-4FA0-A3FE-6DE66CEE211C}" progid="ClassLibrary1.Class1" threadingModel="both" name="ClassLibrary1.Class1" runtimeVersion="v2.0.50727"></clrClass>
    </assembly>
    

    EDIT:

    Now to go through and validate every detail with full error message info etc.

    I start by creating a single solution containing two projects with all default values and the code shown in the question. I begin with no manifest files nor any of the project settings mentioned in the question, and will explicitly call out when I am making these changes in the process below. These are the steps and errors that are on the path to making this project work.

    1. Error: "Class1: Undeclared identifier". Need to run a Developer Command Prompt and execute the following command line in order to get a TLB file that the C++ code can import: tlbexp ClassLibrary1.dll
    2. Move the TLB file into the ConsoleApplication1 project directory and re-build. Same Error.
    3. Replace the angle brackets on #import <ClassLibrary1.tlb> raw_interfaces_only with quotes, so it reads #import "ClassLibrary1.tlb" raw_interfaces_only. Rebuild: Success.
    4. At this point, if we run we get Error 80040154 (Class not registered) because we have not registered the component nor set up registration-free COM.
    5. Knowing that trying to set up Isolated COM in the client would present Error 800401f9 we'll skip that and just try to create a client manifest. Create a new text file with the following content, and save it as ConsoleApplication1.exe.manifest in the ConsoleApplication1 project directory:

    -

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
       <assemblyIdentity type="win32" name="ConsoleApplication1" version="1.0.0.0" />
       <dependency>
       <dependentAssembly>
       <assemblyIdentity name="ClassLibrary1" version="1.0.0.0" />
       </dependentAssembly>
       </dependency>
    </assembly>
    

    1. At this point it appears the steps mentioned earlier in this solution are a little over-complicated. You can simply include the manifest file in the project by showing hidden files, and using the "Include In Project" command on the manifest file.
    2. Running at this point would present the error message "The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command line sxstrace.exe tool for more detail." This is partly because we haven't put ClassLibrary1.dll anywhere where ConsoleApplication1.exe can find it. The parsed sxstrace output at this point looks like this:

    -

    INFO: Parsing Manifest File C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ConsoleApplication1.exe.
        INFO: Manifest Definition Identity is ConsoleApplication1,type="win32",version="1.0.0.0".
        INFO: Reference: ClassLibrary1,version="1.0.0.0"
    INFO: Resolving reference ClassLibrary1,version="1.0.0.0".
        INFO: Resolving reference for ProcessorArchitecture ClassLibrary1,version="1.0.0.0".
            INFO: Resolving reference for culture Neutral.
                INFO: Applying Binding Policy.
                    INFO: No binding policy redirect found.
                INFO: Begin assembly probing.
                    INFO: Did not find the assembly in WinSxS.
                    INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1.DLL.
                    INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1.MANIFEST.
                    INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.DLL.
                    INFO: Attempt to probe manifest at C:\Users\bmarty\Documents\Visual Studio 2013\Projects\RegFreeCOM\Debug\ClassLibrary1\ClassLibrary1.MANIFEST.
                    INFO: Did not find manifest for culture Neutral.
                INFO: End assembly probing.
        ERROR: Cannot resolve reference ClassLibrary1,version="1.0.0.0".
    ERROR: Activation Context generation failed.
    End Activation Context Generation.
    

    1. Copying the ClassLibrary1.dll file into the same directory as ConsoleApplication1.exe doesn't change anything because we haven't provided any manifest for the file by which the COM dependency can be identified. So the next step is to create a manifest for ClassLibrary1. One version of ClassLibrary1.manifest is already present in the question. Let's try that one by creating a text file with that content and saving it in the ClassLibrary1 project directory as ClassLibrary1.manifest. To include it in the project, let's try the same simple "Include in Project" command (again, turning on the visibility of hidden files to make that possible). Now what happens when copying the new ClassLibrary1.dll to the directory with ConsoleApplication1.exe and running?
    2. The same error and sxstrace results occur because a manifest file in a managed DLL does not get embedded as a Win32 resource, as you can verify by opening the DLL file with Visual Studio, which shows the file's Win32 resources. It shows the version resource and nothing else. So let's exclude the manifest from the ClassLibrary1 and just copy the manifest file over to ConsoleApplication1.exe's location as an external file instead.
    3. Success! The program runs and completes normally. But what if we want to use a component built with a different version of .NET framework. Or maybe your test isn't working at this point because your Visual Studio defaulted to a different version? Right now I see that my ClassLibrary1 project defaulted to .NET 3.5. What happens if I change it to 4.0, rebuild, copy and run again?
    4. Error 8013101b occurs. That corresponds (according to a google search) to COR_E_NEWER_RUNTIME which also means "A module specified in the manifest was not found." This happens when, for example, an EXE that loaded .NET 2.0 is trying to reference a DLL built with .NET 4.0. So now we have to tell the un-managed client EXE which version of the .NET framework to load as it resolves its COM reference. This is done with a config file named ConsoleApplication1.exe.config. Just create a new text file and save it with that name in the ConsoleApplication1.exe directory. It has the following content:

    -

    <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0"/>
      </startup>
    </configuration>
    

    The same error would still occur if useLegacyV2RuntimeActivationPolicy were excluded in this case. Unfortunately, I don't fully understand why, but I suspect it has something to do with the newer v4.0 runtime activation policy defaulting to loading CLR v2.0 if the executable being loaded does not explicitly reference .NET 4.0 (which un-managed code does not because it doesn't explicitly reference .NET period).

    1. Success again! But wait, there's more. What if your COM dll is signed with a key (has a strong name)? Let's add a key to ClassLibrary1, configure it to be used in signing the DLL on the project's "Signing" tab, and see what happens when we copy the updated DLL to ConsoleApplication1.exe's directory.
    2. Now we get Error 80131040 ("The located assembly's manifest definition does not match the assembly reference"). And sxstrace, and fuslogvw are frustratingly unhelpful here in yielding any information about what is going on. Fortunately, I now know, in this particular reg-free-com scenario, it's caused by the lack of the publicKeyToken attribute on the assemblyIdentity elements describing ClassLibrary1 (in both manifest files). But how do you get the publicKeyToken value? Run sn -T ClassLibrary1.dll from a developer command prompt. After updating ClassLibrary1.manifest and ConsoleApplication1.exe.manifest, remember to rebuild ConsoleApplication1.exe if the manifest is embedded, and to copy ClassLibrary1.manifest to the ConsoleApplication1.exe directory. Run again and?
    3. I went through a few more gyrations of error solving with the help of sxstrace, but that was due to stupid errors. For the benefit of others who make stupid errors, here are some more things to be aware of if you're getting sxstrace errors: a) make sure you're using the attribute publicKeyToken and not some other ridiculous name like privateKeyToken; b) Make sure that all the attributes you specified in the assemblyIdentity on the server side manifest match those on the client side manifest, and that you don't have type="win32" specified on one but not the other.
    4. Success! The output is B Inserted obby

    I should also note that the VB6 client also works by using the following files along with the VB6 client:

    Project1.exe.config:

    <configuration>
      <startup useLegacyV2RuntimeActivationPolicy="true">
        <supportedRuntime version="v4.0"/>
      </startup>
    </configuration>
    

    Project1.exe.manifest:

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
       <assemblyIdentity type="win32" name="Project1" version="1.0.0.0" />
       <dependency>
       <dependentAssembly>
       <assemblyIdentity name="ClassLibrary1" version="1.0.0.0" publicKeyToken="541b4aff0f04b60a" />
       </dependentAssembly>
       </dependency>
    </assembly>
    

    However, there does seem to be a tendency to report "ActiveX Component Can't Create Object" (Runtime error 429) in rare cases when the executable is built and run before creating the configuration and manifest files. Rebuilding the EXE file seems to fix it, but then I can't get the problem to come back, so it's hard to identify a specific cause.

    I thought I was a reasonably good problem solver, but something about the numerous moving parts and numerous unhelpful error codes and error messages reported in reg-free com configuration problems makes this nearly impossible to figure out without some solid experience, inside knowledge or Microsoft source code. Hopefully this answer will help others acquire similar experience. Please extend this answer if you learn more!

    Addendum 1

    The managed COM server's manifest can be properly and easily embedded if you use the "Add"->"New Item..." command on the project to add an "Application Manifest File". This adds a file called app.manifest to the project. But the real tricky part is that it does so in a way that cannot be replicated any other way via the Visual Studio UI, except through one screwy work-around. Since the "Manifest" field on the "Application" tab of the project settings window is disabled for "Class Library" type projects, the manifest, which would normally be set here, cannot be set for a class library. But you can temporarily change the project to a Windows Application, change the Manifest selection here, then restore it to a Class Library. The setting will stick so the selected manifest gets properly embedded. You can verify the setting in a text editor by viewing the project file. Look for:

    <PropertyGroup>
      <ApplicationManifest>app.manifest</ApplicationManifest>
    </PropertyGroup>
    

    Addendum 2

    Error 0x80131040 can still occur with all the above precautions taken. To help narrow down the cause of this, it helps to use the fusion log viewer to see more information about what is happening as assemblies are being loaded and resolved. Google "Fuslogvw" for more information about how to view this log (fuslogvw.exe is a utility provided when Visual Studio is installed). It's also important to realize that this application, by default, apparently does not show any information until you configure it to log information to files, reproduce the problem, then restart the application to read the log files after they are produced. And, according to MSDN documentation, it's also important to remember to run this utility as administrator.

    Once you've passed all the hurdles to running fuslogvw.exe, you may see something like this in the log:

    WRN: Comparing the assembly name resulted in the mismatch: Major Version
    ERR: The assembly reference did not match the assembly definition found.
    ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
    

    Despite the fact that the COM server's manifest file listed the version as 1.0.0.0, that is not the (only) version used when binding from a COM client reference to the server. My client EXE file was trying to reference 1.0.0.0, which exactly matched the version in the COM server's manifest file, but it did not match the .NET version of the DLL. After correcting both the client and server manifest files to reflect the version actually in the .NET server DLL, then error 0x80131040 went away, and fuslogvw.exe was the key to identifying that as the source of the problem.

    If the Client manifest is in sync with the actual .NET DLL version, but the server DLL's manifest file does not reflect this version, a different error will occur:

    The application has failed to start because its side-by-side configuration is incorrect. Please see the application event log or use the command-line sxstrace.exe tool for more detail.

    Addendum 3

    Error 0xc0150002 or the following message may be reported:

    The application was unable to start correctly (0xc0150002), Click OK to close the application.

    I have seen this occur in a case where the client manifest was embedded in an unmanaged DLL rather than an unmanaged EXE, and the manifest's assemblyIdentity element did not exactly match the server's assemblyIdentity. The client had an extra processorArchitecture="x86" in it that the server did not specify, causing a mismatch. Unfortunately I don't know how to learn this without luckily thinking to check the manifest files to see that they match (or reading this article). That error doesn't clearly point to a manifest file being the source of the problem, so you just have to be aware that there's a possible correlation between that error message and this cause.

    Addendum 4

    I have seen external manifest files get completely ignored yielding a completely empty sxstrace log, even when the executables involved have no embedded manifests. This can apparently happen as a result of the activation context cache (a problem documented at http://csi-windows.com/blog/all/27-csi-news-general/245-find-out-why-your-external-manifest-is-being-ignored). To work around this problem you can use the following command to touch the date stamp of the file whose manifest is being ignored:

    copy /b myfile.exe+,,
    

    Addendum 5

    I have seen another hard-to-explain Class Not Registered error (0x80040154 - REGDB_E_CLASSNOTREG) that occurs when calling CoCreateInstance under the following conditions:

    1. An CPP file contains a constructor for a class that is instantiated in global scope so dynamic initialization will call the constructor during DllMain if the /clr switch is not applied to the CPP file, or during .cctor if the /clr switch is applied to the file.
    2. The DLL has an embedded manifest to make it be able to reference the COM class being created via Reg-Free COM.
    3. The COM DLL is implemented in managed code (with a COM-Callable Wrapper aka CCW) in .NET 2.0.
    4. The EXE that loaded the DLL does not have a Reg-Free Manifest referencing the created COM class.
    5. The COM DLL is not registered with regasm.
    6. The CPP file calling CoCreateInstance has the /clr switch applied to the C++ compiler settings.

    If any of the last 3 conditions are altered, the problem goes away. (Additionally, if the last condition is altered, you may get a loader lock due to #1 -- read about loader lock and it's relation to CLR at Initialization of Mixed Assemblies). So if you are encountering a Class Not Registered error in similar circumstances, consider whether you can alter any of those last 3 conditions to resolve the error. Note: I'm having a hard time nailing down the behavior of #6. It seems the effect of switching this also depends the state of #1. It looks like calling the constructor (including its CoCreateInstance) after the DLL is fully loaded still causes Class Not Registered whereas calling the constructor during the DLL initialization will succeed if the /clr switch is not specified. My solution for the time being is to re-code the client CPP file in managed C++ since it was a relatively simple interface class between the COM component and the rest of the un-managed code. So now there's no more COM in this client, just a .NET reference.

    这篇关于托管注册免费COM服务器不会启动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

    查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆