Xml.Serializer illegal cast exception in C++ ActiveX control hosted in C# .Net 4.0 Application

188 views Asked by At

I have a C# .Net 4.0 Application hosting a C++ ActiveX control utilizing a C++ DLL with CLR enabled. The DLL has the main function of loading the parameters for the OCX and uses XML.Serializer for this purpose.

This stack is working fine when all components are built in MS Visual Studio .Net 2003 and the C# Application running in .Net 1.1.

However, when the entire modules are migrated to VS2010 and the Application to .Net 4.0, I get the dreaded Xml.Serializer illegal cast exception because of the mismatched context.

The exception occurs at the 4th line:

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

Here's the exception statement:

[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'. 

at ParameterModule.ParaClass.execute_DeSerialize() Exception was thrown.

Please note that the 'LoadNeither' context has location path with the tilde (~) character. The 'Default' context has the full path.

The interop DLL for the ActiveX control is automatically generated by VS2010.

I wonder what causes the exception. Is it the mismatch in Path? I'm not sure, but I think the DLL was loaded only once.

Or is it the mismatch in context? If it is because of the context mismatch, how do we make sure the loading context for Interop modules like the C++ ActiveX controls? Or, can we specify Xml.Serializer to load the DLL containing the Serializing classes in the Default context?

I've looked everywhere and I could not find the solution. The more I comb the internet, the more this become a mystery to me. Thanks in advance.

2

There are 2 answers

0
Jefraim On BEST ANSWER

It's odd, but the exception didn't occur when we used static_cast

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

Though this answer does not really solve the problem of the module being loaded twice, this workaround might help somebody out there.

7
Hans Passant On

but I think the DLL was loaded only once

No, it got loaded twice. And that's the problem, the identity of a .NET type is not just the namespace + type name, it also includes the assembly it got loaded from. It is a DLL Hell countermeasure, it ensures that you cannot have the same type loaded more than once from different DLLs with a conflicting definition.

The "LoadNeither" context is the cue to your problem. You are somehow loading this assembly in a unusual way. The common way to do so is by using Assembly.LoadFile(), a very dangerous method that should only be used in very special cases where you intentionally don't want types to match. You should always use LoadFrom() instead but really favor Load() when you can. And you usually can by putting the DLL in the right directory or by using the <probing> element in the app.exe.config file.

Getting version 0.0.0.0 isn't very healthy either btw, the [AssemblyVersion] is a very big deal in .NET.