继MSMQ简单包装类后,又把MSMQ再更新了一下。主要加入一些事件,有消息到达时,可用外部事件处理(ProcessMessageHandler),以及消息格式不是预期格式时的事件(InvalidTypeHandler),设置接收超时时间(Timeout);MSMQ里没有消息或接收超时的处理事件(NoMessageOrTimeoutHandler);可以设置接受到消息后是同步执行(ThreadCount = 0时)还是异步执行(ThreadCount > 0时);并可以限制多线程执行时的数量(ThreadCount = n);

使用MSMQ的准备工作参见http://www.cnblogs.com/iwteih/archive/2010/01/18/1650399.html

先上接口:

namespace XXX.Msmq 
{ 
    public interface IMsmq<T> : IDisposable 
    { 
        /// <summary> 
        /// Serializes and deserializes objects to or from the body of a message  
        /// </summary> 
        MessageFormatter Formatter { get; set; } 

        /// <summary> 
        /// The count of threads to process object in queue. 
        /// If omitted or 0, synchronous execution, otherwise asynchronous execution. 
        /// </summary> 
        int ThreadCount { get; set; } 

        /// <summary> 
        /// The time to be spent to get a message from Msmq. 
        /// If omitted , the hook will be alive all the time 
        /// </summary> 
        TimeSpan Timeout { get; set; } 

        /// <summary> 
        /// Push an object into MSMQ 
        /// </summary> 
        /// <param name="element">The object to be pushed into MSMQ</param> 
        void Push(T element); 

        /// <summary> 
        /// Pop the element in MSMQ 
        /// </summary> 
        /// <returns>object in msmq</returns> 
        T Pop(); 

        /// <summary> 
        /// Start to listen Msmq 
        /// </summary> 
        void StartListener(); 

        /// <summary> 
        /// Stop listening Msmq. Make sure that StopListen & StartListen are paired matched 
        /// or no StopListen. 
        /// </summary> 
        void StopListener(); 

        /// <summary> 
        /// The real funtion to process messsage. 
        /// </summary> 
        event ProcessMessageHandler ProcessMessage; 

        /// <summary> 
        /// Triggered when no message in MSMQ or timeout 
        /// </summary> 
        event NoMessageOrTimeoutHandler ProcessNoMessageOrTimeout; 

        /// <summary> 
        /// If message type is expected, use this event for handling 
        /// </summary> 
        event InvalidTypeHandler ProcessInvalidType; 
    } 
} 

实现如下:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Messaging; 
using System.Threading; 
using System.Runtime.InteropServices; 
using log4net; 

namespace XXX.Msmq 
{ 
    public enum MessageFormatter 
    { 
        XmlMessageFormatter, 
        BinaryMessageFormatter, 
    } 


    public class MessageArgs : EventArgs 
    { 
        /// <summary> 
        /// The object poped from queue 
        /// </summary> 
        public object ObjectToProcess { get; set; } 
    } 


    public delegate void ProcessMessageHandler(object sender, MessageArgs args); 

    public delegate void NoMessageOrTimeoutHandler(object sender, EventArgs args); 

    public delegate void InvalidTypeHandler(object sender, EventArgs args); 


    internal class Msmq<T> : IMsmq<T> 
    { 
        private static readonly ILog logger = LogManager.GetLogger( 
                    System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 

        MessageQueue queue = null; 

        int upperLimit = 0; 

        int currentThreadCount = 0; 


        #region Constructor 
        /// <summary> 
        /// Initial a Msmq object 
        /// </summary> 
        /// <param name="qName">Queue name</param> 
        public Msmq(string qName) 
            : this(qName, 0) 
        { 
        } 


        /// <summary> 
        /// Initial a Msmq object. 
        /// </summary> 
        /// <param name="qName">Queue name</param> 
        /// <param name="limit">Upper limit in this msmq</param> 
        public Msmq(string qName, int limit) 
        { 
            if (!string.IsNullOrEmpty(qName) && !MessageQueue.Exists(qName)) 
            { 
                MessageQueue.Create(qName); 
            } 
            queue = new MessageQueue(qName); 

            upperLimit = limit; 
        } 

        #endregion 

        #region IMsmq<T> Members 

        IMessageFormatter msgFormatter = new XmlMessageFormatter(new Type[] { typeof(T) }); 
        private MessageFormatter formatter; 
        /// <summary> 
        /// Serializes and deserializes objects to or from the body of a message  
        /// </summary> 
        MessageFormatter IMsmq<T>.Formatter 
        { 
            get { return formatter; } 
            set 
            { 
                formatter = value; 
                if (formatter == MessageFormatter.BinaryMessageFormatter) 
                { 
                    msgFormatter = new BinaryMessageFormatter(); 
                } 
                else 
                { 
                    msgFormatter = new XmlMessageFormatter(new Type[] { typeof(T) }); 
                } 
            } 
        } 

        /// <summary> 
        /// How many threads to process message. If omitted or 0, synchronous execution, otherwise asynchronous execution  
        /// </summary> 
        private int threadCount = 0; 
        int IMsmq<T>.ThreadCount 
        { 
            get { return threadCount; } 
            set 
            { 
                threadCount = value; 
                if (threadCount < 0) 
                    threadCount = 0; 
            } 
        } 

        /// <summary> 
        /// The time to wait while trying to get an object from Msmq. 
        /// </summary> 
        private TimeSpan timeout = TimeSpan.MaxValue; 
        TimeSpan IMsmq<T>.Timeout 
        { 
            get { return timeout; } 
            set { timeout = value; } 
        } 

        private event ProcessMessageHandler internelProcessMessage; 
        /// <summary> 
        /// The real funtion to process messsage. 
        /// </summary> 
        //public event ProcessMessageHandler ProcessMessage; 
        event ProcessMessageHandler IMsmq<T>.ProcessMessage 
        { 
            add { internelProcessMessage += value; } 
            remove { internelProcessMessage -= value; } 
        } 

        private event NoMessageOrTimeoutHandler noMessageOrTimeoutHandler; 
        /// <summary> 
        /// Triggered when no message in MSMQ or timeout 
        /// </summary> 
        event NoMessageOrTimeoutHandler IMsmq<T>.ProcessNoMessageOrTimeout 
        { 
            add { noMessageOrTimeoutHandler += value; } 
            remove { noMessageOrTimeoutHandler -= value; } 
        } 

        private event InvalidTypeHandler invalidTypeHandler; 
        /// <summary> 
        /// If message type is expected, use this event for handling 
        /// </summary> 
        event InvalidTypeHandler IMsmq<T>.ProcessInvalidType 
        { 
            add { invalidTypeHandler += value; } 
            remove { invalidTypeHandler -= value; } 
        } 

        /// <summary> 
        /// Push an object into MSMQ 
        /// </summary> 
        /// <param name="element">The object to be pushed into MSMQ</param> 
        void IMsmq<T>.Push(T element) 
        { 
            Send(element); 
        } 


        void Send(object element) 
        { 
            using (System.Messaging.Message message = new System.Messaging.Message()) 
            { 
                message.Body = element; 
                message.Formatter = msgFormatter; 

                queue.Send(message); 
            } 

            //In original status, MSMQ is empty, error will throw when calling CurrentCount. Because queue is not open. 
            //so i allow insert action happen before calling CurrentCount. 
            //If reach the upper message limit number, sleep for one minute. 
            while (upperLimit != 0 && (CurrentMessageCount >= upperLimit)) 
            { 
                System.Threading.Thread.Sleep(60000); 
            } 
        } 


        /// <summary> 
        /// Pop the element in MSMQ 
        /// </summary> 
        /// <returns>object in msmq</returns> 
        T IMsmq<T>.Pop() 
        { 
            return Receive(); 
        } 

        T Receive() 
        { 
            T element = default(T); 
            try 
            { 
                using (Message message = queue.Receive(new TimeSpan(0, 0, 10))) 
                { 
                    message.Formatter = msgFormatter; 
                    element = (T)message.Body; 
                } 
            } 
            catch (MessageQueueException mqex) 
            { 
                //Ingore the exception when queue is empty 
                if (mqex.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout) 
                { 
                    logger.Error(mqex); 
                } 
            } 
            return element; 
        } 

        /// <summary> 
        /// Start to listen Msmq 
        /// </summary> 
        void IMsmq<T>.StartListener() 
        { 
            queue.ReceiveCompleted += new ReceiveCompletedEventHandler(queue_ReceiveCompleted); 
            BeginReceiveMessage(); 
        } 


        /// <summary> 
        /// Stop listening Msmq. Make sure that StopListen & StartListen are paired matched 
        /// or no StopListen. 
        /// </summary> 
        void IMsmq<T>.StopListener() 
        { 
            queue.ReceiveCompleted -= new ReceiveCompletedEventHandler(queue_ReceiveCompleted); 
            currentThreadCount = 0; 
        } 

        #endregion 


        void BeginReceiveMessage() 
        { 
            queue.BeginReceive(timeout); 
        } 

        void queue_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e) 
        { 
            Message msg = null; 
            MessageStatus status = MessageStatus.Unknown; 
            try 
            { 
                msg = ((MessageQueue)sender).EndReceive(e.AsyncResult); 
                status = MessageStatus.OK; 
            } 
            catch (MessageQueueException qexp) 
            { 
                status = MessageStatus.QueueError; 
                if (qexp.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout) 
                { 
                    status = MessageStatus.IOTimeout; 
                } 
                else 
                { 
                    logger.Error(qexp); 
                } 
            } 
            catch (Exception exp) 
            { 
                logger.Error(exp); 
            } 

            //Handle message in exception condition 
            if (msg == null) 
            { 
                switch (status) 
                { 
                    case MessageStatus.IOTimeout: 
                        if (noMessageOrTimeoutHandler != null) 
                        { 
                            noMessageOrTimeoutHandler(this, null); 
                        } 
                        break; 
                    default: 
                        break; 
                } 
                BeginReceiveMessage(); 
                return; 
            } 

            msg.Formatter = msgFormatter; 

            if (msg.Body is T) 
            { 
                T element = (T)msg.Body; 

                if (internelProcessMessage != null) 
                { 
                    //asynchronously execute 
                    if (threadCount > 0) 
                    { 
                        Action<T> callback = RunAsnyc; 
                        callback.BeginInvoke(element, AfterRunAsnyc, null); 

                        //control the multi-thread work flow to make sure only specified threads are working 
                        while (true) 
                        { 
                            if (currentThreadCount >= threadCount) 
                            { 
                                Thread.Sleep(1000); 
                            } 
                            else 
                            { 
                                Interlocked.Increment(ref currentThreadCount); 
                                BeginReceiveMessage(); 
                            } 
                        } 
                    } 
                    // synchronously execute 
                    else 
                    { 
                        internelProcessMessage(this, new MessageArgs { ObjectToProcess = element }); 
                        BeginReceiveMessage(); 
                    } 
                } 
            } 
            else 
            { 
                status = Msmq<T>.MessageStatus.InvalidType; 
                //If invalidTypeHandler defined, using it otherwise resend message into Msmq 
                if (invalidTypeHandler != null) 
                { 
                    invalidTypeHandler(msg, null); 
                } 
                else 
                { 
                    Send(msg); 
                } 
            } 
            msg.Dispose(); 
        } 

        void AfterRunAsnyc(IAsyncResult itfAR) 
        { 
            Interlocked.Decrement(ref currentThreadCount); 
        } 

        private void RunAsnyc(T element) 
        { 
            foreach (var v in internelProcessMessage.GetInvocationList()) 
            { 
                ProcessMessageHandler pmh = (ProcessMessageHandler)v; 
                pmh.Invoke(this, new MessageArgs { ObjectToProcess = element }); 
            } 
        } 


        /// <summary> 
        /// This works well for the situation that MSMQ and KEXQueue is on the same machine. 
        /// I am not sure it can work well if the two are separated. 
        /// </summary> 
        private int CurrentMessageCount 
        { 
            get 
            { 
                //MSMQ.MSMQManagement msmq = new MSMQ.MSMQManagement(); 

                object server = null; 
                object path = queue.Path; 
                object format = null; 

                //msmq.Init(ref server, ref path, ref format); 

                //int count = msmq.MessageCount; 

                //Marshal.ReleaseComObject(msmq); 

                //return count; 
                return 0; 
            } 
        } 

        enum MessageStatus 
        { 
            /// <summary> 
            /// Message received successfully 
            /// </summary> 
            OK, 
            /// <summary> 
            /// Queue is empty or occur when time out 
            /// </summary> 
            IOTimeout, 
            /// <summary> 
            /// Cannot convert the messsage to expected object type 
            /// </summary> 
            InvalidType, 
            /// <summary> 
            /// Exception occurs when receiving the message 
            /// </summary> 
            QueueError, 
            /// <summary> 
            /// Exception thrown not by Msmq 
            /// </summary> 
            Unknown 
        } 

        #region IDisposable Members 

        void IDisposable.Dispose() 
        { 
            if (queue != null) 
                queue.Dispose(); 
        } 

        #endregion 
    } 
} 

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace XXX.Msmq 
{     
    public class MsmqFactory<T> 
    { 
        private static object objLock = new object(); 

        private static Dictionary<string, IMsmq<T>> queuelist = new Dictionary<string, IMsmq<T>>(); 

        /// <summary> 
        /// Create a Msmq instance. 
        /// </summary> 
        /// <param name="queueName">Queue name</param> 
        /// <returns>A Msmq instance</returns> 
        /// <remarks>If calling CreateMsmq twice. The two returned objects are separated instances. 
        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 
        /// one and two are different objecct. 
        /// </remarks> 
        public static IMsmq<T> CreateMsmq(string queueName) 
        { 
            return new Msmq<T>(queueName); 
        } 

        /// <summary> 
        /// Create a Msmq instance with message count limitation. 
        /// </summary> 
        /// <param name="queueName">Queue name</param> 
        /// <param name="limit">Upper limit in this msmq</param> 
        /// <returns>A Msmq instance</returns> 
        /// <remarks>If calling CreateMsmq twice. The two returned objects are separated instances. 
        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 
        /// one and two are different objecct. 
        /// </remarks> 
        public static IMsmq<T> CreateMsmq(string queueName, int limit) 
        { 
            return new Msmq<T>(queueName, limit); 
        } 


        /// <summary> 
        /// Initial a singleton Msmq object 
        /// </summary> 
        /// <param name="qName">Queue name</param> 
        /// <returns>A Msmq instance</returns> 
        /// <remarks>If calling CreateMsmq twice. The two returned objects are the same instances if their queue names are the same.. 
        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 
        /// one and two are the same object because they have the same queuename. 
        /// </remarks> 
        public static IMsmq<T> CreateSingletonMsmq(string queueName) 
        { 
            return CreateSingletonMsmq(queueName, 0); 
        } 


        /// <summary> 
        /// Initial a singleton Msmq object 
        /// </summary> 
        /// <param name="qName">Queue name</param> 
        /// <param name="limit">Upper limit in this msmq</param> 
        /// <returns>A Msmq instance</returns> 
        /// <remarks>If calling CreateMsmq twice. The two returned objects are the same instances if their queue names are the same. 
        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 
        /// one and two are the same object because they have the same queuename. 
        /// </remarks>        
        public static IMsmq<T> CreateSingletonMsmq(string queueName, int limit) 
        { 
            lock (objLock) 
            { 
                if (!queuelist.ContainsKey(queueName)) 
                { 
                    Msmq<T> queue = new Msmq<T>(queueName); 
                    queuelist.Add(queueName, queue); 
                    return queue; 
                } 
                else 
                { 
                    return queuelist[queueName]; 
                } 
            } 
        } 


        /// <summary> 
        /// Dispose a queue by given queuename. 
        /// </summary> 
        /// <param name="qName">The queue with the name to be disposed </param> 
        public static void DisposeQueue(string queueName) 
        { 
            lock (objLock) 
            { 
                if (queuelist.ContainsKey(queueName)) 
                { 
                    queuelist[queueName].Dispose(); 
                    queuelist.Remove(queueName); 
                } 
            } 
        } 

        /// <summary> 
        /// Dispose all queues. 
        /// </summary> 
        public static void DisposeQueue() 
        { 
            lock (objLock) 
            { 
                foreach (var queue in queuelist.Values) 
                { 
                    queue.Dispose(); 
                } 
                queuelist.Clear(); 
            } 
        } 

    } 
} 

调用如下:
IMsmq<YourObj>  msmq = MsmqFactory<YourObj>.CreateMsmq("QueueName"); 
msmq.Formatter = MessageFormatter.XmlMessageFormatter; 
msmq.Timeout = new TimeSpan(0, 0, 30); 
msmq.ProcessMessage += new ProcessMessageHandler(ProcessMessage); 
msmq.ProcessNoMessageOrTimeout += new NoMessageOrTimeoutHandler(ProcessNoMessageOrTimeout); 
msmq.StartListener(); 
 

MSMQ接收消息的方式有很多,比如BeginPeek或EndPeek,可以根据需要自行改动。

NOTE: 若将MSMQ定义成Transaction,则在多线程接收message时会出现消息丢失现象。