//----------------------------------------------------------------------------- // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- namespace System.ServiceModel.ComIntegration { using System; using System.Collections.Generic; using System.Diagnostics; using System.EnterpriseServices; using System.Reflection; using System.Runtime; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Configuration; using System.ServiceModel.Description; using System.ServiceModel.Diagnostics; using System.ServiceModel.Dispatcher; using System.Xml; using SR = System.ServiceModel.SR; class ComPlusTypeLoader : IContractResolver { ServiceInfo info; bool transactionFlow; ITypeCacheManager interfaceResolver; Dictionary contracts; public ComPlusTypeLoader(ServiceInfo info) { this.info = info; this.transactionFlow = info.TransactionOption == TransactionOption.Required || info.TransactionOption == TransactionOption.Supported; this.interfaceResolver = new TypeCacheManager(); this.contracts = new Dictionary(); } void ValidateInterface(Guid iid) { // Filter known invalid IIDs if (!ComPlusTypeValidator.IsValidInterface(iid)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InvalidWebServiceInterface, iid))); } // Filter out interfaces with no configured methods bool configuredInterface = false; foreach (ContractInfo contractInfo in this.info.Contracts) { if (contractInfo.IID == iid) { if (contractInfo.Operations.Count == 0) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.RequireConfiguredMethods, iid))); } configuredInterface = true; break; } } // Filter out interfaces that aren't configured at all if (!configuredInterface) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.RequireConfiguredInterfaces, iid))); } } ContractDescription CreateContractDescriptionInternal(Guid iid, Type type) { ComContractElement contractConfigElement = ConfigLoader.LookupComContract(iid); if (contractConfigElement == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InterfaceNotFoundInConfig, iid))); if (String.IsNullOrEmpty(contractConfigElement.Name) || String.IsNullOrEmpty(contractConfigElement.Namespace)) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.CannotHaveNullOrEmptyNameOrNamespaceForIID, iid))); ContractDescription contract = new ContractDescription(contractConfigElement.Name, contractConfigElement.Namespace); contract.ContractType = type; contract.SessionMode = contractConfigElement.RequiresSession ? SessionMode.Required : SessionMode.Allowed; bool methodFound = false; List guidList = new List(); foreach (ComPersistableTypeElement typeElement in contractConfigElement.PersistableTypes) { Guid typeGuid = Fx.CreateGuid(typeElement.ID); guidList.Add(typeGuid); } IDataContractSurrogate contractSurrogate = null; // We create a surrogate when the persistable types config section is there // even if we have no types that we allow. // That way we have control over the error when the client tries to make a call // persistable type. if (guidList.Count > 0 || contractConfigElement.PersistableTypes.EmitClear) { contractSurrogate = new DataContractSurrogateForPersistWrapper(guidList.ToArray()); } foreach (ComMethodElement configMethod in contractConfigElement.ExposedMethods) { methodFound = false; foreach (MethodInfo method in type.GetMethods()) { if (method.Name == configMethod.ExposedMethod) { OperationDescription operation = CreateOperationDescription(contract, method, contractConfigElement, (null != contractSurrogate)); ConfigureOperationDescriptionBehaviors(operation, contractSurrogate); contract.Operations.Add(operation); methodFound = true; break; } } if (!methodFound) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.MethodGivenInConfigNotFoundOnInterface, configMethod.ExposedMethod, iid))); } if (contract.Operations.Count == 0) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.NoneOfTheMethodsForInterfaceFoundInConfig, iid))); ConfigureContractDescriptionBehaviors(contract); return contract; } void ConfigureContractDescriptionBehaviors(ContractDescription contract) { // OperationSelectorBehavior contract.Behaviors.Add(new OperationSelectorBehavior()); // ComPlusContractBehavior ComPlusContractBehavior comPlusContractBehavior = new ComPlusContractBehavior(this.info); contract.Behaviors.Add(comPlusContractBehavior); } void ConfigureOperationDescriptionBehaviors(OperationDescription operation, IDataContractSurrogate contractSurrogate) { // DataContractSerializerOperationBehavior DataContractSerializerOperationBehavior contractSerializer = new DataContractSerializerOperationBehavior(operation, TypeLoader.DefaultDataContractFormatAttribute); if (null != contractSurrogate) { contractSerializer.DataContractSurrogate = contractSurrogate; } operation.Behaviors.Add(contractSerializer); // OperationInvokerBehavior operation.Behaviors.Add(new OperationInvokerBehavior()); if (info.TransactionOption == TransactionOption.Supported || info.TransactionOption == TransactionOption.Required) { operation.Behaviors.Add(new TransactionFlowAttribute(TransactionFlowOption.Allowed)); } // OperationBehaviorAttribute OperationBehaviorAttribute operationBehaviorAttribute = new OperationBehaviorAttribute(); operationBehaviorAttribute.TransactionAutoComplete = true; operationBehaviorAttribute.TransactionScopeRequired = false; operation.Behaviors.Add(operationBehaviorAttribute); } // // Note - the code below this line a paraphrase of the SM reflection code in TypeLoader.cs // Ideally we would be re-using their code, but our assumptions are too disjoint // for that to be realistic at the time of writing (12/2004). // OperationDescription CreateOperationDescription(ContractDescription contract, MethodInfo methodInfo, ComContractElement config, bool allowReferences) { XmlName operationName = new XmlName(ServiceReflector.GetLogicalName(methodInfo)); XmlName returnValueName = TypeLoader.GetReturnValueName(operationName); if (ServiceReflector.IsBegin(methodInfo) || ServiceReflector.IsTask(methodInfo)) { Fx.Assert("No async operations allowed"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.NoAsyncOperationsAllowed()); } if (contract.Operations.FindAll(operationName.EncodedName).Count != 0) { Fx.Assert("Duplicate operation name"); throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.DuplicateOperation()); } OperationDescription operationDescription = new OperationDescription(operationName.EncodedName, contract); operationDescription.SyncMethod = methodInfo; operationDescription.IsInitiating = true; operationDescription.IsTerminating = false; operationDescription.KnownTypes.Add(typeof(Array)); operationDescription.KnownTypes.Add(typeof(DBNull)); operationDescription.KnownTypes.Add(typeof(CurrencyWrapper)); operationDescription.KnownTypes.Add(typeof(ErrorWrapper)); if (allowReferences) operationDescription.KnownTypes.Add(typeof(PersistStreamTypeWrapper)); foreach (ComUdtElement udt in config.UserDefinedTypes) { Type knownType; Guid typeLibID = Fx.CreateGuid(udt.TypeLibID); TypeCacheManager.Provider.FindOrCreateType(typeLibID, udt.TypeLibVersion, Fx.CreateGuid(udt.TypeDefID), out knownType, false); this.info.AddUdt(knownType, typeLibID); operationDescription.KnownTypes.Add(knownType); } string ns = contract.Namespace; XmlQualifiedName contractQName = new XmlQualifiedName(contract.Name, ns); string requestAction = NamingHelper.GetMessageAction(contractQName, operationName.DecodedName, null, false); string responseAction = NamingHelper.GetMessageAction(contractQName, operationName.DecodedName, null, true); MessageDescription inMessage = CreateIncomingMessageDescription(contract, methodInfo, ns, requestAction, allowReferences); MessageDescription outMessage = CreateOutgoingMessageDescription(contract, methodInfo, returnValueName, ns, responseAction, allowReferences); operationDescription.Messages.Add(inMessage); operationDescription.Messages.Add(outMessage); return operationDescription; } MessageDescription CreateIncomingMessageDescription(ContractDescription contract, MethodInfo methodInfo, string ns, string action, bool allowReferences) { ParameterInfo[] parameters = ServiceReflector.GetInputParameters(methodInfo, false); return CreateParameterMessageDescription(contract, parameters, null, null, null, methodInfo.Name, ns, action, MessageDirection.Input, allowReferences); } MessageDescription CreateOutgoingMessageDescription(ContractDescription contract, MethodInfo methodInfo, XmlName returnValueName, string ns, string action, bool allowReferences) { ParameterInfo[] parameters = ServiceReflector.GetOutputParameters(methodInfo, false); return CreateParameterMessageDescription(contract, parameters, methodInfo.ReturnType, methodInfo.ReturnTypeCustomAttributes, returnValueName, methodInfo.Name, ns, action, MessageDirection.Output, allowReferences); } MessageDescription CreateParameterMessageDescription(ContractDescription contract, ParameterInfo[] parameters, Type returnType, ICustomAttributeProvider returnCustomAttributes, XmlName returnValueName, string methodName, string ns, string action, MessageDirection direction, bool allowReferences) { MessageDescription messageDescription = new MessageDescription(action, direction); messageDescription.Body.WrapperNamespace = ns; for (int index = 0; index < parameters.Length; index++) { ParameterInfo parameter = parameters[index]; Type parameterType = TypeLoader.GetParameterType(parameter); if (!ComPlusTypeValidator.IsValidParameter(parameterType, parameter, allowReferences)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InvalidWebServiceParameter, parameter.Name, parameterType.Name, methodName, contract.Name))); } MessagePartDescription messagePart = CreateMessagePartDescription(parameterType, new XmlName(parameter.Name), ns, index); messageDescription.Body.Parts.Add(messagePart); } XmlName xmlName = new XmlName(methodName); if (returnType == null) { messageDescription.Body.WrapperName = xmlName.EncodedName; } else { messageDescription.Body.WrapperName = TypeLoader.GetBodyWrapperResponseName(xmlName).EncodedName; if (!ComPlusTypeValidator.IsValidParameter(returnType, returnCustomAttributes, allowReferences)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InvalidWebServiceReturnValue, returnType.Name, methodName, contract.Name))); } MessagePartDescription messagePart = CreateMessagePartDescription(returnType, returnValueName, ns, 0); messageDescription.Body.ReturnValue = messagePart; } return messageDescription; } MessagePartDescription CreateMessagePartDescription(Type bodyType, XmlName name, string ns, int index) { MessagePartDescription partDescription = new MessagePartDescription(name.EncodedName, ns); partDescription.SerializationPosition = index; partDescription.MemberInfo = null; partDescription.Type = bodyType; partDescription.Index = index; return partDescription; } ContractDescription ResolveIMetadataExchangeToContract() { // Use ServiceModel's TypeLoader to load the IMetadataExchange contract TypeLoader typeLoader = new TypeLoader(); return typeLoader.LoadContractDescription(typeof(IMetadataExchange)); } public ContractDescription ResolveContract(string contractTypeString) { Guid iid; if (ServiceMetadataBehavior.MexContractName == contractTypeString) iid = typeof(IMetadataExchange).GUID; else { if (!DiagnosticUtility.Utility.TryCreateGuid(contractTypeString, out iid)) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.ContractTypeNotAnIID, contractTypeString))); } ValidateInterface(iid); } // Check our cache to see if we already have one ContractDescription contract; if (this.contracts.TryGetValue(iid, out contract)) { return contract; } // If this is not IMetadataExchange continue if (iid != typeof(IMetadataExchange).GUID) { Type type; // Generate a managed type corresponding to the interface in question try { this.interfaceResolver.FindOrCreateType(this.info.ServiceType, iid, out type, false, true); } catch (InvalidOperationException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(e.Message)); } contract = CreateContractDescriptionInternal(iid, type); } else contract = ResolveIMetadataExchangeToContract(); contracts.Add(iid, contract); ComPlusServiceHostTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationServiceHostCreatedServiceContract, SR.TraceCodeComIntegrationServiceHostCreatedServiceContract, this.info, contract); return contract; } } }