Sunday, May 1, 2011

How can I force unhandled exceptions in a WCF thread to crash the process?

So here's the scenario:

I have a WCF service that has a bunch of FaultContracts defined per operation. I would like to arrange it such that if an unhandled exception is thrown in a WCF service thread that does not match a valid FaultContract, it takes down the entire process rather than just the thread. (The reason being that I would like a crash dump that contains the information on the exception, since it didn't match the contract.)

Is there any way to do this cleanly? The main problem I have is that WCF wants to translate all my exceptions into a client-side fault in order to keep the service running; I actually want to take the entire process down, which essentially means circumventing WCF's normal behavior.

From stackoverflow
  • Application.Exit();
    

    That might do it, but the user will lose anything they're working on at the time.

  • Environment.FailFast() will create a crash dump; it will not run any pending try-finally blocks nor will it run any finalizers.

  • You need to use IErrorHandler to customize WCF's error handling behavior. You "apply the behavior" before you call (ServiceHost).Open().

    For example (look for the line that says "serviceHost.Description.Behaviors.Add(new FailBehavior());" in Main()):

    class FailBehavior : IServiceBehavior
    {
        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
        {
            return;
        }
    
        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            Console.WriteLine("The FailFast behavior has been applied.");
            var f = new FailOnError();
            foreach(ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
            {
                chanDisp.ErrorHandlers.Add(f);      
            }
        }
    
        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            return;
        }
    }
    
    class FailOnError : IErrorHandler
    {
        public bool HandleError(Exception error)
        {
            // this is called for every exception -- even ungraceful disconnects
            if( !(error is CommunicationException) )
                throw new TargetInvocationException( "WCF operation failed.", error );
            else
                throw new CommunicationException( "Unexpected communication problem. (see inner exception)", error );
    
            // Unreachable
            //return false; // other handlers should be called
        }
    
        public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
        {
            // Can't throw from here (it will be swallowed), and using Environment.FailFast
            // will result in all crashes going to the same WER bucket. We could create
            // another thread and throw on that, but instead I think throwing from HandleError
            // should work.
    
            //Console.WriteLine( "Unhandled exception: {0}", error );
            //Environment.FailFast("Unhandled exception thrown -- killing server");            
        }
    }
    
    class Program
    {
        static void Main( string[] args )
        {
            Console.WriteLine( "Greetings from the server." );
    
            Uri uri = new Uri( "net.tcp://localhost:5678/ServerThatShouldCrash" );
            using( ServiceHost serviceHost = new ServiceHost( typeof( Server ), uri ) )
            {
                Binding binding = _CreateBinding();
    
                serviceHost.AddServiceEndpoint( typeof( IServer ),
                                                binding,
                                                uri );
                serviceHost.Description.Behaviors.Add(new FailBehavior());
                serviceHost.Open();
    
                // The service can now be accessed.
                Console.WriteLine( "The service is ready." );
                Console.WriteLine( "\nPress <ENTER> to terminate service.\n" );
                Console.ReadLine();
            }
        }
    
        private static Binding _CreateBinding()
        {
            NetTcpBinding netTcp = new NetTcpBinding( SecurityMode.None );
            netTcp.ReceiveTimeout = TimeSpan.MaxValue;
            netTcp.ReliableSession.InactivityTimeout = TimeSpan.MaxValue;
            return netTcp;
        } // end _CreateBinding()
    }
    

0 comments:

Post a Comment

Note: Only a member of this blog may post a comment.