C# .Net 4.0 应用程序中托管的 C++ ActiveX 控件中的 Xml.Serializer 非法强制转换异常

Posted

技术标签:

【中文标题】C# .Net 4.0 应用程序中托管的 C++ ActiveX 控件中的 Xml.Serializer 非法强制转换异常【英文标题】:Xml.Serializer illegal cast exception in C++ ActiveX control hosted in C# .Net 4.0 Application 【发布时间】:2013-08-11 13:42:23 【问题描述】:

我有一个 C# .Net 4.0 应用程序,它使用启用了 CLR 的 C++ DLL 托管 C++ ActiveX 控件。 DLL 的主要功能是为 OCX 加载参数,并为此使用 XML.Serializer。

当所有组件都在 MS Visual Studio .Net 2003 中构建并且 C# 应用程序在 .Net 1.1 中运行时,此堆栈工作正常。

但是,当整个模块迁移到 VS2010 并将应用程序迁移到 .Net 4.0 时,由于上下文不匹配,我得到了可怕的 Xml.Serializer 非法强制转换异常。

异常发生在第4行:

FileStream* fs = __gcnew FileStream( filename, FileMode::Open );
XmlReader* reader = __gcnew XmlTextReader( fs );
XmlSerializer* serializer = __gcnew XmlSerializer( __typeof(MyClass) );
MyClass* obj = __try_cast<MyClass*>(serializer->Deserialize(reader)); 

这里是异常声明:

[A]MyClass cannot be cast to [B]MyClass.
Type A originates from 'ParameterModule, Version=0.0.0.0, Culture=neutral,      PublicKeyToken=null' in the context 'Default'
at location 'C:\path\to\module\ParameterModule.dll'.
Type B originates from 'ParameterModule, Version=0.0.0.0, Culture=neutral,  PublicKeyToken=null' in the context 'LoadNeither'
at location 'C:\path\to\modu~\ParameterModule.dll'. 

在 ParameterModule.ParaClass.execute_DeSerialize() 抛出异常。

请注意,“LoadNeither”上下文的位置路径带有 波浪号 (~) 字符。 “默认”上下文具有完整路径。

ActiveX 控件的互操作 DLL 由 VS2010 自动生成。

我想知道是什么导致了异常。 是路径不匹配吗?我不确定,但我认为 DLL 只加载了一次。

还是上下文不匹配? 如果是因为上下文不匹配,我们如何确保像 C++ ActiveX 控件这样的互操作模块的加载上下文? 或者,我们可以指定 Xml.Serializer 在默认上下文中加载包含序列化类的 DLL 吗?

我到处寻找,但找不到解决方案。我在互联网上梳理得越多,这对我来说就越是一个谜。提前致谢。

【问题讨论】:

【参考方案1】:

但我认为 DLL 只加载了一次

不,它被加载了两次。这就是问题所在,.NET 类型的标识不仅仅是名称空间 + 类型名称,它还包括从中加载它的程序集。这是一个 DLL Hell 对策,它确保您不能多次从具有冲突定义的不同 DLL 加载相同的类型。

“LoadNeither”上下文提示您的问题。您以某种不寻常的方式加载此程序集。这样做的常用方法是使用 Assembly.LoadFile(),这是一种非常危险的方法,只应在您有意不希望类型匹配的非常特殊的情况下使用。您应该始终使用 LoadFrom() 代替,但如果可以的话,您应该更喜欢 Load()。您通常可以通过将 DLL 放在正确的目录中或使用 app.exe.config 文件中的 &lt;probing&gt; 元素来实现。

顺便说一句,获取版本 0.0.0.0 也不是很健康,[AssemblyVersion] 在 .NET 中非常重要。

【讨论】:

ParameterModule 不是 .Net 程序集。它在 C++ 中,启用了 CLR 以使用 Xml.Seriliazer。 这没什么区别,由 C++/CLI 项目创建的混合模式程序集仍然是具有完全相同的加载程序集规则的 .NET 程序集。它唯一的特别之处在于它包含本机代码。或者,如果你编译了 everything 并且 /clr 生效。 好的,程序集版本控制会影响序列化操作吗?我不确定 .Net 互操作服务如何加载 ActiveX。而且我也不知道 DLL 模块是如何加载到 ActiveX 的,因为 DLL 是动态链接的,Win32 的东西。 Xml 序列化例程可能重新加载了包含类操作数的 DLL 模块。不过,这个假设纯粹是直观的。 不,版本控制对于二进制序列化很重要,但对于 XML 序列化则不是。你在问一些我看不到也猜不到的代码问题。 我用代码行更新了查询。感谢您的见解。 建议可能是我正在寻找的答案。有机会我会试试的。【参考方案2】:

很奇怪,但是我们使用static_cast的时候并没有出现异常

MyClass* obj = static_cast<MyClass*>(serializer->Deserialize(reader)); 

虽然这个答案并不能真正解决模块被加载两次的问题,但这种解决方法可能会对那里的人有所帮助。

【讨论】:

以上是关于C# .Net 4.0 应用程序中托管的 C++ ActiveX 控件中的 Xml.Serializer 非法强制转换异常的主要内容,如果未能解决你的问题,请参考以下文章

如何将托管 c++ 库导入 C# 应用程序(目标 .net 版本 3.5)

非托管C++通过C++/CLI包装调用C# DLL

在 SqlCompact 的 4.0 应用程序中托管 Visual C++ 2005 库 DLL

调试从非托管 C++ 调用的托管 .NET 代码

从非托管 c++ 调用托管 c# 函数

从非托管 c++ 调用托管 c# 函数