Microsoft Message Queuing

Microsoft Message Queuing或MSMQ微軟公司實現的一種消息隊列，始於Windows NT 4與Windows 95。Windows Server 2016與Windows 10仍然包含這種組件。1999年起，Microsoft Embedded平臺以及Windows CE 3.0也開始支持這一組件。

**簡介**
MSMQ作為一種消息協議，允許多服務器/多進程通信，即使不總是保持互聯。而sockets與其他網絡協議要求直連總是成立。 
MSMQ從1997年開始可用。
MSMQ是可靠分發消息。分發失敗的消息保存在隊列中直到目標可達時重發該消息。還支持安全與優先級的消息機制。可以創建死信隊列用於調試。
MSMQ支持可持續性與不可持續性消息，使得性能與消息是否寫到磁盤的一致性上可以權衡。不可持續性消息只能用於向非事務性隊列發送快速消息。 
MSMQ支持事務處理。允許多個動作發給多個隊列中包裝為單個事務。微軟分佈式事務協調器 (MSDTC) 支持對MSMQ或其他資源的事務訪問。 
MSMQ使用下述端口:- TCP: 1801- RPC: 135, 2101*, 2103*, 2105* UDP: 3527, 1801- 這些端口可能增加11，如果RPC端口的初始選擇被使用。端口135用於查詢2xxx端口。

**版本歷史**
 Version 1.0 (1997年5月). 支持Windows 95, Windows NT 4.0 SP3, Windows 98 與 Windows Me. - Version 2.0, 包含在Windows 2000中.
** 新特性包括: Support for registering public message queues in Active Directory, 128-bit encryption and digital certificate support, full COM support for message properties (achieving functional parity with the Win32 API function calls, full DNS path name support, improved performance in multi-threaded applications.- Version 3.0, 包含在Windows XP (專業版, 非家庭版) 與 Windows Server 2003.
** 新特性包括: Internet Messaging (referencing queues via HTTP, SOAP-formatted messages, MSMQ support for Internet Information Services), queue aliases, multicasting of messages, and additional support for programmatic maintenance and administration of queues and MSMQ itself.- Version 4.0, 包含在Windows Vista與Windows Server 2008.
** 新特性包括: Subqueues, improved support for "poison messages" (messages which continually fail to be processed correctly by the receiver), and support for transactional receives of messages from a remote queue.- Version 5.0, 包含在Windows 7 與 Windows Server 2008 R2.
** 新特性包括: support for Secure Hash Algorithm 2.0 (SHA2) and all advanced hash algorithms that are supported in Windows 2008 R2; by default, weaker hash algorithms are disabled.- Version 6.0, 包含在Windows 8 與 Windows Server 2012.- Version 6.3, 包含在 Windows 8.1 與 Windows Server 2012 R2.

**使用**
C#例子：
using System;
using System.Collections.Generic;
using System.Linq;
using System.Messaging;
using System.Text;
using System.Threading.Tasks;
namespace Test
{
public class QueueManger
{
/// 
/// 創建MSMQ隊列
/// 
/// 隊列路徑
/// 是否事務隊列
public static void Createqueue(string queuePath, bool transactional = false)
{
try
{
//判斷隊列是否存在
if (!MessageQueue.Exists(queuePath))
{
MessageQueue.Create(queuePath);
Console.WriteLine(queuePath + "已成功創建！");
}
else
{
Console.WriteLine(queuePath + "已經存在！");
}
}
catch (MessageQueueException e)
{
Console.WriteLine(e.Message);
}
}
/// 
/// 刪除隊列
/// 
/// 
public static void Deletequeue(string queuePath)
{
try
{
//判斷隊列是否存在
if (MessageQueue.Exists(queuePath))
{
MessageQueue.Delete(@".\private$\myQueue");
Console.WriteLine(queuePath + "已刪除！");
}
else
{
Console.WriteLine(queuePath + "不存在！");
}
}
catch (MessageQueueException e)
{
Console.WriteLine(e.Message);
}
}
/// 
/// 發送消息
/// 
/// 用戶數據類型
/// 用戶數據
/// 隊列名稱
/// 
/// 
public static bool SendMessage(T target, string queuePath, MessageQueueTransaction tran = null)
{
try
{
//連接到本地的隊列
MessageQueue myQueue = new MessageQueue(queuePath);
System.Messaging.Message myMessage = new System.Messaging.Message();
myMessage.Body = target;
myMessage.Formatter = new XmlMessageFormatter(new Type { typeof(T) });
//發送消息到隊列中
if (tran == null)
{
myQueue.Send(myMessage);
}
else
{
myQueue.Send(myMessage, tran);
}
Console.WriteLine("消息已成功發送到"+queuePath + "隊列！");
return true;
}
catch (ArgumentException e)
{
Console.WriteLine(e.Message);
return false;
}
}
/// 
/// 接收消息
/// 
/// 用戶的數據類型
/// 消息路徑
/// 用戶填充在消息當中的數據
public static T ReceiveMessage(string queuePath,MessageQueueTransaction tran=null)
{
//連接到本地隊列
MessageQueue myQueue = new MessageQueue(queuePath);
myQueue.Formatter = new XmlMessageFormatter(new Type { typeof(T) });
try
{
//從隊列中接收消息
System.Messaging.Message myMessage = tran == null ? myQueue.Receive() : myQueue.Receive(tran);
return (T)myMessage.Body; //獲取消息的內容
}
catch (MessageQueueException e)
{
Console.WriteLine(e.Message);
}
catch (InvalidCastException e)
{
Console.WriteLine(e.Message);
}
return default(T);
}
/// 
/// 採用Peek方法接收消息
/// 
/// 用戶數據類型
/// 隊列路徑
/// 用戶數據
public static T ReceiveMessageByPeek(string queuePath)
{
//連接到本地隊列
MessageQueue myQueue = new MessageQueue(queuePath);
myQueue.Formatter = new XmlMessageFormatter(new Type { typeof(T) });
try
{
//從隊列中接收消息
System.Messaging.Message myMessage = myQueue.Peek();
return (T)myMessage.Body; //獲取消息的內容
}
catch (MessageQueueException e)
{
Console.WriteLine(e.Message);
}
catch (InvalidCastException e)
{
Console.WriteLine(e.Message);
}
return default(T);
}
/// 
/// 獲取隊列中的所有消息
/// 
/// 用戶數據類型
/// 隊列路徑
/// 用戶數據集合
public static List GetAllMessage(string queuePath)
{
MessageQueue myQueue = new MessageQueue(queuePath);
myQueue.Formatter = new XmlMessageFormatter(new Type { typeof(T) });
try
{
Message msgArr=  myQueue.GetAllMessages();
List list=new List();
msgArr.ToList().ForEach((o) => 
{
list.Add((T)o.Body);
});
return list;
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
return null;
}
}
}
namespace Test
{
public class Student
{
/// 
/// 年齡
/// 
public int Age { get; set; }
/// 
/// 姓名
/// 
public string Name { get; set; }
}
}
namespace Test
{
class Program
{
static void Main(string args)
{
string queuepath = @".\private$\myQueue";
//QueueManger.Createqueue(queuepath);
//Student stu = new Student() { Name="shaoshun",Age=18};
//QueueManger.SendMessage(stu, queuepath);
//Student stu=  QueueManger.ReceiveMessageByPeek(queuepath);
//Student stu = QueueManger.ReceiveMessage(queuepath);
//Console.WriteLine(stu.Name);
QueueManger.Deletequeue(queuepath);
QueueManger.Createqueue(queuepath);
MessageQueueTransaction tran = new MessageQueueTransaction();
tran.Begin();
try
{
Student stu;
for (int i = 0; i (stu, queuepath, tran);
if (i == 3)
{
throw new Exception();
}
}
tran.Commit();
}
catch
{
tran.Abort();
}
Console.ReadKey();
}
}
}
C語言調用Windows API例子：
#include "windows.h"  
#include "mq.h"  
#include "tchar.h"  
#define BUFLEN = 256 ;  

HRESULT CreateMSMQQueue(  
LPWSTR wszPathName,   
PSECURITY_DESCRIPTOR pSecurityDescriptor,  
LPWSTR wszOutFormatName,  
DWORD *pdwOutFormatNameLength  
)  
{  

// Define the maximum number of queue properties.  
const int NUMBEROFPROPERTIES = 2;  

// Define a queue property structure and the structures needed to initialize it.  
MQQUEUEPROPS   QueueProps;  
MQPROPVARIANT  aQueuePropVarNUMBEROFPROPERTIES;  
QUEUEPROPID    aQueuePropIdNUMBEROFPROPERTIES;  
HRESULT        aQueueStatusNUMBEROFPROPERTIES;  
HRESULT        hr = MQ_OK;  

// Validate the input parameters.  
if (wszPathName == NULL 
wszOutFormatName == NULL 
pdwOutFormatNameLength == NULL)  
{  
return MQ_ERROR_INVALID_PARAMETER;  
}  

// Set queue properties.  
DWORD cPropId = 0;  
aQueuePropIdcPropId = PROPID_Q_PATHNAME;  
aQueuePropVarcPropId.vt = VT_LPWSTR;  
aQueuePropVarcPropId.pwszVal = wszPathName;  
cPropId++;  

WCHAR wszLabelMQ_MAX_Q_LABEL_LEN = L"Test Queue";  
aQueuePropIdcPropId = PROPID_Q_LABEL;  
aQueuePropVarcPropId.vt = VT_LPWSTR;  
aQueuePropVarcPropId.pwszVal = wszLabel;  
cPropId++;  

// Initialize the MQQUEUEPROPS structure.  
QueueProps.cProp = cPropId;               // Number of properties  
QueueProps.aPropID = aQueuePropId;        // IDs of the queue properties  
QueueProps.aPropVar = aQueuePropVar;      // Values of the queue properties  
QueueProps.aStatus = aQueueStatus;        // Pointer to the return status  

// Call MQCreateQueue to create the queue.  
WCHAR wszFormatNameBufferBUFLEN;  
DWORD dwFormatNameBufferLength = BUFLEN;  
hr = MQCreateQueue(pSecurityDescriptor,         // Security descriptor  
&QueueProps,                 // Address of queue property structure  
wszFormatNameBuffer,         // Pointer to format name buffer  
&dwFormatNameBufferLength);  // Pointer to receive the queue's format name length in Unicode characters not bytes.  

// Return the format name if the queue is created successfully.  
if (hr == MQ_OK 
hr == MQ_INFORMATION_PROPERTY)  
{  
if (*pdwOutFormatNameLength >= dwFormatNameBufferLength)  
{  
wcsncpy_s(wszOutFormatName, *pdwOutFormatNameLength - 1, wszFormatNameBuffer, _TRUNCATE);  
// ***********************************      // You must copy wszFormatNameBuffer into the   
// wszOutFormatName buffer.  
// ***********************************      wszOutFormatName*pdwOutFormatNameLength - 1 = L'\0';  
*pdwOutFormatNameLength = dwFormatNameBufferLength;  
}  
else  
{  
wprintf(L"The queue was created, but its format name cannot be returned.\n");  
}  
}  
return hr;  
}  

**參見**

**參考文獻**

**外部鏈接**
  MSDN documentation date=20190205160847