0

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.

Jefraim
  • 193
  • 9

2 Answers2

1

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.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • The ParameterModule isn't a .Net assembly. It is in C++ with CLR enabled to use Xml.Seriliazer. – Jefraim Aug 11 '13 at 14:05
  • That makes no difference, the mixed-mode assembly created by a C++/CLI project is still a .NET assembly with the exact same rules for loading assemblies. The only thing special about it is that it *also* contains native code. Or none at all if you compiled *everything* with /clr in effect. – Hans Passant Aug 11 '13 at 14:08
  • Ok, does the assembly versioning affects the serializing operation? I'm not sure how the .Net interop services is loading the ActiveX. And neither do I know how the DLL module is loaded to the ActiveX since the DLL is dynamically linked, Win32 stuff. The Xml serializing routine probably reloaded the DLL module containing the class operand. Though, this assumption is purely intuitive. – Jefraim Aug 11 '13 at 14:13
  • No, versioning is big deal for binary serialization but not for XML serialization. You are asking questions about code that I cannot see and cannot guess at what it might look like. – Hans Passant Aug 11 '13 at 14:16
  • I updated the query with the code line. Thank you for your insights. the suggestion might the the answer I'm looking for. I'll try it when I get the chance. – Jefraim Aug 11 '13 at 14:34
  • The suggestion didn't work. :( And all the module dependencies resides in a single directory I'm sure of that. – Jefraim Aug 13 '13 at 10:33
  • It doesn't have anything to do with directories, everything to do with how the assembly gets loaded. If you have no clue how this assembly could be loaded without a loading context then use a program like SysInternals' ProcMon. Single step your code and you'll see the CLR accessing the DLL. – Hans Passant Aug 13 '13 at 10:45
0

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.

Jefraim
  • 193
  • 9