This may well be my last blog post on .NET Remoting. I am not sure how many applications out there still using .NET Remoting, but the current trend is to migrate from .NET Remoting to WCF. Eventually, we will, but in the meantime, the product I am working on will still relay on .NET Remoting.
Yesterday, I updated all the VS project target framework from .NET 3.5 to .NET 4.0. Usually migrating from one version of .NET framework to a newer one should be fairly simple. There are some difference between .NET 3.5 to .NET 4.0, for example MS removed the CAS and promote the use of Security Transparent Code. It also introduces new classes like Tuple, which is useful for represent data from a data row.
After changing all the target frameworks and rebuild the solution, everything should works just like before. Started my server and client app, boom…. System.TypeInitializationException!!!
Exception
Exception: "The type initializer for 'ME.APTS.ServiceInterface.DataAccess.DataAccessBase`1' threw an exception."
Inner Exception: System.Security.SecurityException {"Request failed."}
Stack Trace:
Server stack trace:
at System.Runtime.Serialization.FormatterServices.nativeGetSafeUninitializedObject(RuntimeType type)
at System.Runtime.Serialization.FormatterServices.GetSafeUninitializedObject(Type type)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObject(ParseRecord pr)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Parse(ParseRecord pr)
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.CoreChannel.DeserializeBinaryRequestMessage(String objectUri, Stream inputStream, Boolean bStrictBinding, TypeFilterLevel securityLevel)
at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
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 ME.APTS.ServiceInterface.IBroadcastListener.Process(ArrayList broadcastQueueArrayList)
at ME.APTS.Service.Provider.Remoting.BroadcastQueueRemoting.Process(ArrayList listOfObjectsToProcess) in C:\dev\Streets\Main\APTS\ME.APTS.Service.Provider\Remoting\BroadcastQueueRemoting.cs:line 119
This exception is being throw by my client app, when my server app tries to do a remoting call on the client application. The server when to send a piece of data to the client, and it needs to create an object, but apparently the server has no permission (that’s what I assumed) to create object on the client side.
The Confusion
After spending hours debugging the problem, it became even more confusing when some client application works and some aren’t. These client applications run the same piece of code on the same machine, and yet only some of them work.
Trying to confuse myself more, I tried to look for security attributes setting that we may have used. Nothing turns up. Since we didn’t change anything in the code and not even the application configuration files, it must be changes in the .NET framework.
Debugging
After hours of frustration, my brain was too tired to think. I asked my colleague to look into this issue together. After a couple of testing, we realized we forget to check the client configuration file. We don’t use App.config, but our setting file is in a very similar format. After doing some configuration files comparison, BINGO!
Solution
It all comes down to a missing .NET Remoting setting, which is highlighted below.
<configuration> <system.runtime.remoting> <customErrors mode="off" /> <debug loadTypes="true" /> <application > <service> <wellknown mode="SingleCall" objectUri="BroadcastListener.rem" type="ME.APTS.Client.Model.BroadcastListener, ME.APTS.Client.Model" /> </service> <channels> <channel ref="tcp" port="9999"> <serverProviders> <formatter ref="binary" typeFilterLevel="Full" /> <provider type="MyCompany.SecurityServerChannelSinkProvider, MyCompany.ServiceInterface" securityPackage="NTLM" impersonationLevel="impersonate" authenticationLevel="call" logging="true"/> </serverProviders> </channel> </channels> </application> </system.runtime.remoting> <appSettings>
After specifying the typeFilterLevel attribute to Full, the app works again. Here is the strange part, why all of the other working application has a specified typeFilterLevel, and not the one that I was debugging. I checked the existing release build, which runs on .NET 3.5, and it doesn’t have this setting either.
Conclusion
The only conclusion I can draw from this is that a change in .NET 4.0 causing this issue. By default, the typeFilterLevel is set to low. The application that I was testing must have been using the low setting. From the typeFilterLevel documentation, it isn’t clear when you should use typeFilterLevel.Low or typeFilterLevel.Full.
Low: The low deserialization level for .NET Framework remoting. It supports types associated with basic remoting functionality.
Full: The full deserialization level for .NET Framework remoting. It supports all types that remoting supports in all situations.
The changes in .NET Remoting security cause my application to use typeFilterLevel to full. It maybe related to the changes of the CAS. It would be interesting to see what exactly has changed, but there is no time for it. Soon our application will migrate to WCF and .NET Remoting will become the past.