Read WCF MSMQ Message from C#

1k Views Asked by At

I used a WCF client to send msmq message to WCF Service, before WCF Service process the msmq message, I want to check the body of the message.
After checking the content of msmq message body, I got below result.
enter image description here
But, I failed to get the exact string.
Below is definition of WCF Service

    [ServiceContract]
public interface IMSMQService
{

    [OperationContract(IsOneWay = true)]       

    void ShowMessage(string msg);
}

I used below method to retrieve message body, and it output empty, if I add watch on ms, I could get

"\0\u0001\0\u0001\u0004\u0002(net.msmq://vdi-v-tazho/private/TestQueue\u0003\aV\u0002\v\u0001s\u0004\v\u0001a\u0006V\bD\n\u001e\0��+http://tempuri.org/IMSMQService/ShowMessage@\u0017VsDebuggerCausalityData\bAhttp://schemas.microsoft.com/vstudio/diagnostics/servicemodelsink�<��ϣ-lN��FoJ�0�u\u0006�\"\0\0\0\0\u0017�\0i8�C�I\7�^Q�A\u0012�w}\f�A�\u000f\rޮ�pe\0\t\0\0D\f\u001e\0��(net.msmq://vdi-v-tazho/private/TestQueue\u0001V\u000e@\vShowMessage\b\u0013http://tempuri.org/@\u0003msg�\u0004test\u0001\u0001\u0001"

                //message.Formatter = new XmlMessageFormatter(new String[] { });
            //StreamReader sr = new StreamReader(message.BodyStream);
            string ms = "";
            //while (sr.Peek() >= 0)
            //{
            //    ms += sr.ReadLine();
            //}
            message.Formatter = new ActiveXMessageFormatter();
            string result = System.Text.Encoding.UTF8.GetString(message.Body as byte[]);
            StreamReader reader = new StreamReader(message.BodyStream);

            ms = reader.ReadToEnd();
            MessageBox.Show(ms);
3

There are 3 best solutions below

0
On BEST ANSWER

I got below code to achieve my requirement.

private void MSMQStringBody_Click(object sender, EventArgs e)
{
    System.Messaging.MessageQueue[] queueList = System.Messaging.MessageQueue.GetPrivateQueuesByMachine(Environment.MachineName);
    MessageQueue myQueue = queueList[1];
    List<System.Messaging.Message> messages = myQueue.GetAllMessages().ToList();
    foreach (System.Messaging.Message message in messages)
    {
        System.Xml.XmlDocument result = ConvertToXMLDoc(message);
        MessageBox.Show(result.InnerText);
    }

}
public System.Xml.XmlDocument ConvertToXMLDoc(System.Messaging.Message msg)
{
    byte[] buffer = new byte[msg.BodyStream.Length];
    msg.BodyStream.Read(buffer, 0, (int)msg.BodyStream.Length);
    int envelopeStart = FindEnvolopeStart(buffer);
    System.IO.MemoryStream stream = new System.IO.MemoryStream(buffer, envelopeStart, buffer.Length - envelopeStart);
    System.ServiceModel.Channels.BinaryMessageEncodingBindingElement elm = new System.ServiceModel.Channels.BinaryMessageEncodingBindingElement();
    System.ServiceModel.Channels.Message msg1 = elm.CreateMessageEncoderFactory().Encoder.ReadMessage(stream, Int32.MaxValue);
    System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
    doc.Load(msg1.GetReaderAtBodyContents());
    msg.BodyStream.Position = 0;
    return doc;
}
private int FindEnvolopeStart(byte[] stream)
{
    int i = 0;
    byte prevByte = stream[i];
    byte curByte = (byte)0;
    for (i = 0; i < stream.Length; i++)
    {
        curByte = stream[i];
        if (curByte == (byte)0x02 &&
        prevByte == (byte)0x56)
            break;
        prevByte = curByte;
    }
    return i - 1;
}
0
On

I know the question is C# code, but I am including Powershell script here for those of us that like to quickly inspect MSMQ contents using the shell. Using Edward's code I also made this work with Powershell, if anybody is looking for a script that can extract these WCF messages that is sent as payload to the WCF service with endpoints using the protocol binding NetMsmqbinding or NetMsmqIntegrationBinding.

Instead of using Int32.MaxValue, I used another large integer value that I know would suffice in most cases. It would be nice to know the logic behind finding the Soap Envelope start by the way.

Here is the Powershell script:

[System.Reflection.Assembly]::LoadWithPartialName("System.Messaging") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.Xml") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.ServiceModel") | Out-Null
[System.Reflection.Assembly]::LoadWithPartialName("System.IO") | Out-Null


$queuePath = ".\private$\demoqueue4"

Write-Host "Powershell MSMQ queue WCF inspector v0.1. Inspecting queue contents of the queue: $queuePath"
Write-Host ""


Run-MainDemoIterateMsmq $queuePath



Function Get-XmlFromWcfMessage([System.Messaging.Message] $msg) {

    $doc = New-Object System.Xml.XmlDocument;
    $messageLength = [int] $msg.BodyStream.Length


    $buffer = New-Object byte[] $messageLength


    $msg.BodyStream.Read($buffer, 0, $messageLength)

    $envelopeStart = Find-SoapEnvelopeStart($buffer)

    $envelopeStart = $envelopeStart - 0

    $envelopeLength = $($buffer.Length - $envelopeStart)
    #Write-Host $envelopeStart


    $stream = New-Object System.IO.MemoryStream($buffer, $envelopeStart, $envelopeLength)

    $elm = New-Object System.ServiceModel.Channels.BinaryMessageEncodingBindingElement
    $elm.ReaderQuotas.MaxStringContentLength = 10000000
    $elm.ReaderQuotas.MaxBytesPerRead = 10000000


    $msg1 = $elm.CreateMessageEncoderFactory().Encoder.ReadMessage($stream, 10000000);

    $doc.Load($msg1.GetReaderAtBodyContents());

    $msg.BodyStream.Position = 0;



    return $doc;
}

Function Find-SoapEnvelopeStart([byte[]] $stream)
{
    $i = 0;
    $j = 0;
    $prevByte = $stream[$i];
    $curByte = [byte]$j;
    for ($i = 0; $i -lt $stream.Length; $i++)
    {
        $curByte = $stream[$i];
        if ($curByte -eq [byte] 0x02 -and $prevByte -eq [byte] 0x56) {
            break;
        }
        $prevByte = $curByte;
    }
    return $i - 1;
}


Function Run-MainDemoIterateMsmq([string] $queuePath) {

$queue = New-Object System.Messaging.MessageQueue $queuePath

foreach ($message in $queue.GetAllMessages()){
  $xmlDoc = Get-XmlFromWcfMessage $message 
  Write-Host $xmlDoc.OuterXml
 } 


}

The output can then look like this for example:

    <SendMessage xmlns="http://tempuri.org/"><message>this is another test!</message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>why hello srmp</message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>test</message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>my message to msmq</message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>This is a new message!</message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>Another message to MSMQ! </message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>another test here</message></SendMessage>
    <SendMessage xmlns="http://tempuri.org/"><message>This is a test message that will be sent using NetMsmqBinding. </message></SendMessage>
    <SendMessageDataContract xmlns="http://tempuri.org/"><message xmlns:b="http://schemas.datacontract.org/2004/07/WcfDemoNetMsmqBinding.Host" xmlns:i="http://www.w3.org/2001
    /XMLSchema-instance"><b:BoolValue>true</b:BoolValue><b:StringValue>This is a test of a stringvalue for a CompositeType</b:StringValue></message></SendMessageDataContract>
0
On

When you send/retrieve a message body into/from MSMQ, you should use the same "type". see generic type "T" below. T could be any of your custom class.

    public T GetQueueMessage()
    {
        Message message = queue.Receive(new TimeSpan(0, 0, 0, 1, 0));
        return (T)message.Body;
    }

    public void InsertQueueMessage(T message)
    {
        using (Message msg = new Message((object)message))
        {
            queue.Send(msg, MessageQueueTransactionType.Single);
        }
    }