none
How to read a message sent via NetMsmqBinding into a queue with a service using the MsmqIntegrationBinding? RRS feed

  • Question

  • Hi,

    I have a service-client that uses WCF/NetMsmqBinding end-to-end between client and service.  What I am trying to do is setup an additional service that monitors the dead-letter queue in a very generic way (i.e. when a message is put in the dead-letter queue the service pulls it and logs the message body/destination/etc somewhere) without attempting to deserialize it into a SOAP format or data contracts.

    My thought was that for that service I could a simple setup using the MsmqIntegrationBinding and the System.Messaging.Message class to pull a message out of the dead-letter queue and log it.

        /// <summary>
        /// IMsmqDeadLetterService interface.
        /// </summary>
        [ServiceContract(SessionMode = SessionMode.NotAllowed)]
        public interface IMsmqDeadLetterService
        {
            /// <summary>
            /// Handles the message.
            /// </summary>
            /// <param name="deadLetter">The dead letter.</param>
            [OperationContract(IsOneWay = true)]
            void HandleMessage(MsmqMessage<Message> deadLetter);
        }

    When I do this I get the following error, which I don't really understand how to deal with:

    <Exception>
    <ExceptionType>System.ServiceModel.MsmqPoisonMessageException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
    <Message>The transport channel detected a poison message. This occurred because the message exceeded the maximum number of delivery attempts or because the channel detected a fundamental problem with the message. The inner exception may contain additional information.</Message>
    <StackTrace>
    at System.ServiceModel.Channels.MsmqChannelListenerBase.NormalizePoisonException(Int64 lookupId, Exception innerException)
    at System.ServiceModel.Channels.MsmqDecodeHelper.DecodeIntegrationDatagram(MsmqIntegrationChannelListener listener, MsmqReceiveHelper receiver, MsmqIntegrationInputMessage msmqMessage, MsmqMessageProperty messageProperty)
    at System.ServiceModel.MsmqIntegration.MsmqIntegrationInputChannel.DecodeMsmqMessage(MsmqInputMessage msmqMessage, MsmqMessageProperty property)
    at System.ServiceModel.Channels.MsmqInputChannelBase.TryReceive(TimeSpan timeout, Message&amp; message)
    at System.ServiceModel.Dispatcher.InputChannelBinder.TryReceive(TimeSpan timeout, RequestContext&amp; requestContext)
    at System.ServiceModel.Dispatcher.ErrorHandlingReceiver.TryReceive(TimeSpan timeout, RequestContext&amp; requestContext)
    at System.ServiceModel.Dispatcher.ChannelHandler.TryReceive(TimeSpan timeout, RequestContext&amp; requestContext)
    at System.ServiceModel.Dispatcher.ChannelHandler.SyncMessagePump()
    at System.ServiceModel.Dispatcher.ChannelHandler.OnStartSyncMessagePump(Object state)
    at System.Runtime.ActionItem.DefaultActionItem.TraceAndInvoke()
    at System.Runtime.ActionItem.CallbackHelper.InvokeWithoutContext(Object state)
    at System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
    at System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
    at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
    </StackTrace>
    <ExceptionString>System.ServiceModel.MsmqPoisonMessageException: The transport channel detected a poison message. This occurred because the message exceeded the maximum number of delivery attempts or because the channel detected a fundamental problem with the message. The inner exception may contain additional information. ---&gt; System.ServiceModel.ProtocolException: An error was encountered while deserializing the message. The message cannot be received. ---&gt; System.Runtime.Serialization.SerializationException: An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.
       at System.ServiceModel.Channels.MsmqDecodeHelper.XmlDeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream stream, Int64 lookupId)
       at System.ServiceModel.Channels.MsmqDecodeHelper.DeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream bodyStream, MsmqIntegrationMessageProperty property, Int64 lookupId)
       at System.ServiceModel.Channels.MsmqDecodeHelper.DecodeIntegrationDatagram(MsmqIntegrationChannelListener listener, MsmqReceiveHelper receiver, MsmqIntegrationInputMessage msmqMessage, MsmqMessageProperty messageProperty)
       --- End of inner exception stack trace ---
       --- End of inner exception stack trace ---</ExceptionString>
    <InnerException>
    <ExceptionType>System.ServiceModel.ProtocolException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
    <Message>An error was encountered while deserializing the message. The message cannot be received.</Message>
    <StackTrace>
    at System.ServiceModel.Channels.MsmqChannelListenerBase.NormalizePoisonException(Int64 lookupId, Exception innerException)
    at System.ServiceModel.Channels.MsmqDecodeHelper.DecodeIntegrationDatagram(MsmqIntegrationChannelListener listener, MsmqReceiveHelper receiver, MsmqIntegrationInputMessage msmqMessage, MsmqMessageProperty messageProperty)
    at System.ServiceModel.MsmqIntegration.MsmqIntegrationInputChannel.DecodeMsmqMessage(MsmqInputMessage msmqMessage, MsmqMessageProperty property)
    at System.ServiceModel.Channels.MsmqInputChannelBase.TryReceive(TimeSpan timeout, Message&amp; message)
    at System.ServiceModel.Dispatcher.InputChannelBinder.TryReceive(TimeSpan timeout, RequestContext&amp; requestContext)
    at System.ServiceModel.Dispatcher.ErrorHandlingReceiver.TryReceive(TimeSpan timeout, RequestContext&amp; requestContext)
    at System.ServiceModel.Dispatcher.ChannelHandler.TryReceive(TimeSpan timeout, RequestContext&amp; requestContext)
    at System.ServiceModel.Dispatcher.ChannelHandler.SyncMessagePump()
    at System.ServiceModel.Dispatcher.ChannelHandler.OnStartSyncMessagePump(Object state)
    at System.Runtime.ActionItem.DefaultActionItem.TraceAndInvoke()
    at System.Runtime.ActionItem.CallbackHelper.InvokeWithoutContext(Object state)
    at System.Runtime.IOThreadScheduler.ScheduledOverlapped.IOCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* nativeOverlapped)
    at System.Runtime.Fx.IOCompletionThunk.UnhandledExceptionFrame(UInt32 error, UInt32 bytesRead, NativeOverlapped* nativeOverlapped)
    at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)
    </StackTrace>
    <ExceptionString>System.ServiceModel.ProtocolException: An error was encountered while deserializing the message. The message cannot be received. ---&gt; System.Runtime.Serialization.SerializationException: An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.
       at System.ServiceModel.Channels.MsmqDecodeHelper.XmlDeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream stream, Int64 lookupId)
       at System.ServiceModel.Channels.MsmqDecodeHelper.DeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream bodyStream, MsmqIntegrationMessageProperty property, Int64 lookupId)
       at System.ServiceModel.Channels.MsmqDecodeHelper.DecodeIntegrationDatagram(MsmqIntegrationChannelListener listener, MsmqReceiveHelper receiver, MsmqIntegrationInputMessage msmqMessage, MsmqMessageProperty messageProperty)
       --- End of inner exception stack trace ---</ExceptionString>
    <InnerException>
    <ExceptionType>System.Runtime.Serialization.SerializationException, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
    <Message>An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.</Message>
    <StackTrace>
    at System.ServiceModel.Channels.MsmqDecodeHelper.XmlDeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream stream, Int64 lookupId)
    at System.ServiceModel.Channels.MsmqDecodeHelper.DeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream bodyStream, MsmqIntegrationMessageProperty property, Int64 lookupId)
    at System.ServiceModel.Channels.MsmqDecodeHelper.DecodeIntegrationDatagram(MsmqIntegrationChannelListener listener, MsmqReceiveHelper receiver, MsmqIntegrationInputMessage msmqMessage, MsmqMessageProperty messageProperty)
    </StackTrace>
    <ExceptionString>System.Runtime.Serialization.SerializationException: An error occurred while deserializing an MSMQ message's XML body. The message cannot be received. Ensure that the service contract is decorated with appropriate [ServiceKnownType] attributes or the TargetSerializationTypes property is set on the MsmqIntegrationBindingElement.
       at System.ServiceModel.Channels.MsmqDecodeHelper.XmlDeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream stream, Int64 lookupId)
       at System.ServiceModel.Channels.MsmqDecodeHelper.DeserializeForIntegration(MsmqIntegrationChannelListener listener, Stream bodyStream, MsmqIntegrationMessageProperty property, Int64 lookupId)
       at System.ServiceModel.Channels.MsmqDecodeHelper.DecodeIntegrationDatagram(MsmqIntegrationChannelListener listener, MsmqReceiveHelper receiver, MsmqIntegrationInputMessage msmqMessage, MsmqMessageProperty messageProperty)</ExceptionString>
    </InnerException>
    </InnerException>
    </Exception>

    It says that there is an issue deserializing the message and that I should decorate the contract with known types, but that's what I am trying to avoid doing by using the System.Messaging.Message class, so that its just a generic message that I don't have to know the types with.

    Here is the binding configurations I setup for the MSMQ integration binding.

      <msmqIntegrationBinding>
        <binding name="NetMsmqIntegrationBinding"
                 closeTimeout="00:00:10"
                 openTimeout="00:00:20"
                 receiveTimeout="00:00:30"
                 sendTimeout="00:00:40"
                 deadLetterQueue="System"
                 durable="true"
                 exactlyOnce="true"
                 maxReceivedMessageSize="2147483646"
                 maxRetryCycles="12"
                 retryCycleDelay="00:05:55"
                 timeToLive="00:11:11"
                 useSourceJournal="false"
                 useMsmqTracing="false"
                 serializationFormat="Xml">
          <security mode="None" />
        </binding>
      </msmqIntegrationBinding>

    Thanks!


    • Edited by BotHead Thursday, July 18, 2013 2:15 PM
    Thursday, July 18, 2013 2:14 PM

Answers

  • Follow up on the solution I will most likely have to use to get the native MSMQ information I want, which is also from the source link I posted in my previous solution... and uses native MSMQ.

    Sample code to get all the messages in a queue, and log them out, which were all generated by a WCF client.

                var queue = new System.Messaging.MessageQueue(@".\private$\deadletter");
                queue.MessageReadPropertyFilter = new MessagePropertyFilter();
                var props = queue.MessageReadPropertyFilter.GetType().GetProperties();
                foreach (var prop in props)
                {
                    try
                    {
                        prop.SetValue(queue.MessageReadPropertyFilter, true, null);
                    }
                    catch { }
                }
    
                var messages = queue.GetAllMessages();
                foreach (var message in messages)
                {
                    var xmlDoc = Msmq.ConvertToXMLDoc(message);
    
                    using (var stringWriter = new System.IO.StringWriter())
                    using (var xmlTextWriter = System.Xml.XmlWriter.Create(stringWriter))
                    {
                        xmlDoc.WriteTo(xmlTextWriter);
                        xmlTextWriter.Flush();
                        logger.Error(stringWriter.GetStringBuilder().ToString());
                    }
                }

    Updated versions of the code finds the envelope start with some actual references to the framing protocol.

            /// <summary>
            /// Converts the MSMQ binary encoded WCF message to XML doc.
            /// </summary>
            /// <param name="msg">The binary encoded message.</param>
            /// <returns>Returns the SOAP message as an XML document.</returns>
            public static System.Xml.XmlDocument ConvertMessageToXMLDoc(System.Messaging.Message msg)
            {
                // Read the body of the MSMQ message.
                byte[] buffer = new byte[msg.BodyStream.Length];
                msg.BodyStream.Read(buffer, 0, (int)msg.BodyStream.Length);
    
                // Find the start of the encoded SOAP envelope.
                int envelopeStart = FindEnvolopeStart(buffer);
                System.IO.MemoryStream stream = new System.IO.MemoryStream(buffer, envelopeStart, buffer.Length - envelopeStart);
    
                // Decode the SOAP envelope using the WCF encoding element.
                System.ServiceModel.Channels.BinaryMessageEncodingBindingElement elm = new System.ServiceModel.Channels.BinaryMessageEncodingBindingElement();
                System.ServiceModel.Channels.Message msg1 = elm.CreateMessageEncoderFactory().Encoder.ReadMessage(stream, Int32.MaxValue);
    
                // Read the SOAP envelope into the XML document and return it.
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.Load(msg1.GetReaderAtBodyContents());
                msg.BodyStream.Position = 0;
                return doc;
            }
    
            /// <summary>
            /// Finds the envolope start in framed binary MSMQ message encoded by WCF.
            /// </summary>
            /// <param name="stream">The stream.</param>
            /// <returns>Returns the envelope start or -1 if not found.</returns>
            /// <remarks>
            /// <para>
            /// This algorithm is based on the .NET Message Framing Protocol...
            /// </para>
            /// <para>
            /// .NET Message Framing Protocol (http://msdn.microsoft.com/en-us/library/cc219293.aspx)
            /// </para>
            /// <para>
            /// .NET Message Framing MSMQ Binding Protocol (http://msdn.microsoft.com/en-us/library/ff469340.aspx)
            /// </para>
            /// <para>
            /// .NET Binary Format: SOAP Data Structure (http://msdn.microsoft.com/en-us/library/cc219175.aspx)
            /// </para>
            /// </remarks>
            private static int FindEnvolopeStart(byte[] stream)
            {
                // ---------------------------------------------------------------------------------------
                // Known Encoding Record (http://msdn.microsoft.com/en-us/library/cc219326.aspx)
                // ---------------------------------------------------------------------------------------
                //
                // 0x03 is the default value for known encoding record, which is the binary encoding type.
                // 0x07 is the value for a binary encoding type.
                //
                byte knownEncodingRecordType = (byte)0x03;
                byte binaryEncodingType = (byte)0x07;
    
                // -----------------------------------------------------------------------------------------------
                // DictionaryString (http://msdn.microsoft.com/en-us/library/cc219187.aspx)
                // Structure Examples (http://msdn.microsoft.com/en-us/library/cc219188.aspx)
                // -----------------------------------------------------------------------------------------------
                // 
                // 0x56 is the value for a CanonicalizationMethod, and when the envelope is encode the "<s:Envelope"
                // part of the envelope is encoded as 0x56 0x02, where 0x02 is the envelope.
                //
                byte canonicalizationMethod = (byte)0x56;
                byte envelopeStart = (byte)0x02;
    
                // Now find the envelope in the encoded binary message based on our
                // known reference points that we have just defined.
                int i = 0;
                byte prevByte = stream[i];
                byte curByte = (byte)0;
                bool foundEnvelopeStart = false;
                for (i = 0; i < stream.Length; i++)
                {
                    curByte = stream[i];
                    if (curByte == envelopeStart &&
                        prevByte == canonicalizationMethod)
                    {
                        // We found what we think is the start of the envelope,
                        // lets make sure that the known record is there that
                        // specifies the binary encoding type just to make sure.
                        // The known encoding record should be 2 positions back.
                        int indexCheck = i - 2;
                        if (indexCheck > 1)
                        {
                            if (stream[indexCheck] == binaryEncodingType &&
                                stream[indexCheck - 1] == knownEncodingRecordType)
                            {
                                foundEnvelopeStart = true;
                                break;
                            }
                        }
                    }
    
                    prevByte = curByte;
                }
    
                return foundEnvelopeStart ? (i - 1) : -1;
            }
    Enjoy!
    • Marked as answer by BotHead Friday, July 19, 2013 7:25 PM
    • Edited by BotHead Monday, July 22, 2013 7:21 PM Updated
    Friday, July 19, 2013 7:24 PM

All replies

  • Ok... so here is the solution that I used to monitor a dead-letter queue without having to use the specific service contracts used to write into that queue (source of how I did this is here http://www.netframeworkdev.com/windows-communication-foundation/how-to-read-the-body-of-a-wcf-netmsmqbinding-bided-message-from-a-non-wcf-app-67734.shtml).

    Service contract with one message:

            /// <summary>
            /// Processes the message.
            /// </summary>
            /// <param name="msg">The MSG.</param>
            [OperationContract(IsOneWay = true, Action = "*")]
            void ProcessMessage(Message msg);

     Service implementation with one message:

            /// <summary>
            /// Processes the message.
            /// </summary>
            /// <param name="msg">The MSG.</param>
            [OperationBehavior(TransactionAutoComplete = true, TransactionScopeRequired = true)]
            public void ProcessMessage(Message msg)
            {
                logger.Error("Got message {0}", msg.ToString());
            }

    So far pretty easy... now you need to create a filtering extension that allows any message to any address to hit your single message.

        /// <summary>
        /// Class FilteringEndpointBehavior
        /// </summary>
        public class FilteringEndpointBehavior : IEndpointBehavior
        {
            #region Fields
            
            /// <summary>
            /// Address filter
            /// </summary>
            MessageFilter addressFilter;
            
            /// <summary>
            /// Contract filter
            /// </summary>
            MessageFilter contractFilter;
            
            #endregion
    
            #region Constructors
            /// <summary>
            /// Initializes a new instance of the <see cref="FilteringEndpointBehavior"/> class.
            /// </summary>
            /// <param name="addressFilter">The address filter.</param>
            /// <param name="contractFilter">The contract filter.</param>
            public FilteringEndpointBehavior(MessageFilter addressFilter, MessageFilter contractFilter)
            {
                this.addressFilter = addressFilter;
                this.contractFilter = contractFilter;
            }
            // Other constructors go here
            #endregion
    
            #region <Enums>
            // Enumerations go here
            #endregion
    
            #region <Events>
            // Events are defined here
            #endregion
    
            #region <Properties>
            // Properties are defined here
            #endregion
    
            #region Public Methods
    
            /// <summary>
            /// Implement to pass data at runtime to bindings to support custom behavior.
            /// </summary>
            /// <param name="endpoint">The endpoint to modify.</param>
            /// <param name="bindingParameters">The objects that binding elements require to support the behavior.</param>
            public void AddBindingParameters(ServiceEndpoint endpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
            {
            }
    
            /// <summary>
            /// Implements a modification or extension of the client across an endpoint.
            /// </summary>
            /// <param name="endpoint">The endpoint that is to be customized.</param>
            /// <param name="clientRuntime">The client runtime to be customized.</param>
            public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
            {
                throw new InvalidOperationException("This behavior should only be used on the server.");
            }
    
            /// <summary>
            /// Implements a modification or extension of the service across an endpoint.
            /// </summary>
            /// <param name="endpoint">The endpoint that exposes the contract.</param>
            /// <param name="endpointDispatcher">The endpoint dispatcher to be modified or extended.</param>
            public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
            {
                endpointDispatcher.AddressFilter = this.addressFilter;
                endpointDispatcher.ContractFilter = this.contractFilter;
            }
    
            /// <summary>
            /// Implement to confirm that the endpoint meets some intended criteria.
            /// </summary>
            /// <param name="endpoint">The endpoint to validate.</param>
            public void Validate(ServiceEndpoint endpoint)
            {
            }
    
            #endregion
    
            #region <Private Methods>
            // Private methods are defined here
            #endregion
        }


        /// <summary>
        /// Class FilteringEndpointBehaviorExtension
        /// </summary>
        public class FilteringEndpointBehaviorExtension : BehaviorExtensionElement
        {
            #region Fields
    
            /// <summary>
            /// Filter extension name
            /// </summary>
            public const string ExtensionName = "filteringEndpointBehaviorExtension";
    
            #endregion
    
            #region Constructors
            /// <summary>
            /// Initializes a new instance of the FilteringEndpointBehaviorExtension class with no
            /// parameters.
            /// </summary>
            public FilteringEndpointBehaviorExtension()
            {
            }
            // Other constructors go here
            #endregion
    
            #region <Enums>
            // Enumerations go here
            #endregion
    
            #region <Events>
            // Events are defined here
            #endregion
    
            #region <Properties>
            // Properties are defined here
            #endregion
    
            #region Public Methods
    
            /// <summary>
            /// Creates the behavior.
            /// </summary>
            /// <returns></returns>
            protected override object CreateBehavior()
            {
                return new FilteringEndpointBehavior(new MatchAllAdddressMessageFilter(), new MatchAllMessageFilter());
            }
    
            /// <summary>
            /// Gets the type of the behavior.
            /// </summary>
            /// <value>
            /// The type of the behavior.
            /// </value>
            public override Type BehaviorType
            {
                get { return typeof(FilteringEndpointBehavior); }
            }
    
            #endregion
    
            #region <Private Methods>
            // Private methods are defined here
            #endregion


        /// <summary>
        /// Class MatchAllAdddressMessageFilter
        /// </summary>
        public class MatchAllAdddressMessageFilter : MessageFilter
        {
            #region <Fields>
            // All member variables go here
            #endregion
    
            #region Constructors
            /// <summary>
            /// Initializes a new instance of the MatchAllAdddressMessageFilter class with no
            /// parameters.
            /// </summary>
            public MatchAllAdddressMessageFilter()
            {
            }
            // Other constructors go here
            #endregion
    
            #region <Enums>
            // Enumerations go here
            #endregion
    
            #region <Events>
            // Events are defined here
            #endregion
    
            #region <Properties>
            // Properties are defined here
            #endregion
    
            #region Public Methods
    
            /// <summary>
            /// When overridden in a derived class, tests whether a buffered message satisfies the criteria of a filter.
            /// </summary>
            /// <param name="buffer">The <see cref="T:System.ServiceModel.Channels.MessageBuffer"/> object to test.</param>
            /// <returns>
            /// true if the <see cref="T:System.ServiceModel.Channels.MessageBuffer"/> object satisfies the filter criteria; otherwise, false.
            /// </returns>
            public override bool Match(MessageBuffer buffer)
            {
                return Match(buffer.CreateMessage());
            }
    
            /// <summary>
            /// When overridden in a derived class, tests whether a message satisfies the filter criteria. The body cannot be examined.
            /// </summary>
            /// <param name="message">The <see cref="T:System.ServiceModel.Channels.Message"/> object to test.</param>
            /// <returns>
            /// true if the <see cref="T:System.ServiceModel.Channels.Message"/> object satisfies the filter criteria; otherwise, false.
            /// </returns>
            public override bool Match(Message message)
            {
                return true;
            }
    
            #endregion
    
            #region <Private Methods>
            // Private methods are defined here
            #endregion
        }

    Now tied it all together with configuration...

      <endpointBehaviors>
        <behavior name="EndpointBehavior">
          <filteringEndpointBehavior />
        </behavior>
      </endpointBehaviors>


        <extensions>
          <behaviorExtensions>
            <add name="filteringEndpointBehavior" type="FilteringEndpointBehaviorExtension, YourDLL, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          </behaviorExtensions>
        </extensions>


      <service name="DeadLetterService"
           behaviorConfiguration="ServiceBehavior">
        <endpoint address="net.msmq://localhost/private/deadletter"
                  binding="netMsmqBinding"
                  bindingConfiguration="NetMsmqBinding"
                  contract="IDeadLetterService"
                  behaviorConfiguration="EndpointBehavior"/>
        <endpoint address="http://localhost:8460/DeadLetterService/mex"
                  binding="mexHttpBinding"
                  contract="IMetadataExchange" />
      </service>

    Run it... and you will get a readable SOAP message in your logging statement!

    I am going to call this a partial answer at the moment... while with this solution you get a nice logging statement with the SOAP message you don't get any of the actual MSMQ related information (i.e. why it ended up in the dead-letter queue, where it was going, etc...) which I would really like to also have in the final solution, which is what native MSMQ or the MSMQ integration binding give you, but you need to figure out a way to decode the MSMQ message contents, which the solution I have posted above does for you... More to come when I figure that part out...

    • Marked as answer by BotHead Friday, July 19, 2013 6:06 PM
    • Edited by BotHead Friday, July 19, 2013 6:45 PM New information.
    • Unmarked as answer by BotHead Friday, July 19, 2013 6:45 PM
    Friday, July 19, 2013 6:04 PM
  • Follow up on the solution I will most likely have to use to get the native MSMQ information I want, which is also from the source link I posted in my previous solution... and uses native MSMQ.

    Sample code to get all the messages in a queue, and log them out, which were all generated by a WCF client.

                var queue = new System.Messaging.MessageQueue(@".\private$\deadletter");
                queue.MessageReadPropertyFilter = new MessagePropertyFilter();
                var props = queue.MessageReadPropertyFilter.GetType().GetProperties();
                foreach (var prop in props)
                {
                    try
                    {
                        prop.SetValue(queue.MessageReadPropertyFilter, true, null);
                    }
                    catch { }
                }
    
                var messages = queue.GetAllMessages();
                foreach (var message in messages)
                {
                    var xmlDoc = Msmq.ConvertToXMLDoc(message);
    
                    using (var stringWriter = new System.IO.StringWriter())
                    using (var xmlTextWriter = System.Xml.XmlWriter.Create(stringWriter))
                    {
                        xmlDoc.WriteTo(xmlTextWriter);
                        xmlTextWriter.Flush();
                        logger.Error(stringWriter.GetStringBuilder().ToString());
                    }
                }

    Updated versions of the code finds the envelope start with some actual references to the framing protocol.

            /// <summary>
            /// Converts the MSMQ binary encoded WCF message to XML doc.
            /// </summary>
            /// <param name="msg">The binary encoded message.</param>
            /// <returns>Returns the SOAP message as an XML document.</returns>
            public static System.Xml.XmlDocument ConvertMessageToXMLDoc(System.Messaging.Message msg)
            {
                // Read the body of the MSMQ message.
                byte[] buffer = new byte[msg.BodyStream.Length];
                msg.BodyStream.Read(buffer, 0, (int)msg.BodyStream.Length);
    
                // Find the start of the encoded SOAP envelope.
                int envelopeStart = FindEnvolopeStart(buffer);
                System.IO.MemoryStream stream = new System.IO.MemoryStream(buffer, envelopeStart, buffer.Length - envelopeStart);
    
                // Decode the SOAP envelope using the WCF encoding element.
                System.ServiceModel.Channels.BinaryMessageEncodingBindingElement elm = new System.ServiceModel.Channels.BinaryMessageEncodingBindingElement();
                System.ServiceModel.Channels.Message msg1 = elm.CreateMessageEncoderFactory().Encoder.ReadMessage(stream, Int32.MaxValue);
    
                // Read the SOAP envelope into the XML document and return it.
                System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
                doc.Load(msg1.GetReaderAtBodyContents());
                msg.BodyStream.Position = 0;
                return doc;
            }
    
            /// <summary>
            /// Finds the envolope start in framed binary MSMQ message encoded by WCF.
            /// </summary>
            /// <param name="stream">The stream.</param>
            /// <returns>Returns the envelope start or -1 if not found.</returns>
            /// <remarks>
            /// <para>
            /// This algorithm is based on the .NET Message Framing Protocol...
            /// </para>
            /// <para>
            /// .NET Message Framing Protocol (http://msdn.microsoft.com/en-us/library/cc219293.aspx)
            /// </para>
            /// <para>
            /// .NET Message Framing MSMQ Binding Protocol (http://msdn.microsoft.com/en-us/library/ff469340.aspx)
            /// </para>
            /// <para>
            /// .NET Binary Format: SOAP Data Structure (http://msdn.microsoft.com/en-us/library/cc219175.aspx)
            /// </para>
            /// </remarks>
            private static int FindEnvolopeStart(byte[] stream)
            {
                // ---------------------------------------------------------------------------------------
                // Known Encoding Record (http://msdn.microsoft.com/en-us/library/cc219326.aspx)
                // ---------------------------------------------------------------------------------------
                //
                // 0x03 is the default value for known encoding record, which is the binary encoding type.
                // 0x07 is the value for a binary encoding type.
                //
                byte knownEncodingRecordType = (byte)0x03;
                byte binaryEncodingType = (byte)0x07;
    
                // -----------------------------------------------------------------------------------------------
                // DictionaryString (http://msdn.microsoft.com/en-us/library/cc219187.aspx)
                // Structure Examples (http://msdn.microsoft.com/en-us/library/cc219188.aspx)
                // -----------------------------------------------------------------------------------------------
                // 
                // 0x56 is the value for a CanonicalizationMethod, and when the envelope is encode the "<s:Envelope"
                // part of the envelope is encoded as 0x56 0x02, where 0x02 is the envelope.
                //
                byte canonicalizationMethod = (byte)0x56;
                byte envelopeStart = (byte)0x02;
    
                // Now find the envelope in the encoded binary message based on our
                // known reference points that we have just defined.
                int i = 0;
                byte prevByte = stream[i];
                byte curByte = (byte)0;
                bool foundEnvelopeStart = false;
                for (i = 0; i < stream.Length; i++)
                {
                    curByte = stream[i];
                    if (curByte == envelopeStart &&
                        prevByte == canonicalizationMethod)
                    {
                        // We found what we think is the start of the envelope,
                        // lets make sure that the known record is there that
                        // specifies the binary encoding type just to make sure.
                        // The known encoding record should be 2 positions back.
                        int indexCheck = i - 2;
                        if (indexCheck > 1)
                        {
                            if (stream[indexCheck] == binaryEncodingType &&
                                stream[indexCheck - 1] == knownEncodingRecordType)
                            {
                                foundEnvelopeStart = true;
                                break;
                            }
                        }
                    }
    
                    prevByte = curByte;
                }
    
                return foundEnvelopeStart ? (i - 1) : -1;
            }
    Enjoy!
    • Marked as answer by BotHead Friday, July 19, 2013 7:25 PM
    • Edited by BotHead Monday, July 22, 2013 7:21 PM Updated
    Friday, July 19, 2013 7:24 PM
  • Hi,

    I am very glad that you have solved your problem by yourself.

    If you have any other problem, welcome to post it in the wcf forums.

    Best Regards.


    Amy Peng
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.






    Saturday, July 20, 2013 12:37 AM
    Moderator