| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 |
- //-----------------------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //-----------------------------------------------------------------------------
- namespace System.ServiceModel.Dispatcher
- {
- using System.Collections;
- using System.ServiceModel.Channels;
- using System.ServiceModel;
- using System.ServiceModel.Description;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Diagnostics;
- using System.Runtime.Serialization;
- using System.Reflection;
- using System.Xml;
- using System.Xml.Serialization;
- using System.ServiceModel.Diagnostics;
- using System.IO;
- using System.Xml.Schema;
- class XmlSerializerOperationFormatter : OperationFormatter
- {
- const string soap11Encoding = "http://schemas.xmlsoap.org/soap/encoding/";
- const string soap12Encoding = "http://www.w3.org/2003/05/soap-encoding";
- bool isEncoded;
- MessageInfo requestMessageInfo;
- MessageInfo replyMessageInfo;
- public XmlSerializerOperationFormatter(OperationDescription description, XmlSerializerFormatAttribute xmlSerializerFormatAttribute,
- MessageInfo requestMessageInfo, MessageInfo replyMessageInfo) :
- base(description, xmlSerializerFormatAttribute.Style == OperationFormatStyle.Rpc, xmlSerializerFormatAttribute.IsEncoded)
- {
- if (xmlSerializerFormatAttribute.IsEncoded && xmlSerializerFormatAttribute.Style != OperationFormatStyle.Rpc)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxDocEncodedNotSupported, description.Name)));
- this.isEncoded = xmlSerializerFormatAttribute.IsEncoded;
- this.requestMessageInfo = requestMessageInfo;
- this.replyMessageInfo = replyMessageInfo;
- }
- protected override void AddHeadersToMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest)
- {
- XmlSerializer serializer;
- MessageHeaderDescriptionTable headerDescriptionTable;
- MessageHeaderDescription unknownHeaderDescription;
- bool mustUnderstand;
- bool relay;
- string actor;
- try
- {
- if (isRequest)
- {
- serializer = requestMessageInfo.HeaderSerializer;
- headerDescriptionTable = requestMessageInfo.HeaderDescriptionTable;
- unknownHeaderDescription = requestMessageInfo.UnknownHeaderDescription;
- }
- else
- {
- serializer = replyMessageInfo.HeaderSerializer;
- headerDescriptionTable = replyMessageInfo.HeaderDescriptionTable;
- unknownHeaderDescription = replyMessageInfo.UnknownHeaderDescription;
- }
- if (serializer != null)
- {
- object[] headerValues = new object[headerDescriptionTable.Count];
- MessageHeaderOfTHelper messageHeaderOfTHelper = null;
- int headerIndex = 0;
- foreach (MessageHeaderDescription headerDescription in messageDescription.Headers)
- {
- object parameterValue = parameters[headerDescription.Index];
- if (!headerDescription.IsUnknownHeaderCollection)
- {
- if (headerDescription.TypedHeader)
- {
- if (messageHeaderOfTHelper == null)
- messageHeaderOfTHelper = new MessageHeaderOfTHelper(parameters.Length);
- headerValues[headerIndex++] = messageHeaderOfTHelper.GetContentAndSaveHeaderAttributes(parameters[headerDescription.Index], headerDescription);
- }
- else
- headerValues[headerIndex++] = parameterValue;
- }
- }
- MemoryStream memoryStream = new MemoryStream();
- XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream);
- bufferWriter.WriteStartElement("root");
- serializer.Serialize(bufferWriter, headerValues, null, isEncoded ? GetEncoding(message.Version.Envelope) : null);
- bufferWriter.WriteEndElement();
- bufferWriter.Flush();
- XmlDocument doc = new XmlDocument();
- memoryStream.Position = 0;
- doc.Load(memoryStream);
- //doc.Save(Console.Out);
- foreach (XmlElement element in doc.DocumentElement.ChildNodes)
- {
- MessageHeaderDescription matchingHeaderDescription = headerDescriptionTable.Get(element.LocalName, element.NamespaceURI);
- if (matchingHeaderDescription == null)
- message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI,
- false/*mustUnderstand*/, null/*actor*/, false/*relay*/, element));
- else
- {
- if (matchingHeaderDescription.TypedHeader)
- messageHeaderOfTHelper.GetHeaderAttributes(matchingHeaderDescription, out mustUnderstand, out relay, out actor);
- else
- {
- mustUnderstand = matchingHeaderDescription.MustUnderstand;
- relay = matchingHeaderDescription.Relay;
- actor = matchingHeaderDescription.Actor;
- }
- message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI,
- mustUnderstand, actor, relay, element));
- }
- }
- }
- if (unknownHeaderDescription != null && parameters[unknownHeaderDescription.Index] != null)
- {
- foreach (object unknownHeader in (IEnumerable)parameters[unknownHeaderDescription.Index])
- {
- XmlElement element = (XmlElement)GetContentOfMessageHeaderOfT(unknownHeaderDescription, unknownHeader, out mustUnderstand, out relay, out actor);
- if (element != null)
- message.Headers.Add(new XmlElementMessageHeader(this, message.Version, element.LocalName, element.NamespaceURI,
- mustUnderstand, actor, relay, element));
- }
- }
- }
- catch (InvalidOperationException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
- SR.GetString(SR.SFxErrorSerializingHeader, messageDescription.MessageName, e.Message), e));
- }
- }
- protected override void GetHeadersFromMessage(Message message, MessageDescription messageDescription, object[] parameters, bool isRequest)
- {
- try
- {
- XmlSerializer serializer;
- MessageHeaderDescriptionTable headerDescriptionTable;
- MessageHeaderDescription unknownHeaderDescription;
- if (isRequest)
- {
- serializer = requestMessageInfo.HeaderSerializer;
- headerDescriptionTable = requestMessageInfo.HeaderDescriptionTable;
- unknownHeaderDescription = requestMessageInfo.UnknownHeaderDescription;
- }
- else
- {
- serializer = replyMessageInfo.HeaderSerializer;
- headerDescriptionTable = replyMessageInfo.HeaderDescriptionTable;
- unknownHeaderDescription = replyMessageInfo.UnknownHeaderDescription;
- }
- MessageHeaders headers = message.Headers;
- ArrayList unknownHeaders = null;
- XmlDocument xmlDoc = null;
- if (unknownHeaderDescription != null)
- {
- unknownHeaders = new ArrayList();
- xmlDoc = new XmlDocument();
- }
- if (serializer == null)
- {
- if (unknownHeaderDescription != null)
- {
- for (int headerIndex = 0; headerIndex < headers.Count; headerIndex++)
- AddUnknownHeader(unknownHeaderDescription, unknownHeaders, xmlDoc, null/*bufferWriter*/, headers[headerIndex], headers.GetReaderAtHeader(headerIndex));
- parameters[unknownHeaderDescription.Index] = unknownHeaders.ToArray(unknownHeaderDescription.TypedHeader ? typeof(MessageHeader<XmlElement>) : typeof(XmlElement));
- }
- return;
- }
- MemoryStream memoryStream = new MemoryStream();
- XmlDictionaryWriter bufferWriter = XmlDictionaryWriter.CreateTextWriter(memoryStream);
- message.WriteStartEnvelope(bufferWriter);
- message.WriteStartHeaders(bufferWriter);
- MessageHeaderOfTHelper messageHeaderOfTHelper = null;
- for (int headerIndex = 0; headerIndex < headers.Count; headerIndex++)
- {
- MessageHeaderInfo header = headers[headerIndex];
- XmlDictionaryReader headerReader = headers.GetReaderAtHeader(headerIndex);
- MessageHeaderDescription matchingHeaderDescription = headerDescriptionTable.Get(header.Name, header.Namespace);
- if (matchingHeaderDescription != null)
- {
- if (header.MustUnderstand)
- headers.UnderstoodHeaders.Add(header);
- if (matchingHeaderDescription.TypedHeader)
- {
- if (messageHeaderOfTHelper == null)
- messageHeaderOfTHelper = new MessageHeaderOfTHelper(parameters.Length);
- messageHeaderOfTHelper.SetHeaderAttributes(matchingHeaderDescription, header.MustUnderstand, header.Relay, header.Actor);
- }
- }
- if (matchingHeaderDescription == null && unknownHeaderDescription != null)
- AddUnknownHeader(unknownHeaderDescription, unknownHeaders, xmlDoc, bufferWriter, header, headerReader);
- else
- bufferWriter.WriteNode(headerReader, false);
- headerReader.Close();
- }
- bufferWriter.WriteEndElement();
- bufferWriter.WriteEndElement();
- bufferWriter.Flush();
- /*
- XmlDocument doc = new XmlDocument();
- memoryStream.Position = 0;
- doc.Load(memoryStream);
- doc.Save(Console.Out);
- */
- memoryStream.Position = 0;
- XmlDictionaryReader bufferReader = XmlDictionaryReader.CreateTextReader(memoryStream.GetBuffer(), 0, (int)memoryStream.Length, XmlDictionaryReaderQuotas.Max);
- bufferReader.ReadStartElement();
- bufferReader.MoveToContent();
- if (!bufferReader.IsEmptyElement)
- {
- bufferReader.ReadStartElement();
- object[] headerValues = (object[])serializer.Deserialize(bufferReader, isEncoded ? GetEncoding(message.Version.Envelope) : null);
- int headerIndex = 0;
- foreach (MessageHeaderDescription headerDescription in messageDescription.Headers)
- {
- if (!headerDescription.IsUnknownHeaderCollection)
- {
- object parameterValue = headerValues[headerIndex++];
- if (headerDescription.TypedHeader && parameterValue != null)
- parameterValue = messageHeaderOfTHelper.CreateMessageHeader(headerDescription, parameterValue);
- parameters[headerDescription.Index] = parameterValue;
- }
- }
- bufferReader.Close();
- }
- if (unknownHeaderDescription != null)
- parameters[unknownHeaderDescription.Index] = unknownHeaders.ToArray(unknownHeaderDescription.TypedHeader ? typeof(MessageHeader<XmlElement>) : typeof(XmlElement));
- }
- catch (InvalidOperationException e)
- {
- // all exceptions from XmlSerializer get wrapped in InvalidOperationException,
- // so we must be conservative and never turn this into a fault
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
- SR.GetString(SR.SFxErrorDeserializingHeader, messageDescription.MessageName), e));
- }
- }
- private static void AddUnknownHeader(MessageHeaderDescription unknownHeaderDescription, ArrayList unknownHeaders, XmlDocument xmlDoc, XmlDictionaryWriter bufferWriter, MessageHeaderInfo header, XmlDictionaryReader headerReader)
- {
- object unknownHeader = xmlDoc.ReadNode(headerReader);
- if (bufferWriter != null)
- ((XmlElement)unknownHeader).WriteTo(bufferWriter);
- if (unknownHeader != null && unknownHeaderDescription.TypedHeader)
- unknownHeader = TypedHeaderManager.Create(unknownHeaderDescription.Type, unknownHeader, header.MustUnderstand, header.Relay, header.Actor);
- unknownHeaders.Add(unknownHeader);
- }
- protected override void WriteBodyAttributes(XmlDictionaryWriter writer, MessageVersion version)
- {
- if (isEncoded && version.Envelope == EnvelopeVersion.Soap11)
- {
- string encoding = GetEncoding(version.Envelope);
- writer.WriteAttributeString("encodingStyle", version.Envelope.Namespace, encoding);
- }
- writer.WriteAttributeString("xmlns", "xsi", null, XmlSchema.InstanceNamespace);
- writer.WriteAttributeString("xmlns", "xsd", null, XmlSchema.Namespace);
- }
- protected override void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, string action, MessageDescription messageDescription, object returnValue, object[] parameters, bool isRequest)
- {
- if (writer == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("writer"));
- if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("parameters"));
- try
- {
- MessageInfo messageInfo;
- if (isRequest)
- messageInfo = requestMessageInfo;
- else
- messageInfo = replyMessageInfo;
- if (messageInfo.RpcEncodedTypedMessageBodyParts == null)
- {
- SerializeBody(writer, version, messageInfo.BodySerializer, messageDescription.Body.ReturnValue, messageDescription.Body.Parts, returnValue, parameters);
- return;
- }
- object[] bodyPartValues = new object[messageInfo.RpcEncodedTypedMessageBodyParts.Count];
- object bodyObject = parameters[messageDescription.Body.Parts[0].Index];
- if (bodyObject == null)
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxBodyCannotBeNull, messageDescription.MessageName)));
- int i = 0;
- foreach (MessagePartDescription bodyPart in messageInfo.RpcEncodedTypedMessageBodyParts)
- {
- MemberInfo member = bodyPart.MemberInfo;
- FieldInfo field = member as FieldInfo;
- if (field != null)
- bodyPartValues[i++] = field.GetValue(bodyObject);
- else
- {
- PropertyInfo property = member as PropertyInfo;
- if (property != null)
- bodyPartValues[i++] = property.GetValue(bodyObject, null);
- }
- }
- SerializeBody(writer, version, messageInfo.BodySerializer, null/*returnPart*/, messageInfo.RpcEncodedTypedMessageBodyParts, null/*returnValue*/, bodyPartValues);
- }
- catch (InvalidOperationException e)
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
- SR.GetString(SR.SFxErrorSerializingBody, messageDescription.MessageName, e.Message), e));
- }
- }
- void SerializeBody(XmlDictionaryWriter writer, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, object returnValue, object[] parameters)
- {
- if (serializer == null)
- {
- return;
- }
- bool hasReturnValue = IsValidReturnValue(returnPart);
- object[] bodyParameters = new object[bodyParts.Count + (hasReturnValue ? 1 : 0)];
- int paramIndex = 0;
- if (hasReturnValue)
- bodyParameters[paramIndex++] = returnValue;
- for (int i = 0; i < bodyParts.Count; i++)
- bodyParameters[paramIndex++] = parameters[bodyParts[i].Index];
- string encoding = isEncoded ? GetEncoding(version.Envelope) : null;
- serializer.Serialize(writer, bodyParameters, null, encoding);
- }
- protected override object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, string action, MessageDescription messageDescription, object[] parameters, bool isRequest)
- {
- MessageInfo messageInfo;
- if (isRequest)
- messageInfo = requestMessageInfo;
- else
- messageInfo = replyMessageInfo;
- if (messageInfo.RpcEncodedTypedMessageBodyParts == null)
- return DeserializeBody(reader, version, messageInfo.BodySerializer, messageDescription.Body.ReturnValue, messageDescription.Body.Parts, parameters, isRequest);
- object[] bodyPartValues = new object[messageInfo.RpcEncodedTypedMessageBodyParts.Count];
- DeserializeBody(reader, version, messageInfo.BodySerializer, null/*returnPart*/, messageInfo.RpcEncodedTypedMessageBodyParts, bodyPartValues, isRequest);
- object bodyObject = Activator.CreateInstance(messageDescription.Body.Parts[0].Type);
- int i = 0;
- foreach (MessagePartDescription bodyPart in messageInfo.RpcEncodedTypedMessageBodyParts)
- {
- MemberInfo member = bodyPart.MemberInfo;
- FieldInfo field = member as FieldInfo;
- if (field != null)
- field.SetValue(bodyObject, bodyPartValues[i++]);
- else
- {
- PropertyInfo property = member as PropertyInfo;
- if (property != null)
- property.SetValue(bodyObject, bodyPartValues[i++], null);
- }
- }
- parameters[messageDescription.Body.Parts[0].Index] = bodyObject;
- return null;
- }
- object DeserializeBody(XmlDictionaryReader reader, MessageVersion version, XmlSerializer serializer, MessagePartDescription returnPart, MessagePartDescriptionCollection bodyParts, object[] parameters, bool isRequest)
- {
- try
- {
- if (reader == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("reader"));
- if (parameters == null) throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentNullException("parameters"));
- object returnValue = null;
- if (serializer == null)
- {
- return null;
- }
- if (reader.NodeType == XmlNodeType.EndElement)
- return null;
- object[] bodyParameters = (object[])serializer.Deserialize(reader, isEncoded ? GetEncoding(version.Envelope) : null);
- int paramIndex = 0;
- if (IsValidReturnValue(returnPart))
- returnValue = bodyParameters[paramIndex++];
- for (int i = 0; i < bodyParts.Count; i++)
- parameters[bodyParts[i].Index] = bodyParameters[paramIndex++];
- return returnValue;
- }
- catch (InvalidOperationException e)
- {
- // all exceptions from XmlSerializer get wrapped in InvalidOperationException,
- // so we must be conservative and never turn this into a fault
- string resourceKey = isRequest ? SR.SFxErrorDeserializingRequestBody : SR.SFxErrorDeserializingReplyBody;
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(
- SR.GetString(resourceKey, OperationName), e));
- }
- }
- internal static string GetEncoding(EnvelopeVersion version)
- {
- if (version == EnvelopeVersion.Soap11)
- {
- return soap11Encoding;
- }
- else if (version == EnvelopeVersion.Soap12)
- {
- return soap12Encoding;
- }
- else
- {
- throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("version",
- SR.GetString(SR.EnvelopeVersionNotSupported, version));
- }
- }
- internal abstract class MessageInfo
- {
- internal abstract XmlSerializer BodySerializer { get; }
- internal abstract XmlSerializer HeaderSerializer { get; }
- internal abstract MessageHeaderDescriptionTable HeaderDescriptionTable { get; }
- internal abstract MessageHeaderDescription UnknownHeaderDescription { get; }
- internal abstract MessagePartDescriptionCollection RpcEncodedTypedMessageBodyParts { get; }
- }
- class MessageHeaderOfTHelper
- {
- object[] attributes;
- internal MessageHeaderOfTHelper(int parameterCount)
- {
- attributes = new object[parameterCount];
- }
- internal object GetContentAndSaveHeaderAttributes(object parameterValue, MessageHeaderDescription headerDescription)
- {
- if (parameterValue == null)
- return null;
- bool mustUnderstand;
- bool relay;
- string actor;
- if (headerDescription.Multiple)
- {
- object[] messageHeaderOfTArray = (object[])parameterValue;
- MessageHeader<object>[] messageHeaderOfTAttributes = new MessageHeader<object>[messageHeaderOfTArray.Length];
- Array tArray = Array.CreateInstance(headerDescription.Type, messageHeaderOfTArray.Length);
- for (int i = 0; i < tArray.Length; i++)
- {
- tArray.SetValue(GetContentOfMessageHeaderOfT(headerDescription, messageHeaderOfTArray[i], out mustUnderstand, out relay, out actor), i);
- messageHeaderOfTAttributes[i] = new MessageHeader<object>(null, mustUnderstand, actor, relay);
- }
- attributes[headerDescription.Index] = messageHeaderOfTAttributes;
- return tArray;
- }
- else
- {
- object content = GetContentOfMessageHeaderOfT(headerDescription, parameterValue, out mustUnderstand, out relay, out actor);
- attributes[headerDescription.Index] = new MessageHeader<object>(null, mustUnderstand, actor, relay);
- return content;
- }
- }
- internal void GetHeaderAttributes(MessageHeaderDescription headerDescription, out bool mustUnderstand, out bool relay, out string actor)
- {
- MessageHeader<object> matchingMessageHeaderOfTAttribute = null;
- if (headerDescription.Multiple)
- {
- MessageHeader<object>[] messageHeaderOfTAttributes = (MessageHeader<object>[])attributes[headerDescription.Index];
- for (int i = 0; i < messageHeaderOfTAttributes.Length; i++)
- {
- if (messageHeaderOfTAttributes[i] != null)
- {
- matchingMessageHeaderOfTAttribute = messageHeaderOfTAttributes[i];
- messageHeaderOfTAttributes[i] = null;
- break;
- }
- }
- //assert(matchingMessageHeaderOfTAttribute != null);
- }
- else
- matchingMessageHeaderOfTAttribute = (MessageHeader<object>)attributes[headerDescription.Index];
- mustUnderstand = matchingMessageHeaderOfTAttribute.MustUnderstand;
- relay = matchingMessageHeaderOfTAttribute.Relay;
- actor = matchingMessageHeaderOfTAttribute.Actor;
- }
- internal void SetHeaderAttributes(MessageHeaderDescription headerDescription, bool mustUnderstand, bool relay, string actor)
- {
- if (headerDescription.Multiple)
- {
- if (attributes[headerDescription.Index] == null)
- attributes[headerDescription.Index] = new List<MessageHeader<object>>();
- ((List<MessageHeader<object>>)attributes[headerDescription.Index]).Add(new MessageHeader<object>(null, mustUnderstand, actor, relay));
- }
- else
- attributes[headerDescription.Index] = new MessageHeader<object>(null, mustUnderstand, actor, relay);
- }
- internal object CreateMessageHeader(MessageHeaderDescription headerDescription, object headerValue)
- {
- if (headerDescription.Multiple)
- {
- IList<MessageHeader<object>> messageHeaderOfTAttributes = (IList<MessageHeader<object>>)attributes[headerDescription.Index];
- object[] messageHeaderOfTArray = (object[])Array.CreateInstance(TypedHeaderManager.GetMessageHeaderType(headerDescription.Type), messageHeaderOfTAttributes.Count);
- Array headerValues = (Array)headerValue;
- for (int i = 0; i < messageHeaderOfTArray.Length; i++)
- {
- MessageHeader<object> messageHeaderOfTAttribute = messageHeaderOfTAttributes[i];
- messageHeaderOfTArray[i] = TypedHeaderManager.Create(headerDescription.Type, headerValues.GetValue(i),
- messageHeaderOfTAttribute.MustUnderstand, messageHeaderOfTAttribute.Relay, messageHeaderOfTAttribute.Actor);
- }
- return messageHeaderOfTArray;
- }
- else
- {
- MessageHeader<object> messageHeaderOfTAttribute = (MessageHeader<object>)attributes[headerDescription.Index];
- return TypedHeaderManager.Create(headerDescription.Type, headerValue,
- messageHeaderOfTAttribute.MustUnderstand, messageHeaderOfTAttribute.Relay, messageHeaderOfTAttribute.Actor);
- }
- }
- }
- }
- }
|