The type initializer for '<Module>' threw an exception
This post describes a rather obscure problem, and its solution, that occurred when we upgraded to ImageVault 3.5.4 with .NET 4.0 and EPiServer CMS 6 R2.
For once, I don’t quite understand all that happens, or rather why, but I’m hoping someone better versed than me can comment on it. Also since we did fix the issue, it may help others with similar problems and not necessarily tied to EPiServer or ImageVault. LeadTools is likely the key component here, which is used by ImageVault, but the situation as such is pretty generic and may pop up in any number of situations where cross AppDomain calls occur.
For readability, I’m showing the actual stack trace at the bottom of this post.
In brief, what seems to happen is that an AppDomain transition is indirectly caused by the call to ImageVault.ImageConverter(). I have not had the time to figure just what code is running in the other AppDomain, or why it needs to. But, the problem is, that when a cross AppDomain call is made, objects attached to the thread needs to be marshaled or serialized across. This is typically done by serialization. The fault we’re seeing is caused by the receiving AppDomain needing a reference to all types needed for deserialization, and it might not have access to the ASP.NET bin folder. This is the case here.
We’re using a custom IPrincipal object, here called Axantum.Identity, which obviously is attached to the thread, and thus gets serialized. But it can’t be deserialized in the other AppDomain, because there it can’t get at our custom type implementing IPrincipal there.
The solution is either to place the assemblies containing the types that needs to get serialized into the GAC, or to use marshaling by having the type in question derive from MarshalByRefObject . We wrapped our IPrincipal implementation inside another decorator that did derive from MarshalByRefObject as well as changed some component types to derive from MarshalByRefObject and problem was solved. We choose not to install into the GAC although that should work as well. I’m not entirely sure about any caveats or performance issues caused by using marshaling by reference instead of serialization – comments appreciated.
Here’s the stack trace, for reference:
ImageStoreNET.WS.Ajax.AjaxEditImageService.GetImageByName - Error converting image (ImageStoreNET.WS.Ajax.AjaxEditImageService) System.TypeInitializationException: The type initializer for '<Module>' threw an exception. ---> System.TypeInitializationException: The type initializer for '<Module>' threw an exception. ---> <CrtImplementationDetails>.ModuleLoadException: The C++ module failed to load while attempting to initialize the default appdomain.
---> System.Runtime.Serialization.SerializationException: Unable to find assembly 'Axantum.Identity, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'.
Server stack trace:
at System.Runtime.Serialization.Formatters.Binary.BinaryAssemblyInfo.GetAssembly()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.GetType(BinaryAssemblyInfo assemblyInfo, String name)
at System.Runtime.Serialization.Formatters.Binary.ObjectMap..ctor(String objectName, String[] memberNames, BinaryTypeEnum[] binaryTypeEnumA, Object[] typeInformationA, Int32[] memberAssemIds, ObjectReader objectReader, Int32 objectId, BinaryAssemblyInfo assemblyInfo, SizedArray assemIdToAssemblyTable)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryObjectWithMapTyped record)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadObjectWithMapTyped(BinaryHeaderEnum binaryHeaderEnum)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Remoting.Channels.CrossAppDomainSerializer.DeserializeObject(MemoryStream stm)
at System.Runtime.Remoting.Messaging.SmuggledMethodCallMessage.FixupForNewAppDomain()
at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoDispatch(Byte[] reqStmBuff, SmuggledMethodCallMessage smuggledMcm, SmuggledMethodReturnMessage& smuggledMrm)
at System.Runtime.Remoting.Channels.CrossAppDomainSink.DoTransitionDispatchCallback(Object[] args)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.AppDomain.get_Id()
at <CrtImplementationDetails>.DoCallBackInDefaultDomain(IntPtr function, Void* cookie)
at <CrtImplementationDetails>.LanguageSupport.InitializeDefaultAppDomain(LanguageSupport* )
at <CrtImplementationDetails>.LanguageSupport._Initialize(LanguageSupport* )
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
--- End of inner exception stack trace ---
at <CrtImplementationDetails>.ThrowModuleLoadException(String errorMessage, Exception innerException)
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
at .cctor()
--- End of inner exception stack trace ---
at <CrtImplementationDetails>.ThrowModuleLoadException(String , Exception )
at <CrtImplementationDetails>.LanguageSupport.Initialize(LanguageSupport* )
at .cctor()
--- End of inner exception stack trace ---
at ImageConverterService.ImageConverter..ctor()
at ImageConverterService.ImageConverterService.LoadDataObjectFormatInformation(String fullFilename)
at ImageStoreNET.Classes.Data.DataFactory.LoadDataObjectFormatInformation(FileInfo file)
at ImageStoreNET.Classes.Data.DataObjectFormatInformation.Load(FileInfo file)
at ImageStoreNET.Classes.Data.DataFactory.GetImageInformation(FileInfo file)
at ImageStoreNET.Classes.Util.ImageUtil.GetImageInformation(String filename, Boolean temp)
at ImageStoreNET.WS.Ajax.AjaxEditImageService.GetImageInfo(String filename)
at ImageStoreNET.WS.Ajax.AjaxEditImageService.GetImageByName(String filename, Boolean isUndo, String clientSideUndoScript, Int32 applicationMode2, Int32 dataObjectID)
The marshaling in this case occurs since Leadtools is an unmanaged dll (dotnet C++ dll). Managed an unmanaged code cannot share memory so to communicate between those worlds we need to transfer parameters, return values and other data to the other side. This is what causes the cross app domain calls and the marshaling of your IPrincipal.
Regarding your question about serializing your data or using MarshalByRefObject I would think (I'm no expert in this field, I'm only guessing) that if the object don't get accessed much (properties/methods) then the MarshalByRefObject would be the fastest since you don't have to serialize/deserialize the object. If communication with the object is large (which it probably isn't in this case) then the serialization way would perhaps be fastest. To facilitate the communication between the appdomains, proxy classes are used and each call takes some amount of time.
All depends on the type of object and what communication that needs to take place.
As you stated, this is not a Leadtools specific problem, it applies to all calls between unmanaged and managed (or COM) code.
For more reading on the subject, the following articles can be a good start.
An Overview of Managed/Unmanaged Code Interoperability
http://msdn.microsoft.com/en-us/library/ms973872.aspx
Marshaling between Managed and Unmanaged Code
http://msdn.microsoft.com/en-us/magazine/cc164193.aspx
Thanks!
When you say it, it's of course obvious that unmanaged code must run separately. I was under the impression after a quick peek that Leadtools was C++/CLI, and without being any kind of expert I thought that transition would be benign and do run in the same AppDomain.
I doubt that the IPrincipal instance is ever really used after the transition and if it is it's not large, so I'll stick with the MarshalByRefObject solution until further notice.
Anyway, it's an important lesson to learn, and thanks for the clarification about Managed/Unmanaged interop.