托管的无注册 COM 服务器不会激活 [英] Managed Reg-Free COM Server Won't Activate

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

问题描述

我从一个包含 COM 引用和其他东西的非常复杂的客户端和服务器系统开始,然后我不断削减,直到我意识到我什至无法获得 Microsoft 示例代码来为一个免费注册的 COM 激活工作用 C# 编写的托管 COM 服务器.

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#.

服务器代码:

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);
      }
   }
}

客户端 VB6 代码:

Client VB6 Code:

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

客户端 C++ 代码:

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
"), hr);
      CoUninitialize();
      return 1;
   }
   _bstr_t b = _T("Bobby");
   BSTR b2;
   p->DummyFunction(b, &b2);
   wprintf_s(L"%s
", b2);
   p->Release();
   CoUninitialize();
    return 0;
}

当我删除所有 Reg-Free COM 代码并使用 regasm/codebase 注册 ClassLibrary1.dll 时,两个客户端都可以正常工作.

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

然后我取消注册 ClassLibrary1,并尝试使用 Project1.exe.manifest 文件为 VB6 客户端引入 Reg-Free COM:

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>

还有 ClassLibrary1.manifest:

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>

现在我有时会收到错误 429(ActiveX 组件无法创建对象),有时会(莫名其妙地)出现自动化错误:

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

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

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

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

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

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

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

错误 800401f9

Error 800401f9

推荐答案

在 Microsoft 支持下对各种示例进行多次试验后,我发现了在尝试实现托管 COM 服务器时出现的许多陷阱使用非托管 C++ COM 客户端.以下是我记得的关键信息,可以将其应用于问题中的示例代码以确保其正常工作.

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. 客户端不应在 C++ 项目中使用独立 COM 设置.我的记忆正在消退,但微软的支持告诉我这是为了别的——我认为是为了开发一个与非托管 COM 服务器而不是托管 COM 服务器的隔离 COM 接口.尽管从
  2. 如果 COM 服务器使用密钥签名(强命名),则客户端清单和服务器清单中的 assemblyIdentity 元素必须包含 publicKeyToken,否则在执行期间会出现 HRESULT 错误 0x80131040共同创建实例.
  3. 在 Visual Studio 2013 中将 RT_MANIFEST 资源作为 Win32 资源嵌入托管代码并不容易,因为 C# 和 VB.NET 项目倾向于将资源嵌入为托管 .NET 资源,而不是 Win32 资源(您可以通过打开使用资源查看器查看 DLL 输出文件,并注意 .NET 可执行文件通常会获取版本资源而不是其他资源,即使项目包含清单文件).解决这个问题的一种方法是创建一个这样的 RC 文件:
  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 Kits8.1inx86
c.exe"  "$(ProjectDir)ClassLibrary1.rc"

然后在项目设置应用程序"选项卡中,将资源"更改为使用 ClassLibrary1.res 而不是图标和清单".但这会带来问题:首先,RC.EXE 的路径如果不进行硬编码就不容易定义;其次,来自 AssemblyInfoCommon 的版本信息将被忽略,因为 RC 文件中的资源完全替换了所有由 .NET 编译器生成的 Win32 资源.

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.

另一种可能性是简单地将服务器 COM DLL 清单文件分开,而不是将其作为资源嵌入.我读到这可能不可靠,但它适用于 Windows 7 Enterprise 64 位 SP1.

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. 为确保非托管客户端加载正确的 .NET 运行时,它需要一个定义如何加载 .NET 的配置文件 (ConsoleApplication1.exe.config).对于 .NET 4.5,我已经看到了这项工作:
  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>

对于 .NET 3.5,似乎 useLegacyV2RuntimeActivationPolicy 需要切换:

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

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

  1. 检查所有框架和 CLR 版本是否同步很重要.重要的是要了解 CLR 版本与框架版本不同.例如,如果要在 .NET 3.5 上构建托管 COM 服务器,则 CLR 版本(runtimeVersion)应为2.0.50727",但 .NET 版本(supportedRuntime)应为v3.5".

  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".

确保 COM 服务器的 .NET Framework 目标版本与客户端的 supportedRuntime 匹配.如果它是从命令行构建的,它可能不会从 Visual Studio 项目文件中获取框架版本(例如,如果您直接运行 VB.NET 编译器的 C# 而不是调用 MSBuild),请确保构建的目标是正确的框架版本.

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(在源目录中,在构建时被复制或嵌入到输出目录中)

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>

现在通过完整的错误消息信息等检查并验证每个细节.

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. 错误:Class1:未声明的标识符".需要运行 Developer Command Prompt 并执行以下命令行以获得 C++ 代码可以导入的 TLB 文件:tlbexp ClassLibrary1.dll
  2. 将 TLB 文件移动到 ConsoleApplication1 项目目录并重新构建.同样的错误.
  3. 替换 #import <ClassLibrary1.tlb> 上的尖括号raw_interfaces_only 带引号,所以它读取 #import "ClassLibrary1.tlb" raw_interfaces_only.重建:成功.
  4. 此时,如果我们运行,我们会得到 Error 80040154 (Class not registered),因为我们没有注册组件,也没有设置免注册 COM.
  5. 知道尝试在客户端中设置独立 COM 会出现 Error 800401f9,我们将跳过该步骤并尝试创建客户端清单.新建一个文本文件,内容如下,在ConsoleApplication1工程目录下保存为ConsoleApplication1.exe.manifest:
  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. 此时,此解决方案中前面提到的步骤似乎有点过于复杂.您可以通过显示隐藏文件并在清单文件上使用包含在项目中"命令来简单地将清单文件包含在项目中.
  2. 此时运行会显示错误消息应用程序无法启动,因为它的并排配置不正确.请查看应用程序事件日志或使用命令行 sxstrace.exe 工具了解更多详细信息."这部分是因为我们没有将 ClassLibrary1.dll 放在任何 ConsoleApplication1.exe 可以找到它的地方.此时解析的 sxstrace 输出如下所示:

-

INFO: Parsing Manifest File C:UsersmartyDocumentsVisual Studio 2013ProjectsRegFreeCOMDebugConsoleApplication1.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:UsersmartyDocumentsVisual Studio 2013ProjectsRegFreeCOMDebugClassLibrary1.DLL.
                INFO: Attempt to probe manifest at C:UsersmartyDocumentsVisual Studio 2013ProjectsRegFreeCOMDebugClassLibrary1.MANIFEST.
                INFO: Attempt to probe manifest at C:UsersmartyDocumentsVisual Studio 2013ProjectsRegFreeCOMDebugClassLibrary1ClassLibrary1.DLL.
                INFO: Attempt to probe manifest at C:UsersmartyDocumentsVisual Studio 2013ProjectsRegFreeCOMDebugClassLibrary1ClassLibrary1.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. 将 ClassLibrary1.dll 文件复制到与 ConsoleApplication1.exe 相同的目录不会改变任何内容,因为我们没有为该文件提供任何可以识别 COM 依赖项的清单.所以下一步是为 ClassLibrary1 创建一个清单.问题中已经存在一个版本的 ClassLibrary1.manifest.让我们通过创建一个包含该内容的文本文件并将其作为 ClassLibrary1.manifest 保存在 ClassLibrary1 项目目录中来尝试一下.要将其包含在项目中,让我们尝试同样简单的包含在项目中"命令(同样,打开隐藏文件的可见性以使其成为可能).现在将新的 ClassLibrary1.dll 复制到带有 ConsoleApplication1.exe 的目录并运行时会发生什么?
  2. 出现相同的错误和 sxstrace 结果是因为托管 DLL 中的清单文件没有作为 Win32 资源嵌入,您可以通过使用 Visual Studio 打开 DLL 文件来验证,该文件显示文件的 Win32 资源.它显示版本资源,仅此而已.因此,让我们从 ClassLibrary1 中排除清单,然后将清单文件作为外部文件复制到 ConsoleApplication1.exe 的位置.
  3. 成功!程序运行并正常完成.但是,如果我们想使用使用不同版本的 .NET 框架构建的组件怎么办.或者您的测试此时无法正常工作,因为您的 Visual Studio 默认使用不同的版本?现在我看到我的 ClassLibrary1 项目默认为 .NET 3.5.如果我将其更改为 4.0,重新构建、复制并再次运行会怎样?
  4. 出现错误 8013101b.这对应(根据谷歌搜索)对应于 COR_E_NEWER_RUNTIME,这也意味着未找到清单中指定的模块".例如,当加载 .NET 2.0 的 EXE 尝试引用使用 .NET 4.0 构建的 DLL 时,就会发生这种情况.所以现在我们必须告诉非托管客户端 EXE 在解析其 COM 引用时加载哪个版本的 .NET 框架.这是通过一个名为 ConsoleApplication1.exe.config 的配置文件完成的.只需创建一个新的文本文件并使用该名称将其保存在 ConsoleApplication1.exe 目录中.它有以下内容:

-

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

如果在这种情况下排除 useLegacyV2RuntimeActivationPolicy,仍然会发生相同的错误.不幸的是,我不完全理解为什么,但我怀疑它与较新的 v4.0 运行时激活策略有关,如果正在加载的可执行文件没有显式引用 .NET 4.0(这不托管代码没有,因为它没有显式引用 .NET 期间).

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. 再次成功!但是等等,还有更多.如果您的 COM dll 使用密钥(具有强名称)签名怎么办?让我们向 ClassLibrary1 添加一个密钥,将其配置为用于在项目的签名"选项卡上对 DLL 进行签名,看看当我们将更新后的 DLL 复制到 ConsoleApplication1.exe 的目录时会发生什么.
  2. 现在我们得到错误 80131040(找到的程序集的清单定义与程序集引用不匹配").并且 sxstrace 和 fuslogvw 在提供有关正在发生的事情的任何信息方面毫无帮助,令人沮丧.幸运的是,我现在知道,在这个特定的无 reg-free-com 场景中,它是由描述 ClassLibrary1(在两个清单文件中)的 assemblyIdentity 元素上缺少 publicKeyToken 属性引起的.但是如何获得 publicKeyToken 值呢?从开发人员命令提示符运行 sn -T ClassLibrary1.dll.更新 ClassLibrary1.manifest 和 ConsoleApplication1.exe.manifest 后,如果嵌入了清单,请记住重建 ConsoleApplication1.exe,并将 ClassLibrary1.manifest 复制到 ConsoleApplication1.exe 目录.再跑一次?
  3. 在 sxstrace 的帮助下,我又经历了几次错误解决,但那是由于愚蠢的错误造成的.为了其他犯愚蠢错误的人的利益,如果您遇到 sxstrace 错误,请注意以下几点: a) 确保您使用的是属性 publicKeyToken 而不是其他一些荒谬的privateKeyToken 之类的名称;b) 确保您在服务器端清单上的 assemblyIdentity 中指定的所有属性都与客户端清单上的属性相匹配,并且您没有在其中指定一个 type="win32" 但没有另一个.
  4. 成功!输出是 B Inserted obby
  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

我还应该注意,VB6 客户端也可以通过使用以下文件与 VB6 客户端一起工作:

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>

但是,在创建配置和清单文件之前构建并运行可执行文件的极少数情况下,似乎确实会报告ActiveX 组件无法创建对象"(运行时错误 429).重建EXE文件似乎可以解决它,但我无法让问题再次出现,因此很难确定具体原因.

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.

我认为我是一个相当出色的问题解决者,但是在无注册的 com 配置问题中报告的大量活动部件和大量无用的错误代码和错误消息使得如果没有一些扎实的经验和内部知识,这几乎不可能解决或微软源代码.希望这个答案能帮助其他人获得类似的经验.如果您了解更多信息,请扩展此答案!

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!

如果您在项目上使用添加"->新项目..."命令添加应用程序清单文件",则可以正确且轻松地嵌入托管 COM 服务器的清单.这会在项目中添加一个名为 app.manifest 的文件.但真正棘手的部分是,它以一种无法通过 Visual Studio UI 以任何其他方式复制的方式实现这一点,除非通过一个棘手的解决方法.由于类库"类型项目的项目设置窗口的应用程序"选项卡上的清单"字段被禁用,因此通常在此处设置的清单无法为类库设置.但是您可以暂时将项目更改为 Windows 应用程序,在此处更改清单选择,然后将其恢复为类库.该设置将保持不变,以便正确嵌入所选清单.您可以通过查看项目文件在文本编辑器中验证设置.寻找:

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>

附录 2

在采取上述所有预防措施后,仍然可能发生错误 0x80131040.为了帮助缩小造成这种情况的范围,它有助于使用融合日志查看器来查看有关加载和解析程序集时发生的情况的更多信息.谷歌Fuslogvw"以获取有关如何查看此日志的更多信息(fuslogvw.exe 是安装 Visual Studio 时提供的实用程序).同样重要的是要认识到,默认情况下,此应用程序显然不会显示任何信息,直到您将其配置为将信息记录到文件中,重现问题,然后在生成日志文件后重新启动应用程序以读取日志文件.而且,根据 MSDN 文档,记住以管理员身份运行此实用程序也很重要.

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.

一旦您克服了运行 fuslogvw.exe 的所有障碍,您可能会在日志中看到如下内容:

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.

尽管 COM 服务器的清单文件将版本列为 1.0.0.0,但这并不是从 COM 客户端引用绑定到服务器时使用的(唯一)版本.我的客户端 EXE 文件试图引用 1.0.0.0,它与 COM 服务器清单文件中的版本完全匹配,但与 DLL 的 .NET 版本不匹配.在更正客户端和服务器清单文件以实际反映 .NET 服务器 DLL 中的版本后,错误 0x80131040 消失了,而 fuslogvw.exe 是确定问题根源的关键.

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.

如果客户端清单与实际 .NET DLL 版本同步,但服务器 DLL 的清单文件未反映此版本,则会出现不同的错误:

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:

应用程序无法启动,因为它并排配置不正确.请查看应用程序事件日志或使用命令行 sxstrace.exe 工具了解更多详细信息.

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.

附录 3

可能会报错0xc0150002或以下信息:

Addendum 3

Error 0xc0150002 or the following message may be reported:

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

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

我看到这种情况发生在客户端清单嵌入非托管 DLL 而不是非托管 EXE 的情况下,并且清单的 assemblyIdentity 元素不完全匹配服务器的 assemblyIdentity.客户端中有一个服务器未指定的额外 processorArchitecture="x86",导致不匹配.不幸的是,如果没有幸运地考虑检查清单文件以查看它们是否匹配(或阅读本文),我不知道如何学习这一点.该错误并未明确指出清单文件是问题的根源,因此您只需注意该错误消息与此原因之间可能存在相关性.

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.

我已经看到外部清单文件被完全忽略,从而产生一个完全空的 sxstrace 日志,即使涉及的可执行文件没有嵌入式清单也是如此.这显然是由于激活上下文缓存而发生的(http://csi-windows.com/blog/all/27-csi-news-general/245-find-out-why-你的外部清单被忽略).要解决此问题,您可以使用以下命令触摸其清单被忽略的文件的日期戳:

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+,,

附录 5

我看到在以下情况下调用 CoCreateInstance 时发生的另一个难以解释的 Class Not Registered 错误(0x80040154 - REGDB_E_CLASSNOTREG):

  1. CPP 文件包含在全局范围内实例化的类的构造函数,因此如果 /clr 开关为 ,动态初始化将在 DllMain 期间调用构造函数不应用于 CPP 文件,或者在 .cctor 期间如果 /clr 开关应用于文件.
  2. DLL 具有嵌入式清单,使其能够引用通过无注册 COM 创建的 COM 类.
  3. COM DLL 在 .NET 2.0 中以托管代码(使用 COM-Callable Wrapper aka CCW)实现.
  4. 加载 DLL 的 EXE没有具有引用创建的 COM 类的无注册清单.
  5. COM DLL regasm 注册.
  6. 调用 CoCreateInstance 的 CPP 文件将 /clr 开关应用于 C++ 编译器设置.
  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.

如果最后 3 个条件中的任何一个被更改,问题就会消失.(此外,如果最后一个条件被更改,您可能会因为 #1 而获得加载程序锁——在 混合程序集的初始化).因此,如果您在类似情况下遇到 Class Not Registered 错误,请考虑是否可以更改最后 3 个条件中的任何一个来解决该错误.注意:我很难确定#6 的行为.似乎切换它的效果也取决于#1的状态.看起来在 DLL 完全加载后调用构造函数(包括其 CoCreateInstance)仍然会导致未注册类,而在 DLL 初始化期间调用构造函数将成功,如果 /clr未指定开关.我目前的解决方案是在托管 C++ 中重新编码客户端 CPP 文件,因为它是 COM 组件和其余非托管代码之间的一个相对简单的接口类.所以现在这个客户端中不再有 COM,只有一个 .NET 引用.

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天全站免登陆