Blu*_*kMN 9 c# c++ vb6 com regfreecom
我开始使用一个非常复杂的客户端和服务器系统,带有COM引用和其他东西,我一直在减少,直到我意识到我甚至无法获得Microsoft示例代码来管理受管COM服务器的免注册COM激活用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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
客户端VB6代码:
Dim c As ClassLibrary1.Class1
Set c = New Class1
MsgBox c.DummyFunction("Ben")
Run Code Online (Sandbox Code Playgroud)
客户端C++代码:
#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;
}
Run Code Online (Sandbox Code Playgroud)
当我删除所有Reg-Free COM代码并使用regasm/codebase注册ClassLibrary1.dll时,两个客户端都正常工作.
然后我取消注册ClassLibrary1,并尝试使用文件Project1.exe.manifest为VB6客户端引入Reg-Free COM:
<?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>
Run Code Online (Sandbox Code Playgroud)
和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>
Run Code Online (Sandbox Code Playgroud)
现在我有时会得到错误429(ActiveX组件无法创建对象),并且(莫名其妙地)有时会出现自动化错误:
运行时错误'-2146234304(80131040)':自动化错误
现在,当我运行C++客户端时,输出仅仅是
错误800401f9
Blu*_*kMN 12
经过Microsoft支持的各种样本的多次试验后,我发现了在尝试使用非托管C++ COM客户端实现托管COM服务器时出现的许多缺陷.以下是我记得的关键信息,可以应用于问题中的示例代码以确保其有效.

-
#define RT_MANIFEST 24
#define MANIFEST_RESOURCE_ID 1
MANIFEST_RESOURCE_ID RT_MANIFEST ClassLibrary1.manifest
Run Code Online (Sandbox Code Playgroud)
Then add a pre-build step like this:
"C:\Program Files (x86)\Windows Kits\8.1\bin\x86\rc.exe" "$(ProjectDir)ClassLibrary1.rc"
Run Code Online (Sandbox Code Playgroud)
然后在项目设置"应用程序"选项卡中,更改"资源"以使用ClassLibrary1.res而不是"Icon and manifest".但是这带来了一些问题:首先,如果不对其进行硬编码,RC.EXE的路径就不容易定义; 其次,AssemblyInfoCommon中的版本信息将被忽略,因为RC文件中的资源完全取代了.NET编译器生成的所有Win32资源.
另一种可能性是简单地将服务器COM DLL清单文件保持独立,而不是将其作为资源嵌入.我已经读过这可能不太可靠,但它可以在Windows 7 Enterprise 64位SP1上运行.
ConsoleApplication1.exe.config定义如何加载.NET 的配置文件().对于.NET 4.5,我看到了这项工作:-
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>
Run Code Online (Sandbox Code Playgroud)
While for .NET 3.5, it seems useLegacyV2RuntimeActivationPolicy needs to be switched:
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="false">
<supportedRuntime version="v3.5"/>
</startup>
</configuration>
Run Code Online (Sandbox Code Playgroud)
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".
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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
EDIT:
Now to go through and validate every detail with full error message info etc.
我首先创建一个包含两个项目的解决方案,其中包含所有默认值和问题中显示的代码.我首先没有明确的文件,也没有问题中提到的任何项目设置,并且在我在下面的过程中进行这些更改时会明确地调出.这些是使该项目工作的步骤和错误.
tlbexp ClassLibrary1.dll#import <ClassLibrary1.tlb> raw_interfaces_only用引号替换尖括号,使其读取#import "ClassLibrary1.tlb" raw_interfaces_only.重建:成功.Error 80040154(Class未注册)因为我们没有注册组件也没有设置免注册COM.Error 800401f9我们将跳过它并尝试创建客户端清单.创建包含以下内容的新文本文件,并将其另存为ConsoleApplication1.exe.manifest在ConsoleApplication1项目目录中:-
<?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>
Run Code Online (Sandbox Code Playgroud)
-
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.
Run Code Online (Sandbox Code Playgroud)
-
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>
Run Code Online (Sandbox Code Playgroud)
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).
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?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.B Inserted obbyI 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>
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
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!
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>
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)
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.
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.
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+,,
Run Code Online (Sandbox Code Playgroud)
I have seen another hard-to-explain Class Not Registered error (0x80040154 - REGDB_E_CLASSNOTREG) that occurs when calling CoCreateInstance under the following conditions:
DllMain if the /clr switch is not applied to the CPP file, or during .cctor if the /clr switch is applied to the file.regasm.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.
| 归档时间: |
|
| 查看次数: |
1791 次 |
| 最近记录: |