CLSIDFromProgID在免注册COM并行程序集场景下返回错误CLSID问题
问题背景
我们有一个包含多个COM类与对象的.NET DLL,可供合作方使用,此前一直使用regasm在注册表中注册COM类,多年来运行稳定。
现在我们计划取消COM注册,改用并行程序集(side by side assemblies)实现免注册COM。目前迁移进入收尾阶段却遇到问题:我们提供的使用上述COM类的C++示例应用,无法在免注册模式下正常运行。
.NET DLL代码
我们的.NET DLL中定义的类示例如下:
namespace MyNamespace { [ComVisible(true)] [Guid("ef828ade-b459-4446-80db-956715588601")] [ClassInterface(ClassInterfaceType.None)] [ProgId("MyVendor.MyClass")] public partial class MyClass { } }
应用程序集清单
适配该COM类的C++示例应用配置的清单如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="x86" publicKeyToken="6595b64144ccf1df" language="*" /> </dependentAssembly> </dependency> <dependency> <dependentAssembly> <assemblyIdentity type="win32" name="MyComDll.X" version="1.0.0.0" /> </dependentAssembly> </dependency> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> <security> <requestedPrivileges> <requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel> </requestedPrivileges> </security> </trustInfo> <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware> </windowsSettings> </application> </assembly>
DLL程序集清单
.NET DLL自身配置了对外暴露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="MyComDll.X" version="1.0.0.0" /> <file name="MyComDll.dll"> <comClass description="MyVendor.MyClass" clsid="{ef828ade-b459-4446-80db-956715588601}" progid="MyVendor.MyClass.1" threadingModel="Both"> <progid>MyVendor.MyClass</progid> </comClass> </file> </assembly>
COM调用代码
以下为C++示例代码中获取CLSID并创建类实例的逻辑(示例中使用代称,实际已确认所有命名均合法有效):
HRESULT hr = CLSIDFromProgID(OLESTR("MyVendor.MyClass"), &clsid); hr = CoCreateInstance(clsid, nullptr, CLSCTX_SERVER, IID_IDispatch, (void**)(&_pMyClass));
问题现象
通过调试输出查看CLSIDFromProgID返回的GUID与返回值:使用regasm注册COM类时,返回值为S_OK,clsid与.NET DLL中定义的类GUID完全一致,运行正常。
但取消COM注册、改用上述清单配置的并行程序集后,CLSIDFromProgID同样返回S_OK,但得到的GUID与预期完全不符,导致无法创建类实例。该异常GUID既不存在于注册表中,也不在开发项目的任何位置。
已排查项如下:
- C++示例应用本身逻辑无误,COM类正常注册时可完美运行;
- 所有清单内容均对照微软官方规范与多个教程核对,配置符合要求;
- Windows事件日志中无SideBySide相关报错,确认清单格式合法;
- CLSIDFromProgID返回S_OK,说明ProgID可被正常识别,仅返回的CLSID错误;
- 已通过StringFromCLSID()函数对比,确认CLSIDFromProgID返回的CLSID与预期不符。
问题原因与解决方案
该问题属于.NET程序集免注册COM场景下的典型配置遗漏问题,核心原因是DLL的程序集清单中缺少了CLR运行时绑定与COM类到.NET类型的映射信息,系统无法将ProgID匹配到声明的真实CLSID,因此返回了CLR自动生成的临时代理类CLSID。
具体需要修改的配置项如下:
- 给.NET DLL的程序集清单添加clrClass节点
免注册COM场景下,.NET COM类不能仅声明comClass节点,必须额外添加clrClass节点建立.NET类型和COM标识的映射,修改后的DLL清单file节点部分如下:
<file name="MyComDll.dll"> <comClass description="MyVendor.MyClass" clsid="{ef828ade-b459-4446-80db-956715588601}" progid="MyVendor.MyClass.1" threadingModel="Both"> <progid>MyVendor.MyClass</progid> </comClass> <!-- 新增clrClass节点,指定.NET类型信息 --> <clrClass clsid="{ef828ade-b459-4446-80db-956715588601}" progid="MyVendor.MyClass" name="MyNamespace.MyClass" runtimeVersion="v4.0.30319"/> </file>
注意将runtimeVersion替换为实际的.NET Framework运行时版本。
确认程序集的强名称签名配置
如果.NET DLL使用了强名称签名,需要在DLL清单的assemblyIdentity节点补充publicKeyToken属性,同时应用程序清单的dependentAssembly节点也要对应补充相同的publicKeyToken,确保程序集绑定正确。检查清单嵌入配置
确认.NET DLL的清单是作为Win32资源嵌入到DLL文件中,且资源ID为2;C++应用的清单资源ID为1,避免系统读取到错误的清单文件。
以上修改完成后重新编译项目即可恢复正常。
内容的提问来源于stack exchange,提问作者GameDevAlien

