//---------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------- namespace System.ServiceModel.Activities { using System.Diagnostics; using System.Globalization; using System.ServiceModel; using System.ServiceModel.Activities.Description; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.Runtime; using System.Threading; using System.Security; using System.Security.Permissions; [Fx.Tag.XamlVisible(false)] public class WorkflowControlEndpoint : ServiceEndpoint { static Uri defaultBaseUri; // Double-checked locking pattern requires volatile for read/write synchronization static volatile ContractDescription workflowControlServiceBaseContract; static volatile ContractDescription workflowControlServiceContract; static object workflowContractDescriptionLock = new object(); public WorkflowControlEndpoint() : this(WorkflowControlEndpoint.GetDefaultBinding(), new EndpointAddress(new Uri(WorkflowControlEndpoint.DefaultBaseUri, new Uri(Guid.NewGuid().ToString(), UriKind.Relative)))) { } public WorkflowControlEndpoint(Binding binding, EndpointAddress address) : base(WorkflowControlEndpoint.WorkflowControlServiceContract, binding, address) { this.IsSystemEndpoint = true; } internal static ContractDescription WorkflowControlServiceBaseContract { get { if (workflowControlServiceBaseContract == null) { lock (workflowContractDescriptionLock) { if (workflowControlServiceBaseContract == null) { foreach (OperationDescription operation in WorkflowControlServiceContract.Operations) { if (operation.DeclaringContract != WorkflowControlServiceContract) { workflowControlServiceBaseContract = operation.DeclaringContract; break; } } } } } return workflowControlServiceBaseContract; } } internal static ContractDescription WorkflowControlServiceContract { get { if (workflowControlServiceContract == null) { lock (workflowContractDescriptionLock) { if (workflowControlServiceContract == null) { ContractDescription tempControlServiceContract = ContractDescription.GetContract( typeof(IWorkflowUpdateableInstanceManagement)); tempControlServiceContract.Behaviors.Add(new ServiceMetadataContractBehavior(true)); ApplyOperationBehaviors(tempControlServiceContract); // For back-compat, need to support existing code which expects the old contract type tempControlServiceContract.ContractType = typeof(IWorkflowInstanceManagement); workflowControlServiceContract = tempControlServiceContract; } } } return workflowControlServiceContract; } } [Fx.Tag.SecurityNote(Critical = "Critical because it retreives the Process Id via Process.Id, which has a link demand for Full Trust.", Safe = "Safe because it demands FullTrust")] [SecuritySafeCritical] [PermissionSet(SecurityAction.Demand, Name = "FullTrust")] static void RetrieveProcessIdAndAppDomainId(out int processId, out int appDomainId) { processId = Process.GetCurrentProcess().Id; appDomainId = AppDomain.CurrentDomain.Id; } static Uri DefaultBaseUri { get { if (defaultBaseUri == null) { // If we are running in full trust, use the ProcessId and AppDomainId. If partial trust, use a new guid to make the URI unique and avoid // the usage of ProcessId and AppDomainId. We are doing this for back compat. if (PartialTrustHelpers.AppDomainFullyTrusted) { int processId; int appDomainId; RetrieveProcessIdAndAppDomainId(out processId, out appDomainId); defaultBaseUri = new Uri(string.Format(CultureInfo.InvariantCulture, "net.pipe://localhost/workflowControlServiceEndpoint/{0}/{1}", processId, appDomainId)); } else { Uri tempUri = new Uri(string.Format(CultureInfo.InvariantCulture, "net.pipe://localhost/workflowControlServiceEndpoint/{0}", Guid.NewGuid().ToString())); // Using Interlocked.CompareExchange because new Guid.NewGuid is not atomic. Interlocked.CompareExchange(ref defaultBaseUri, tempUri, null); } } return defaultBaseUri; } } static Binding GetDefaultBinding() { return new NetNamedPipeBinding(NetNamedPipeSecurityMode.None) { TransactionFlow = true }; } static void ApplyOperationBehaviors(ContractDescription contractDescription) { foreach (OperationDescription operationDescription in contractDescription.Operations) { //Except "Abandon" all the operations in this contract are Async. //All Transacted* operation are Transacted & Async. switch (operationDescription.Name) { case XD2.WorkflowInstanceManagementService.Abandon: case XD2.WorkflowInstanceManagementService.Cancel: case XD2.WorkflowInstanceManagementService.Run: case XD2.WorkflowInstanceManagementService.Suspend: case XD2.WorkflowInstanceManagementService.Terminate: case XD2.WorkflowInstanceManagementService.Unsuspend: case XD2.WorkflowInstanceManagementService.Update: EnsureDispatch(operationDescription); break; case XD2.WorkflowInstanceManagementService.TransactedCancel: case XD2.WorkflowInstanceManagementService.TransactedRun: case XD2.WorkflowInstanceManagementService.TransactedSuspend: case XD2.WorkflowInstanceManagementService.TransactedTerminate: case XD2.WorkflowInstanceManagementService.TransactedUnsuspend: case XD2.WorkflowInstanceManagementService.TransactedUpdate: EnsureDispatch(operationDescription); EnsureTransactedInvoke(operationDescription); break; } } } static void EnsureDispatch(OperationDescription operationDescription) { operationDescription.Behaviors.Add(new ControlOperationBehavior(false)); } static void EnsureTransactedInvoke(OperationDescription operationDescription) { OperationBehaviorAttribute operationAttribute = operationDescription.Behaviors.Find(); operationAttribute.TransactionScopeRequired = true; } } }