Processing MSMQ Message in Windows Service
Asked Answered
M

2

9

I have a windows service for processing the MSMQ messages. It relies on the following logic

· There is a timer in the windows service. Every ten minute it will execute the method named “ProcessMessages”.

· Inside this method, it first creates a list of existing messageIds by calling GetAllMessages method of the queue.

· For each messageId, it receives the message (using ReceiveById) and stores it into a file

Is there a better way to achieve the message processing?

Reference: http://www.switchonthecode.com/tutorials/creating-a-simple-windows-service-in-csharp

Note: The following code does not give the desired result when I made it as a service; however there is no error in event viewer (I am not doing any explicit logging). It was working fine when it was a simple console app. How to correct it? [Now it is working when I changed the accoun to "User" as shwon in the comments below]

My actaul requirement is to process all messages at fixed time slots – say at 10 AM and 11 AM only (on each day). What is the best approach to do this?

namespace ConsoleSwitchApp
{
    class Program : ServiceBase
    {
        private static Timer scheduleTimer = null;
        static MessageQueue helpRequestQueue = null;
        static System.Messaging.XmlMessageFormatter stringFormatter = null;

        static void Main(string[] args)
        {
            ServiceBase.Run(new Program());
        }

        public Program()
        {
            this.ServiceName = "LijosService6";

            //Queue initialize
            helpRequestQueue = new MessageQueue(@".\Private$\MyPrivateQueue", false);
            stringFormatter = new System.Messaging.XmlMessageFormatter(new string[] { "System.String" });

            //Set Message Filters
            MessagePropertyFilter filter = new MessagePropertyFilter();
            filter.ClearAll();
            filter.Body = true;
            filter.Label = true;
            filter.Priority = true;
            filter.Id = true;
            helpRequestQueue.MessageReadPropertyFilter = filter;

            //Start a timer
            scheduleTimer = new Timer();
            scheduleTimer.Enabled = true;
            scheduleTimer.Interval = 120000;//2 mins
            scheduleTimer.AutoReset = true;
            scheduleTimer.Start();
            scheduleTimer.Elapsed += new ElapsedEventHandler(scheduleTimer_Elapsed);
        }

        protected static void scheduleTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            ProcessMessages();
        }

        private static void ProcessMessages()
        {
            string messageString = "1";

            //Message Processing
            List<string> messageIdList = GetAllMessageId();
            foreach (string messageId in messageIdList)
            {
                System.Messaging.Message messages = helpRequestQueue.ReceiveById(messageId);
                //Store the message into database

                messages.Formatter = stringFormatter;
                string messageBody = System.Convert.ToString(messages.Body);

                if (String.IsNullOrEmpty(messageString))
                {
                    messageString = messageBody;
                }
                else
                {
                    messageString = messageString + "___________" + messageBody;
                }
            }

            //Write File
            string lines = DateTime.Now.ToString();
            lines = lines.Replace("/", "-");
            lines = lines.Replace(":", "_");
            System.IO.StreamWriter file = new System.IO.StreamWriter("c:\\test" + lines + ".txt");
            file.WriteLine(messageString);
            file.Close();
        }

        private static List<string> GetAllMessageId()
        {
            List<string> messageIdList = new List<string>();

            DataTable messageTable = new DataTable();
            messageTable.Columns.Add("Label");
            messageTable.Columns.Add("Body");

            //Get All Messages
            System.Messaging.Message[] messages = helpRequestQueue.GetAllMessages();
            for (int index = 0; index < messages.Length; index++)
            {
                string messageId = (System.Convert.ToString(messages[index].Id));
                messageIdList.Add(messageId);

                messages[index].Formatter = stringFormatter;
                messageTable.Rows.Add(new string[] { messages[index].Label, messages[index].Body.ToString() });
            }

            return messageIdList;
        }


        protected override void OnStart(string[] args)
        {
            base.OnStart(args);
        }

        protected override void OnStop()
        {
            base.OnStop();
        }
    }
}

namespace ConsoleSwitchApp
{
    [RunInstaller(true)]
    public class MyWindowsServiceInstaller : Installer
    {
        public MyWindowsServiceInstaller()
        {
            var processInstaller = new ServiceProcessInstaller();
            var serviceInstaller = new ServiceInstaller();

            //set the privileges
            processInstaller.Account = ServiceAccount.LocalSystem;
            serviceInstaller.DisplayName = "LijosService6";
            serviceInstaller.StartType = ServiceStartMode.Manual;

            //must be the same as what was set in Program's constructor

           serviceInstaller.ServiceName = "LijosService6";

            this.Installers.Add(processInstaller);
            this.Installers.Add(serviceInstaller);
        }
    }
}
Mountain answered 28/3, 2012 at 16:26 Comment(4)
It's probably a permissions problem. Try using one of the other built-in accounts.Ghoul
@Ghoul Thanks.. Service worked when I used ServiceAccount.User and gave my username and password. What is the suggested account here?Mountain
I would highly recommend not using a dedicated user account in a production environment for this. The problem is likely related to this KB article (it is about Vista but the same problem would likely exist in 7 and 2008).Ghoul
The link in your question 404s fyiPegeen
C
17

A nice alternative to using a timer is to use the MessageQueue.BeginReceive method and do work in the ReceiveCompleted event. This way your code will wait until there is a message in the queue and then immediately process the message, then check for the next message.

A short stub (a complete example in the linked MSDN article.)

private void Start()
{
    MessageQueue myQueue = new MessageQueue(".\\myQueue");

    myQueue.ReceiveCompleted += 
        new ReceiveCompletedEventHandler(MyReceiveCompleted);

    myQueue.BeginReceive();
}

private static void MyReceiveCompleted(Object source, 
    ReceiveCompletedEventArgs asyncResult)
{
    try
    {
        MessageQueue mq = (MessageQueue)source;
        Message m = mq.EndReceive(asyncResult.AsyncResult);

        // TODO: Process the m message here

        // Restart the asynchronous receive operation.
        mq.BeginReceive();
    }
    catch(MessageQueueException)
    {
        // Handle sources of MessageQueueException.
    }

    return; 
}
Calcicole answered 28/3, 2012 at 17:43 Comment(1)
Use Windows Task Scheduler to run a Console Application.Landscape
S
2

Why not to subscribe to ReceiveCompleted event? Another option, if both sender and subscriber is .Net projects you are working on, use WCF over MSMQ.

Sonometer answered 28/3, 2012 at 17:49 Comment(2)
My requirement is to process all messages at fixed time slots – say at 10 AM and 11 AM only (on each day). What is the best approach to do this?Mountain
Might be you should grant access to MSMQ to Anonymous domain accountSonometer

© 2022 - 2024 — McMap. All rights reserved.