none
How to write C#/WPF/WCF client to display entries in WCF/MSMQ Queue RRS feed

  • Question

  • As per the suggestion at https://social.msdn.microsoft.com/Forums/vstudio/en-US/fa54aaac-0b9e-4864-8e74-96b0ba0cb8d7/how-to-write-cwpfwcf-client-to-display-and-delete-entries-in-msmq?forum=wcf I am posting again here.

    I modified the server source code at https://www.codeproject.com/Articles/326909/Creating-a-WCF-Service-with-MSMQ-Communication-and to execute CMD.EXE commands and implemented a WCF throttle so that only one queue entry is executed at a time.

    How can I enhance the following code to fetch my pending queue entries and display the CMD.EXE command in a WPF list box so I can delete a queue entry that has not been removed from the queue yet by the server. Linqpad is giving me the XmlExecption "Invalid character in the given encoding. Line 1 postion 57". 

    Thanks

    Siegfried

    System.Messaging.MessageQueue[] queueList = System.Messaging.MessageQueue.GetPrivateQueuesByMachine(Environment.MachineName);
    //queueList.Dump();
    var q = queueList[0];
    q.QueueName.Dump();
    q.CanRead.Dump();
    var e = q.GetMessageEnumerator2();
    while (e.MoveNext())
    {
    	var mes = e.Current;
    	mes.Formatter = new System.Messaging.XmlMessageFormatter(new Type[]	{typeof(string)});
    //	XmlMessageFormatter(new string[] { "System.String,mscorlib" });
    	mes.Body.ToString().Dump();
    }

    More information (as per Edwards request):

    Let's pretend I want implement a throttled build server using WCF/MSMQ and  have some builds that take 4 or 5 hours. So I'm working on bug #1 for project #1 and submit a build and start working on bug #2 in project #2 in  TFS enlistment/view #2. After 30 minutes I submit a second build for bug #2 which sits in the queue because the queue is throttled. Then I start work on bug #3 in TFS enlistment/view #3 and 30 minutes later I submit a third build for bug #3 which also sits in the queue. Then I talk to a colleague and realize my fix for bug #2 is wrong and the build for bug #2 has not started yet because build #1 is still executing. Assuming my CMD.EXE/MSBUILD commands are very short, how do I enumerate the queue displaying each MSBUILD command and allow the user to select the MSBUILD command for bug #2 and cancel (delete) it?

    Continuing my use-case scenario: Then I make some changes for bug/project/enlistment/view #2 and submit it as build #4 and go home for the day.

    My WCF contract contains a function called ShellCommand(string cmdText). I want to enumerate the entries in the queue, display them in a list box, allow the user to select the build command for bug #2 at 4PM, and delete that entry from the WCF/MSMQ queue so that when the build for bug #1 completes 6PM, it starts executing the build command for bug #3 (after I have gone home for the day) instead of bug #2 and when I come back the next morning, the builds for bugs #1, #2 and #3 have completed.

    So again: how do I extract that string argument for the ShellCommand sitting in the MSMQ for build #2?

    I hope this makes it clear!

    Thanks

    Siegfried

     

    siegfried heintze



    • Edited by siegfried_ Tuesday, March 14, 2017 1:10 AM
    Tuesday, March 14, 2017 12:42 AM

Answers

  • Have your issue been resolved? If not, I suggest you try below code to get the body.

            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;
            }
    

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by siegfried_ Thursday, March 23, 2017 4:08 AM
    Tuesday, March 21, 2017 9:48 AM

All replies

  • >>How can I enhance the following code to fetch my pending queue entries

    Are you able to get the queue entry body? What is “mes.Body.ToString().Dump();”? There is no Dump definition at my side. What is the type for your message body? Is it a string or an object?

    For getting message body, I suggest you try something like below:

                System.Messaging.MessageQueue[] queueList = System.Messaging.MessageQueue.GetPrivateQueuesByMachine(Environment.MachineName);
                MessageQueue myQueue = queueList[0];
                List<System.Messaging.Message> messages = myQueue.GetAllMessages().ToList();
                foreach (System.Messaging.Message message in messages)
                {
                    message.Formatter = new XmlMessageFormatter(
                                    new Type[]{typeof(Order)});
                    Order order = message.Body as Order;
                    //get message label
                    Console.WriteLine(message.Label);
                    //get message body
                    Console.WriteLine(order.orderId);
                    Console.WriteLine(order.orderTime);
                }

    My message body is Order object

            public class Order
            {
                public int orderId;
                public DateTime orderTime;
            }

    In addition, for your previous thread which asking for removing message from queue entries, if there is no other issue to removing messages, I suggest you mark the solution as answer to close previous thread.


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Tuesday, March 14, 2017 1:58 AM
  • I tried your code and I received this XmlException from linqpad:

    Invalid character in the given encoding. Line 1, position 57.

    What am I doing wrong?

    Thanks

    Siegfried

    Here is my code from linqpad:

    void Main()
    {
    	System.Messaging.MessageQueue[] queueList = System.Messaging.MessageQueue.GetPrivateQueuesByMachine(Environment.MachineName);
    	//queueList.Dump();
    	var myQueue = queueList[0];
    	List<System.Messaging.Message> messages = myQueue.GetAllMessages().ToList();
    	foreach (System.Messaging.Message message in messages)
    	{
    		message.Formatter = new System.Messaging.XmlMessageFormatter(
    						new Type[] { typeof(ShellCommand) });
    		ShellCommand order = message.Body as ShellCommand;
    		//get message label
    		Console.WriteLine(message.Label);
    		//get message body
    		Console.WriteLine(order.jobName);
    		Console.WriteLine(order.workingDirectory);
    	}
    }
    public class ShellCommand
    {
    	public string msg;
    	public bool cygwin;
    	public bool admin;
    	public bool impersonate;
    	public bool loadEnvironmentVariablesFromEmacs;
    	public string jobName;
    	public string logfile;
    	public string workingDirectory;
    }

    Here is my contract (I need to rename msg to command):

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.Text;
    
    namespace MSMQNoSecurityService
    {
    
        [ServiceContract]
        public interface IMSMQService
        {
            
            [OperationContract(IsOneWay = true)]
            void ShellCommand(string msg, bool cygwin, bool admin, bool impersonate, bool loadEnvironmentVariablesFromEmacs, string jobName, string logfile, string workingDirectory);
        }
    }
    


    siegfried heintze

    Wednesday, March 15, 2017 3:50 AM
  • Based on the error message, it seems the message body could not be converted to ShellCommand class. Could you share us the body xml in Private Queues message?

    It would be helpful if you could share us a simple project which contains WCF client and Service with MSMQ.


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Friday, March 17, 2017 2:48 AM
  • At the very beginning of the post I put a link to the Code Project project I started with. That link contains 2 pairs of projects:

    (1) MSMQ Client and server with no security

    (2) MSMQ Client and server with security

    Since I was not interested in Certificate Security, I started with the first one and changed the function name from Send (or show, I forget) to ShellCommand and called System.Diagnostics.Process().

    Thanks

    Siegfried


    siegfried heintze

    Friday, March 17, 2017 3:15 AM
  • Could you share us a screen shot about your body in msmq message?

    I suggest you try below code:

                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)
                {
                    string ms = "";
                    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);
                }


    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Monday, March 20, 2017 6:21 AM
  • Have your issue been resolved? If not, I suggest you try below code to get the body.

            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;
            }
    

    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    • Marked as answer by siegfried_ Thursday, March 23, 2017 4:08 AM
    Tuesday, March 21, 2017 9:48 AM
  • Wow! That is some impressive decoding....

    Is there way to identify the fields so I can separate them with some commas?

    Thanks

    Siegfried


    siegfried heintze

    Thursday, March 23, 2017 4:10 AM
  • >>Is there way to identify the fields so I can separate them with some commas?

    For this new issue, I would suggest you post a new thread, and share us did you send fields with below:

    void ShellCommand(string msg, bool cygwin, bool admin, bool impersonate, bool loadEnvironmentVariablesFromEmacs, string jobName, string logfile, string workingDirectory);

    Or

    void ShellCommand(ShellCommand shellCommand);



    MSDN Community Support
    Please remember to click "Mark as Answer" the responses that resolved your issue, and to click "Unmark as Answer" if not. This can be beneficial to other community members reading this thread. If you have any compliments or complaints to MSDN Support, feel free to contact MSDNFSF@microsoft.com.

    Thursday, March 23, 2017 5:28 AM