//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ namespace System.ServiceModel { using System.ComponentModel; using System.Diagnostics; using System.Runtime; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.ServiceModel.Diagnostics; using System.Threading; using System.ServiceModel.Diagnostics.Application; using System.Runtime.Remoting.Messaging; using System.ServiceModel.Dispatcher; using System.Security; public abstract class ClientBase : ICommunicationObject, IDisposable where TChannel : class { TChannel channel; ChannelFactoryRef channelFactoryRef; EndpointTrait endpointTrait; // Determine whether the proxy can share factory with others. It is false only if the public getters // are invoked. bool canShareFactory = true; // Determine whether the proxy is currently holding a cached factory bool useCachedFactory; // Determine whether we have locked down sharing for this proxy. This is turned on only when the channel // is created. bool sharingFinalized; // Determine whether the ChannelFactoryRef has been released. We should release it only once per proxy bool channelFactoryRefReleased; // Determine whether we have released the last ref count of the ChannelFactory so that we could abort it when it was closing. bool releasedLastRef; object syncRoot = new object(); object finalizeLock = new object(); // Cache at most 32 ChannelFactories const int maxNumChannelFactories = 32; static ChannelFactoryRefCache factoryRefCache = new ChannelFactoryRefCache(maxNumChannelFactories); static object staticLock = new object(); static object cacheLock = new object(); static CacheSetting cacheSetting = CacheSetting.Default; static bool isCacheSettingReadOnly; static AsyncCallback onAsyncCallCompleted = Fx.ThunkCallback(new AsyncCallback(OnAsyncCallCompleted)); // IMPORTANT: any changes to the set of protected .ctors of this class need to be reflected // in ServiceContractGenerator.cs as well. protected ClientBase() { MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef(new ChannelFactory("*")); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait("*", null, null); InitializeChannelFactoryRef(); } } protected ClientBase(string endpointConfigurationName) { if (endpointConfigurationName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef(new ChannelFactory(endpointConfigurationName)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait(endpointConfigurationName, null, null); InitializeChannelFactoryRef(); } } protected ClientBase(string endpointConfigurationName, string remoteAddress) { if (endpointConfigurationName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName"); if (remoteAddress == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress"); MakeCacheSettingReadOnly(); EndpointAddress endpointAddress = new EndpointAddress(remoteAddress); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef(new ChannelFactory(endpointConfigurationName, endpointAddress)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait(endpointConfigurationName, endpointAddress, null); InitializeChannelFactoryRef(); } } protected ClientBase(string endpointConfigurationName, EndpointAddress remoteAddress) { if (endpointConfigurationName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName"); if (remoteAddress == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef(new ChannelFactory(endpointConfigurationName, remoteAddress)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait(endpointConfigurationName, remoteAddress, null); InitializeChannelFactoryRef(); } } protected ClientBase(Binding binding, EndpointAddress remoteAddress) { if (binding == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding"); if (remoteAddress == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOn) { this.endpointTrait = new ProgrammaticEndpointTrait(binding, remoteAddress, null); InitializeChannelFactoryRef(); } else { this.channelFactoryRef = new ChannelFactoryRef(new ChannelFactory(binding, remoteAddress)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } } protected ClientBase(ServiceEndpoint endpoint) { if (endpoint == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOn) { this.endpointTrait = new ServiceEndpointTrait(endpoint, null); this.InitializeChannelFactoryRef(); } else { channelFactoryRef = new ChannelFactoryRef(new ChannelFactory(endpoint)); channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } } protected ClientBase(InstanceContext callbackInstance) { if (callbackInstance == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef( new DuplexChannelFactory(callbackInstance, "*")); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait("*", null, callbackInstance); InitializeChannelFactoryRef(); } } protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName) { if (callbackInstance == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance"); if (endpointConfigurationName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef( new DuplexChannelFactory(callbackInstance, endpointConfigurationName)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait(endpointConfigurationName, null, callbackInstance); InitializeChannelFactoryRef(); } } protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, string remoteAddress) { if (callbackInstance == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance"); if (endpointConfigurationName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName"); if (remoteAddress == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress"); MakeCacheSettingReadOnly(); EndpointAddress endpointAddress = new EndpointAddress(remoteAddress); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef( new DuplexChannelFactory(callbackInstance, endpointConfigurationName, endpointAddress)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait(endpointConfigurationName, endpointAddress, callbackInstance); InitializeChannelFactoryRef(); } } protected ClientBase(InstanceContext callbackInstance, string endpointConfigurationName, EndpointAddress remoteAddress) { if (callbackInstance == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance"); if (endpointConfigurationName == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpointConfigurationName"); if (remoteAddress == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOff) { this.channelFactoryRef = new ChannelFactoryRef( new DuplexChannelFactory(callbackInstance, endpointConfigurationName, remoteAddress)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } else { this.endpointTrait = new ConfigurationEndpointTrait(endpointConfigurationName, remoteAddress, callbackInstance); InitializeChannelFactoryRef(); } } protected ClientBase(InstanceContext callbackInstance, Binding binding, EndpointAddress remoteAddress) { if (callbackInstance == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance"); if (binding == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("binding"); if (remoteAddress == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("remoteAddress"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOn) { this.endpointTrait = new ProgrammaticEndpointTrait(binding, remoteAddress, callbackInstance); InitializeChannelFactoryRef(); } else { this.channelFactoryRef = new ChannelFactoryRef( new DuplexChannelFactory(callbackInstance, binding, remoteAddress)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } } protected ClientBase(InstanceContext callbackInstance, ServiceEndpoint endpoint) { if (callbackInstance == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("callbackInstance"); if (endpoint == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endpoint"); MakeCacheSettingReadOnly(); if (cacheSetting == CacheSetting.AlwaysOn) { this.endpointTrait = new ServiceEndpointTrait(endpoint, callbackInstance); InitializeChannelFactoryRef(); } else { this.channelFactoryRef = new ChannelFactoryRef( new DuplexChannelFactory(callbackInstance, endpoint)); this.channelFactoryRef.ChannelFactory.TraceOpenAndClose = false; TryDisableSharing(); } } protected T GetDefaultValueForInitialization() { return default(T); } object ThisLock { get { return syncRoot; } } protected TChannel Channel { get { // created on demand, so that Mort can modify .Endpoint before calling methods on the client if (this.channel == null) { lock (ThisLock) { if (this.channel == null) { using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null) { if (DiagnosticUtility.ShouldUseActivity) { ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityOpenClientBase, typeof(TChannel).FullName), ActivityType.OpenClient); } if (this.useCachedFactory) { try { CreateChannelInternal(); } #pragma warning suppress 56500 // covered by FxCOP catch (Exception ex) { if (this.useCachedFactory && (ex is CommunicationException || ex is ObjectDisposedException || ex is TimeoutException)) { DiagnosticUtility.TraceHandledException(ex, TraceEventType.Warning); InvalidateCacheAndCreateChannel(); } else { #pragma warning suppress 56503 // [....], We throw only for unknown exceptions. throw; } } } else { CreateChannelInternal(); } } } } } return channel; } } public static CacheSetting CacheSetting { get { return cacheSetting; } set { lock (cacheLock) { if (isCacheSettingReadOnly && cacheSetting != value) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxImmutableClientBaseCacheSetting, typeof(TChannel).ToString()))); } else { cacheSetting = value; } } } } public ChannelFactory ChannelFactory { get { if (cacheSetting == CacheSetting.Default) { TryDisableSharing(); } return GetChannelFactory(); } } public ClientCredentials ClientCredentials { get { if (cacheSetting == CacheSetting.Default) { TryDisableSharing(); } return this.ChannelFactory.Credentials; } } public CommunicationState State { get { IChannel channel = (IChannel)this.channel; if (channel != null) { return channel.State; } else { // we may have failed to create the channel under open, in which case we our factory wouldn't be open if (!this.useCachedFactory) { return GetChannelFactory().State; } else { return CommunicationState.Created; } } } } public IClientChannel InnerChannel { get { return (IClientChannel)Channel; } } public ServiceEndpoint Endpoint { get { if (cacheSetting == CacheSetting.Default) { TryDisableSharing(); } return GetChannelFactory().Endpoint; } } public void Open() { ((ICommunicationObject)this).Open(GetChannelFactory().InternalOpenTimeout); } public void Abort() { IChannel channel = (IChannel)this.channel; if (channel != null) { channel.Abort(); } if (!channelFactoryRefReleased) { lock (staticLock) { if (!channelFactoryRefReleased) { if (this.channelFactoryRef.Release()) { this.releasedLastRef = true; } channelFactoryRefReleased = true; } } } // Abort the ChannelFactory if we released the last one. We should be able to abort it when another thread is closing it. if (this.releasedLastRef) { this.channelFactoryRef.Abort(); } } public void Close() { ((ICommunicationObject)this).Close(GetChannelFactory().InternalCloseTimeout); } public void DisplayInitializationUI() { ((IClientChannel)this.InnerChannel).DisplayInitializationUI(); } // This ensures that the cachesetting (on, off or default) cannot be modified by // another ClientBase instance of matching TChannel after the first instance is created. void MakeCacheSettingReadOnly() { if (isCacheSettingReadOnly) return; lock (cacheLock) { isCacheSettingReadOnly = true; } } void CreateChannelInternal() { try { this.channel = this.CreateChannel(); if (this.sharingFinalized) { if (this.canShareFactory && !this.useCachedFactory) { // It is OK to add ChannelFactory to the cache now. TryAddChannelFactoryToCache(); } } } finally { if (!this.sharingFinalized && cacheSetting == CacheSetting.Default) { // this.CreateChannel() is not called. For safety, we disable sharing. TryDisableSharing(); } } } protected virtual TChannel CreateChannel() { if (this.sharingFinalized) return GetChannelFactory().CreateChannel(); lock (this.finalizeLock) { this.sharingFinalized = true; return GetChannelFactory().CreateChannel(); } } void IDisposable.Dispose() { this.Close(); } void ICommunicationObject.Open(TimeSpan timeout) { TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); if (!this.useCachedFactory) { GetChannelFactory().Open(timeoutHelper.RemainingTime()); } this.InnerChannel.Open(timeoutHelper.RemainingTime()); } void ICommunicationObject.Close(TimeSpan timeout) { using (ServiceModelActivity activity = DiagnosticUtility.ShouldUseActivity ? ServiceModelActivity.CreateBoundedActivity() : null) { if (DiagnosticUtility.ShouldUseActivity) { ServiceModelActivity.Start(activity, SR.GetString(SR.ActivityCloseClientBase, typeof(TChannel).FullName), ActivityType.Close); } TimeoutHelper timeoutHelper = new TimeoutHelper(timeout); if (this.channel != null) { InnerChannel.Close(timeoutHelper.RemainingTime()); } if (!channelFactoryRefReleased) { lock (staticLock) { if (!channelFactoryRefReleased) { if (this.channelFactoryRef.Release()) { this.releasedLastRef = true; } this.channelFactoryRefReleased = true; } } // Close the factory outside of the lock so that we can abort from a different thread. if (this.releasedLastRef) { if (this.useCachedFactory) { this.channelFactoryRef.Abort(); } else { this.channelFactoryRef.Close(timeoutHelper.RemainingTime()); } } } } } event EventHandler ICommunicationObject.Closed { add { this.InnerChannel.Closed += value; } remove { this.InnerChannel.Closed -= value; } } event EventHandler ICommunicationObject.Closing { add { this.InnerChannel.Closing += value; } remove { this.InnerChannel.Closing -= value; } } event EventHandler ICommunicationObject.Faulted { add { this.InnerChannel.Faulted += value; } remove { this.InnerChannel.Faulted -= value; } } event EventHandler ICommunicationObject.Opened { add { this.InnerChannel.Opened += value; } remove { this.InnerChannel.Opened -= value; } } event EventHandler ICommunicationObject.Opening { add { this.InnerChannel.Opening += value; } remove { this.InnerChannel.Opening -= value; } } IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state) { return ((ICommunicationObject)this).BeginClose(GetChannelFactory().InternalCloseTimeout, callback, state); } IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return new ChainedAsyncResult(timeout, callback, state, BeginChannelClose, EndChannelClose, BeginFactoryClose, EndFactoryClose); } void ICommunicationObject.EndClose(IAsyncResult result) { ChainedAsyncResult.End(result); } IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state) { return ((ICommunicationObject)this).BeginOpen(GetChannelFactory().InternalOpenTimeout, callback, state); } IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return new ChainedAsyncResult(timeout, callback, state, BeginFactoryOpen, EndFactoryOpen, BeginChannelOpen, EndChannelOpen); } void ICommunicationObject.EndOpen(IAsyncResult result) { ChainedAsyncResult.End(result); } //ChainedAsyncResult methods for opening and closing ChannelFactory internal IAsyncResult BeginFactoryOpen(TimeSpan timeout, AsyncCallback callback, object state) { if (this.useCachedFactory) { return new CompletedAsyncResult(callback, state); } else { return GetChannelFactory().BeginOpen(timeout, callback, state); } } internal void EndFactoryOpen(IAsyncResult result) { if (this.useCachedFactory) { CompletedAsyncResult.End(result); } else { GetChannelFactory().EndOpen(result); } } internal IAsyncResult BeginChannelOpen(TimeSpan timeout, AsyncCallback callback, object state) { return this.InnerChannel.BeginOpen(timeout, callback, state); } internal void EndChannelOpen(IAsyncResult result) { this.InnerChannel.EndOpen(result); } internal IAsyncResult BeginFactoryClose(TimeSpan timeout, AsyncCallback callback, object state) { if (this.useCachedFactory) { return new CompletedAsyncResult(callback, state); } else { return GetChannelFactory().BeginClose(timeout, callback, state); } } internal void EndFactoryClose(IAsyncResult result) { if (typeof(CompletedAsyncResult).IsAssignableFrom(result.GetType())) { CompletedAsyncResult.End(result); } else { GetChannelFactory().EndClose(result); } } internal IAsyncResult BeginChannelClose(TimeSpan timeout, AsyncCallback callback, object state) { if (this.channel != null) { return this.InnerChannel.BeginClose(timeout, callback, state); } else { return new CompletedAsyncResult(callback, state); } } internal void EndChannelClose(IAsyncResult result) { if (typeof(CompletedAsyncResult).IsAssignableFrom(result.GetType())) { CompletedAsyncResult.End(result); } else { this.InnerChannel.EndClose(result); } } ChannelFactory GetChannelFactory() { return this.channelFactoryRef.ChannelFactory; } void InitializeChannelFactoryRef() { Fx.Assert(this.channelFactoryRef == null, "The channelFactory should have never been assigned"); Fx.Assert(this.canShareFactory, "GetChannelFactoryFromCache can be called only when canShareFactory is true"); lock (staticLock) { ChannelFactoryRef factoryRef; if (factoryRefCache.TryGetValue(this.endpointTrait, out factoryRef)) { if (factoryRef.ChannelFactory.State != CommunicationState.Opened) { // Remove the bad ChannelFactory. factoryRefCache.Remove(this.endpointTrait); } else { this.channelFactoryRef = factoryRef; this.channelFactoryRef.AddRef(); useCachedFactory = true; if (TD.ClientBaseChannelFactoryCacheHitIsEnabled()) { TD.ClientBaseChannelFactoryCacheHit(this); } return; } } } if (this.channelFactoryRef == null) { // Creating the ChannelFactory at initial time to catch configuration exception earlier. this.channelFactoryRef = CreateChannelFactoryRef(this.endpointTrait); } } static ChannelFactoryRef CreateChannelFactoryRef(EndpointTrait endpointTrait) { Fx.Assert(endpointTrait != null, "The endpointTrait should not be null when the factory can be shared."); ChannelFactory channelFactory = endpointTrait.CreateChannelFactory(); channelFactory.TraceOpenAndClose = false; return new ChannelFactoryRef(channelFactory); } // Once the channel is created, we can't disable caching. // This method can be called safely multiple times. // this.sharingFinalized is set the first time the method is called. // Subsequent calls are essentially no-ops. void TryDisableSharing() { if (this.sharingFinalized) return; lock (this.finalizeLock) { if (this.sharingFinalized) return; this.canShareFactory = false; this.sharingFinalized = true; if (this.useCachedFactory) { ChannelFactoryRef pendingFactoryRef = this.channelFactoryRef; this.channelFactoryRef = CreateChannelFactoryRef(this.endpointTrait); this.useCachedFactory = false; lock (staticLock) { if (!pendingFactoryRef.Release()) { pendingFactoryRef = null; } } if (pendingFactoryRef != null) pendingFactoryRef.Abort(); } } // can be done outside the lock since the lines below do not access shared data. // also the use of this.sharingFinalized in the lines above ensures that tracing // happens only once and only when needed. if (TD.ClientBaseUsingLocalChannelFactoryIsEnabled()) { TD.ClientBaseUsingLocalChannelFactory(this); } } void TryAddChannelFactoryToCache() { Fx.Assert(this.canShareFactory, "This should be called only when this proxy can share ChannelFactory."); Fx.Assert(this.channelFactoryRef.ChannelFactory.State == CommunicationState.Opened, "The ChannelFactory must be in Opened state for caching."); // Lock the cache and add the item to synchronize with lookup. lock (staticLock) { ChannelFactoryRef cfRef; if (!factoryRefCache.TryGetValue(this.endpointTrait, out cfRef)) { // Increment the ref count before adding to the cache. this.channelFactoryRef.AddRef(); factoryRefCache.Add(this.endpointTrait, this.channelFactoryRef); this.useCachedFactory = true; if (TD.ClientBaseCachedChannelFactoryCountIsEnabled()) { TD.ClientBaseCachedChannelFactoryCount(factoryRefCache.Count, maxNumChannelFactories, this); } } } } // NOTE: This should be called inside ThisLock void InvalidateCacheAndCreateChannel() { RemoveFactoryFromCache(); TryDisableSharing(); CreateChannelInternal(); } void RemoveFactoryFromCache() { lock (staticLock) { ChannelFactoryRef factoryRef; if (factoryRefCache.TryGetValue(this.endpointTrait, out factoryRef)) { if (object.ReferenceEquals(this.channelFactoryRef, factoryRef)) { factoryRefCache.Remove(this.endpointTrait); } } } } // WARNING: changes in the signature/name of the following delegates must be applied to the // ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code. protected delegate IAsyncResult BeginOperationDelegate(object[] inValues, AsyncCallback asyncCallback, object state); protected delegate object[] EndOperationDelegate(IAsyncResult result); // WARNING: Any changes in the signature/name of the following type and its ctor must be applied to the // ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code. protected class InvokeAsyncCompletedEventArgs : AsyncCompletedEventArgs { object[] results; internal InvokeAsyncCompletedEventArgs(object[] results, Exception error, bool cancelled, object userState) : base(error, cancelled, userState) { this.results = results; } public object[] Results { get { return this.results; } } } // WARNING: Any changes in the signature/name of the following method ctor must be applied to the // ClientClassGenerator.cs as well, otherwise the ClientClassGenerator would generate wrong code. protected void InvokeAsync(BeginOperationDelegate beginOperationDelegate, object[] inValues, EndOperationDelegate endOperationDelegate, SendOrPostCallback operationCompletedCallback, object userState) { if (beginOperationDelegate == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("beginOperationDelegate"); } if (endOperationDelegate == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("endOperationDelegate"); } AsyncOperation asyncOperation = AsyncOperationManager.CreateOperation(userState); AsyncOperationContext context = new AsyncOperationContext(asyncOperation, endOperationDelegate, operationCompletedCallback); Exception error = null; object[] results = null; IAsyncResult result = null; try { result = beginOperationDelegate(inValues, onAsyncCallCompleted, context); if (result.CompletedSynchronously) { results = endOperationDelegate(result); } } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } error = e; } if (error != null || result.CompletedSynchronously) /* result cannot be null if error == null */ { CompleteAsyncCall(context, results, error); } } static void OnAsyncCallCompleted(IAsyncResult result) { if (result.CompletedSynchronously) { return; } AsyncOperationContext context = (AsyncOperationContext)result.AsyncState; Exception error = null; object[] results = null; try { results = context.EndDelegate(result); } catch (Exception e) { if (Fx.IsFatal(e)) { throw; } error = e; } CompleteAsyncCall(context, results, error); } static void CompleteAsyncCall(AsyncOperationContext context, object[] results, Exception error) { if (context.CompletionCallback != null) { InvokeAsyncCompletedEventArgs e = new InvokeAsyncCompletedEventArgs(results, error, false, context.AsyncOperation.UserSuppliedState); context.AsyncOperation.PostOperationCompleted(context.CompletionCallback, e); } else { context.AsyncOperation.OperationCompleted(); } } class AsyncOperationContext { AsyncOperation asyncOperation; EndOperationDelegate endDelegate; SendOrPostCallback completionCallback; internal AsyncOperationContext(AsyncOperation asyncOperation, EndOperationDelegate endDelegate, SendOrPostCallback completionCallback) { this.asyncOperation = asyncOperation; this.endDelegate = endDelegate; this.completionCallback = completionCallback; } internal AsyncOperation AsyncOperation { get { return this.asyncOperation; } } internal EndOperationDelegate EndDelegate { get { return this.endDelegate; } } internal SendOrPostCallback CompletionCallback { get { return this.completionCallback; } } } protected class ChannelBase : IClientChannel, IOutputChannel, IRequestChannel, IChannelBaseProxy where T : class { ServiceChannel channel; System.ServiceModel.Dispatcher.ImmutableClientRuntime runtime; protected ChannelBase(ClientBase client) { if (client.Endpoint.Address == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxChannelFactoryEndpointAddressUri))); } ChannelFactory cf = client.ChannelFactory; cf.EnsureOpened(); // to prevent the NullReferenceException that is thrown if the ChannelFactory is not open when cf.ServiceChannelFactory is accessed. this.channel = cf.ServiceChannelFactory.CreateServiceChannel(client.Endpoint.Address, client.Endpoint.Address.Uri); this.channel.InstanceContext = cf.CallbackInstance; this.runtime = this.channel.ClientRuntime.GetRuntime(); } [Fx.Tag.SecurityNote(Critical = "Accesses the critical IMethodCallMessage interface.", Safe = "The implementation of IMethodCallMessage is local and is created locally as weell; i.e. not passed in from Remoting.")] [SecuritySafeCritical] protected IAsyncResult BeginInvoke(string methodName, object[] args, AsyncCallback callback, object state) { object[] inArgs = new object[args.Length + 2]; Array.Copy(args, inArgs, args.Length); inArgs[inArgs.Length - 2] = callback; inArgs[inArgs.Length - 1] = state; IMethodCallMessage methodCall = new MethodCallMessage(inArgs); ProxyOperationRuntime op = GetOperationByName(methodName); object[] ins = op.MapAsyncBeginInputs(methodCall, out callback, out state); return this.channel.BeginCall(op.Action, op.IsOneWay, op, ins, callback, state); } [Fx.Tag.SecurityNote(Critical = "Accesses the critical IMethodCallMessage interface.", Safe = "The implementation of IMethodCallMessage is local and is created locally as weell; i.e. not passed in from Remoting.")] [SecuritySafeCritical] protected object EndInvoke(string methodName, object[] args, IAsyncResult result) { object[] inArgs = new object[args.Length + 1]; Array.Copy(args, inArgs, args.Length); inArgs[inArgs.Length - 1] = result; IMethodCallMessage methodCall = new MethodCallMessage(inArgs); ProxyOperationRuntime op = GetOperationByName(methodName); object[] outs; op.MapAsyncEndInputs(methodCall, out result, out outs); object ret = this.channel.EndCall(op.Action, outs, result); object[] retArgs = op.MapAsyncOutputs(methodCall, outs, ref ret); if (retArgs != null) { Fx.Assert(retArgs.Length == inArgs.Length, "retArgs.Length should be equal to inArgs.Length"); Array.Copy(retArgs, args, args.Length); } return ret; } System.ServiceModel.Dispatcher.ProxyOperationRuntime GetOperationByName(string methodName) { ProxyOperationRuntime op = this.runtime.GetOperationByName(methodName); if (op == null) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotSupportedException(SR.GetString(SR.SFxMethodNotSupported1, methodName))); } return op; } bool IClientChannel.AllowInitializationUI { get { return ((IClientChannel)this.channel).AllowInitializationUI; } set { ((IClientChannel)this.channel).AllowInitializationUI = value; } } bool IClientChannel.DidInteractiveInitialization { get { return ((IClientChannel)this.channel).DidInteractiveInitialization; } } Uri IClientChannel.Via { get { return ((IClientChannel)this.channel).Via; } } event EventHandler IClientChannel.UnknownMessageReceived { add { ((IClientChannel)this.channel).UnknownMessageReceived += value; } remove { ((IClientChannel)this.channel).UnknownMessageReceived -= value; } } void IClientChannel.DisplayInitializationUI() { ((IClientChannel)this.channel).DisplayInitializationUI(); } IAsyncResult IClientChannel.BeginDisplayInitializationUI(AsyncCallback callback, object state) { return ((IClientChannel)this.channel).BeginDisplayInitializationUI(callback, state); } void IClientChannel.EndDisplayInitializationUI(IAsyncResult result) { ((IClientChannel)this.channel).EndDisplayInitializationUI(result); } bool IContextChannel.AllowOutputBatching { get { return ((IContextChannel)this.channel).AllowOutputBatching; } set { ((IContextChannel)this.channel).AllowOutputBatching = value; } } IInputSession IContextChannel.InputSession { get { return ((IContextChannel)this.channel).InputSession; } } EndpointAddress IContextChannel.LocalAddress { get { return ((IContextChannel)this.channel).LocalAddress; } } TimeSpan IContextChannel.OperationTimeout { get { return ((IContextChannel)this.channel).OperationTimeout; } set { ((IContextChannel)this.channel).OperationTimeout = value; } } IOutputSession IContextChannel.OutputSession { get { return ((IContextChannel)this.channel).OutputSession; } } EndpointAddress IContextChannel.RemoteAddress { get { return ((IContextChannel)this.channel).RemoteAddress; } } string IContextChannel.SessionId { get { return ((IContextChannel)this.channel).SessionId; } } TProperty IChannel.GetProperty() { return ((IChannel)this.channel).GetProperty(); } CommunicationState ICommunicationObject.State { get { return ((ICommunicationObject)this.channel).State; } } event EventHandler ICommunicationObject.Closed { add { ((ICommunicationObject)this.channel).Closed += value; } remove { ((ICommunicationObject)this.channel).Closed -= value; } } event EventHandler ICommunicationObject.Closing { add { ((ICommunicationObject)this.channel).Closing += value; } remove { ((ICommunicationObject)this.channel).Closing -= value; } } event EventHandler ICommunicationObject.Faulted { add { ((ICommunicationObject)this.channel).Faulted += value; } remove { ((ICommunicationObject)this.channel).Faulted -= value; } } event EventHandler ICommunicationObject.Opened { add { ((ICommunicationObject)this.channel).Opened += value; } remove { ((ICommunicationObject)this.channel).Opened -= value; } } event EventHandler ICommunicationObject.Opening { add { ((ICommunicationObject)this.channel).Opening += value; } remove { ((ICommunicationObject)this.channel).Opening -= value; } } void ICommunicationObject.Abort() { ((ICommunicationObject)this.channel).Abort(); } void ICommunicationObject.Close() { ((ICommunicationObject)this.channel).Close(); } void ICommunicationObject.Close(TimeSpan timeout) { ((ICommunicationObject)this.channel).Close(timeout); } IAsyncResult ICommunicationObject.BeginClose(AsyncCallback callback, object state) { return ((ICommunicationObject)this.channel).BeginClose(callback, state); } IAsyncResult ICommunicationObject.BeginClose(TimeSpan timeout, AsyncCallback callback, object state) { return ((ICommunicationObject)this.channel).BeginClose(timeout, callback, state); } void ICommunicationObject.EndClose(IAsyncResult result) { ((ICommunicationObject)this.channel).EndClose(result); } void ICommunicationObject.Open() { ((ICommunicationObject)this.channel).Open(); } void ICommunicationObject.Open(TimeSpan timeout) { ((ICommunicationObject)this.channel).Open(timeout); } IAsyncResult ICommunicationObject.BeginOpen(AsyncCallback callback, object state) { return ((ICommunicationObject)this.channel).BeginOpen(callback, state); } IAsyncResult ICommunicationObject.BeginOpen(TimeSpan timeout, AsyncCallback callback, object state) { return ((ICommunicationObject)this.channel).BeginOpen(timeout, callback, state); } void ICommunicationObject.EndOpen(IAsyncResult result) { ((ICommunicationObject)this.channel).EndOpen(result); } IExtensionCollection IExtensibleObject.Extensions { get { return ((IExtensibleObject)this.channel).Extensions; } } void IDisposable.Dispose() { ((IDisposable)this.channel).Dispose(); } Uri IOutputChannel.Via { get { return ((IOutputChannel)this.channel).Via; } } EndpointAddress IOutputChannel.RemoteAddress { get { return ((IOutputChannel)this.channel).RemoteAddress; } } void IOutputChannel.Send(Message message) { ((IOutputChannel)this.channel).Send(message); } void IOutputChannel.Send(Message message, TimeSpan timeout) { ((IOutputChannel)this.channel).Send(message, timeout); } IAsyncResult IOutputChannel.BeginSend(Message message, AsyncCallback callback, object state) { return ((IOutputChannel)this.channel).BeginSend(message, callback, state); } IAsyncResult IOutputChannel.BeginSend(Message message, TimeSpan timeout, AsyncCallback callback, object state) { return ((IOutputChannel)this.channel).BeginSend(message, timeout, callback, state); } void IOutputChannel.EndSend(IAsyncResult result) { ((IOutputChannel)this.channel).EndSend(result); } Uri IRequestChannel.Via { get { return ((IRequestChannel)this.channel).Via; } } EndpointAddress IRequestChannel.RemoteAddress { get { return ((IRequestChannel)this.channel).RemoteAddress; } } Message IRequestChannel.Request(Message message) { return ((IRequestChannel)this.channel).Request(message); } Message IRequestChannel.Request(Message message, TimeSpan timeout) { return ((IRequestChannel)this.channel).Request(message, timeout); } IAsyncResult IRequestChannel.BeginRequest(Message message, AsyncCallback callback, object state) { return ((IRequestChannel)this.channel).BeginRequest(message, callback, state); } IAsyncResult IRequestChannel.BeginRequest(Message message, TimeSpan timeout, AsyncCallback callback, object state) { return ((IRequestChannel)this.channel).BeginRequest(message, timeout, callback, state); } Message IRequestChannel.EndRequest(IAsyncResult result) { return ((IRequestChannel)this.channel).EndRequest(result); } ServiceChannel IChannelBaseProxy.GetServiceChannel() { return this.channel; } class MethodCallMessage : IMethodCallMessage { readonly object[] args; public MethodCallMessage(object[] args) { this.args = args; } public object[] Args { get { return this.args; } } public int ArgCount { get { return this.args.Length; } } public LogicalCallContext LogicalCallContext { get { return null; } } public object GetInArg(int argNum) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } public string GetInArgName(int index) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } public int InArgCount { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public object[] InArgs { get { return this.args; } } public object GetArg(int argNum) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } public string GetArgName(int index) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } public bool HasVarArgs { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public Reflection.MethodBase MethodBase { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public string MethodName { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public object MethodSignature { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public string TypeName { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public string Uri { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } public Collections.IDictionary Properties { get { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new NotImplementedException()); } } } } } }