File: System\ServiceModel\ComIntegration\TypedServiceChannelBuilder.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//-----------------------------------------------------------------------------
#pragma warning disable 1634, 1691
 
namespace System.ServiceModel.ComIntegration
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime;
    using System.Runtime.InteropServices;
    using System.Runtime.Remoting;
    using System.Runtime.Remoting.Proxies;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.ServiceModel.Description;
    using System.ServiceModel.Diagnostics;
    using System.Threading;
 
    class TypedServiceChannelBuilder : IProxyCreator, IProvideChannelBuilderSettings, ICreateServiceChannel
    {
 
        ServiceChannelFactory serviceChannelFactory = null;
        Type contractType = null;
 
        // Double-checked locking pattern requires volatile for read/write synchronization
        volatile RealProxy serviceProxy = null;
        ServiceEndpoint serviceEndpoint = null;
        KeyedByTypeCollection<IEndpointBehavior> behaviors = new KeyedByTypeCollection<IEndpointBehavior>();
        Binding binding = null;
        string configurationName = null;
        string address = null;
        EndpointIdentity identity = null;
 
        void IDisposable.Dispose()
        {
            if (serviceProxy != null)
            {
                IChannel channel = serviceProxy.GetTransparentProxy() as IChannel;
                if (channel == null)
                {
                    throw Fx.AssertAndThrow("serviceProxy MUST support IChannel");
                }
                channel.Close();
            }
        }
 
        //Suppressing PreSharp warning that property get methods should not throw
#pragma warning disable 6503
        ServiceChannelFactory IProvideChannelBuilderSettings.ServiceChannelFactoryReadWrite
        {
            get
            {
                if (serviceProxy != null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new COMException(SR.GetString(SR.TooLate), HR.RPC_E_TOO_LATE));
                return serviceChannelFactory;
            }
        }
#pragma warning restore 6503
        ServiceChannelFactory IProvideChannelBuilderSettings.ServiceChannelFactoryReadOnly
        {
            get
            {
                return serviceChannelFactory;
            }
        }
        //Suppressing PreSharp warning that property get methods should not throw
#pragma warning disable 6503
        KeyedByTypeCollection<IEndpointBehavior> IProvideChannelBuilderSettings.Behaviors
        {
            get
            {
                if (serviceProxy != null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new COMException(SR.GetString(SR.TooLate), HR.RPC_E_TOO_LATE));
                return behaviors;
            }
        }
#pragma warning restore 6503
 
        ServiceChannel IProvideChannelBuilderSettings.ServiceChannel
        {
            get
            {
                return null;
            }
        }
 
        RealProxy ICreateServiceChannel.CreateChannel()
        {
            if (serviceProxy == null)
            {
                lock (this)
                {
                    if (serviceProxy == null)
                    {
                        try
                        {
                            if (serviceChannelFactory == null)
                            {
                                FaultInserviceChannelFactory();
                            }
 
                            if (serviceChannelFactory == null)
                            {
                                throw Fx.AssertAndThrow("ServiceChannelFactory cannot be null at this point");
                            }
 
                            serviceChannelFactory.Open();
 
                            if (contractType == null)
                            {
                                throw Fx.AssertAndThrow("contractType cannot be null");
                            }
                            if (serviceEndpoint == null)
                            {
                                throw Fx.AssertAndThrow("serviceEndpoint cannot be null");
                            }
 
                            object transparentProxy = serviceChannelFactory.CreateChannel(contractType, new EndpointAddress(serviceEndpoint.Address.Uri, serviceEndpoint.Address.Identity, serviceEndpoint.Address.Headers), serviceEndpoint.Address.Uri);
 
                            ComPlusChannelCreatedTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationChannelCreated,
                                SR.TraceCodeComIntegrationChannelCreated, serviceEndpoint.Address.Uri, contractType);
 
                            RealProxy localProxy = RemotingServices.GetRealProxy(transparentProxy);
 
                            serviceProxy = localProxy;
 
                            if (serviceProxy == null)
                            {
                                throw Fx.AssertAndThrow("serviceProxy MUST derive from RealProxy");
                            }
                        }
                        finally
                        {
                            if ((serviceProxy == null) && (serviceChannelFactory != null))
                                serviceChannelFactory.Close();
                        }
                    }
                }
            }
            return serviceProxy;
        }
 
        private ServiceEndpoint CreateServiceEndpoint()
        {
            TypeLoader loader = new TypeLoader();
            ContractDescription contractDescription = loader.LoadContractDescription(contractType);
 
            ServiceEndpoint endpoint = new ServiceEndpoint(contractDescription);
            if (address != null)
                endpoint.Address = new EndpointAddress(new Uri(address), identity);
            if (binding != null)
                endpoint.Binding = binding;
 
            if (configurationName != null)
            {
                ConfigLoader configLoader = new ConfigLoader();
                configLoader.LoadChannelBehaviors(endpoint, configurationName);
            }
 
            ComPlusTypedChannelBuilderTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationTypedChannelBuilderLoaded,
                SR.TraceCodeComIntegrationTypedChannelBuilderLoaded, contractType, binding);
 
            return endpoint;
        }
 
        private ServiceChannelFactory CreateServiceChannelFactory()
        {
            ServiceChannelFactory serviceChannelFactory = ServiceChannelFactory.BuildChannelFactory(serviceEndpoint) as ServiceChannelFactory;
            if (serviceChannelFactory == null)
            {
                throw Fx.AssertAndThrow("We should get a ServiceChannelFactory back");
            }
            return serviceChannelFactory;
        }
 
        void FaultInserviceChannelFactory()
        {
            if (contractType == null)
            {
                throw Fx.AssertAndThrow("contractType should not be null");
            }
            if (serviceEndpoint == null)
            {
                serviceEndpoint = CreateServiceEndpoint();
            }
            foreach (IEndpointBehavior behavior in behaviors)
                serviceEndpoint.Behaviors.Add(behavior);
            serviceChannelFactory = CreateServiceChannelFactory();
 
        }
 
        internal void ResolveTypeIfPossible(Dictionary<MonikerHelper.MonikerAttribute, string> propertyTable)
        {
            string typeIID;
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Contract, out typeIID);
            Guid iid;
            if (!string.IsNullOrEmpty(typeIID))
            {
                try
                {
                    dispatchEnabled = true;
                    iid = new Guid(typeIID);
                    TypeCacheManager.Provider.FindOrCreateType(iid, out contractType, true, false);
                    serviceEndpoint = CreateServiceEndpoint();
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.TypeLoadForContractTypeIIDFailedWith, typeIID, e.Message)));
                }
 
            }
        }
 
        internal TypedServiceChannelBuilder(Dictionary<MonikerHelper.MonikerAttribute, string> propertyTable)
        {
            string bindingType = null;
            string bindingConfigName = null;
 
            string spnIdentity = null;
            string upnIdentity = null;
            string dnsIdentity = null;
 
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Address, out address);
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Binding, out bindingType);
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.BindingConfiguration, out bindingConfigName);
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.SpnIdentity, out spnIdentity);
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.UpnIdentity, out upnIdentity);
            propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.DnsIdentity, out dnsIdentity);
 
            if (!string.IsNullOrEmpty(bindingType))
            {
                try
                {
                    binding = ConfigLoader.LookupBinding(bindingType, bindingConfigName);
                }
                catch (Exception e)
                {
                    if (Fx.IsFatal(e))
                        throw;
 
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.BindingLoadFromConfigFailedWith, bindingType, e.Message)));
                }
                if (binding == null)
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.BindingNotFoundInConfig, bindingType, bindingConfigName)));
 
            }
 
            if (binding == null)
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.BindingNotSpecified)));
 
            if (string.IsNullOrEmpty(address))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.AddressNotSpecified)));
 
            if (!string.IsNullOrEmpty(spnIdentity))
            {
                if ((!string.IsNullOrEmpty(upnIdentity)) || (!string.IsNullOrEmpty(dnsIdentity)))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentity)));
                identity = EndpointIdentity.CreateSpnIdentity(spnIdentity);
            }
            else if (!string.IsNullOrEmpty(upnIdentity))
            {
                if ((!string.IsNullOrEmpty(spnIdentity)) || (!string.IsNullOrEmpty(dnsIdentity)))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentity)));
                identity = EndpointIdentity.CreateUpnIdentity(upnIdentity);
            }
            else if (!string.IsNullOrEmpty(dnsIdentity))
            {
                if ((!string.IsNullOrEmpty(spnIdentity)) || (!string.IsNullOrEmpty(upnIdentity)))
                    throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentity)));
                identity = EndpointIdentity.CreateDnsIdentity(dnsIdentity);
            }
            else
                identity = null;
            ResolveTypeIfPossible(propertyTable);
        }
 
 
        bool dispatchEnabled = false;
        private bool CheckDispatch(ref Guid riid)
        {
            if ((dispatchEnabled) && (riid == InterfaceID.idIDispatch))
                return true;
            else
                return false;
        }
 
        ComProxy IProxyCreator.CreateProxy(IntPtr outer, ref Guid riid)
        {
            if (outer == IntPtr.Zero)
            {
                throw Fx.AssertAndThrow("OuterProxy cannot be null");
            }
 
            // No contract Fault on in
            if (contractType == null)
                TypeCacheManager.Provider.FindOrCreateType(riid, out contractType, true, false);
 
            if ((contractType.GUID != riid) && !(CheckDispatch(ref riid)))
                throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidCastException(SR.GetString(SR.NoInterface, riid)));
 
            Type proxiedType = EmitterCache.TypeEmitter.FindOrCreateType(contractType);
            ComProxy comProxy = null;
            TearOffProxy tearoffProxy = null;
            try
            {
                tearoffProxy = new TearOffProxy(this, proxiedType);
                comProxy = ComProxy.Create(outer, tearoffProxy.GetTransparentProxy(), tearoffProxy);
                return comProxy;
 
            }
            finally
            {
                if ((comProxy == null) && (tearoffProxy != null))
                    ((IDisposable)tearoffProxy).Dispose();
 
            }
        }
 
        bool IProxyCreator.SupportsErrorInfo(ref Guid riid)
        {
            if (contractType == null)
                return false;
            else
            {
                if ((contractType.GUID != riid) && !(CheckDispatch(ref riid)))
                    return false;
                else
                    return true;
            }
        }
 
        bool IProxyCreator.SupportsDispatch()
        {
            return dispatchEnabled;
        }
 
        bool IProxyCreator.SupportsIntrinsics()
        {
            return true;
        }
    }
}