前沿拓展:
#程序員##IT教育##軟件開(kāi)發(fā)##.net##軟件工程師#
銳英源精品原創(chuàng),禁止全文或局部轉(zhuǎn)載,禁止任何形式的非法使用,侵權(quán)必究。點(diǎn)名“簡(jiǎn)易百科”和閑暇巴盜用銳英源原創(chuàng)內(nèi)容。
最近開(kāi)發(fā)平臺(tái),需要進(jìn)程間通信,看了些命名管道和復(fù)雜的消息隊(duì)列例子及文章,一直想找輕量的平臺(tái),本文里介紹說(shuō)消息隊(duì)列可以跨進(jìn)程,非常感興趣,翻譯學(xué)習(xí)也,也供大家學(xué)習(xí)。請(qǐng)記住,看不懂codeproject,請(qǐng)找銳英源軟件,學(xué)用開(kāi)源軟件,請(qǐng)找銳英源軟件。
介紹
當(dāng)需要在不同程序之間傳遞信息時(shí),Windows Mobile 和 Windows CE 提供了多種技術(shù)和解決方案來(lái)做到這一點(diǎn)。信息可以通過(guò)共享存儲(chǔ)位置傳遞,例如注冊(cè)表、文件或數(shù)據(jù)庫(kù)。對(duì)于小消息的頻繁通信,可以將消息放在應(yīng)用程序消息泵上或通過(guò)消息隊(duì)列使用。消息隊(duì)列與**、信號(hào)量和互斥鎖屬于同一對(duì)象族;它們被命名為內(nèi)核對(duì)象。目前 .NET Framework 不直接支持這些對(duì)象。但是通過(guò)一些 P/Invokes 聲明,可以輕松訪問(wèn)該功能。在本文中,我將展示如何與消息隊(duì)列功能進(jìn)行交互。
我的目標(biāo)不是對(duì)消息隊(duì)列做詳盡的解釋?zhuān)菫樽x者提供足夠的信息來(lái)了解這個(gè)概念并繼續(xù)下去。
先決條件
本文建立在其他一些 Windows CE 內(nèi)核對(duì)象的概念之上,即**、互斥體和信號(hào)量。它還基于我在最近一篇關(guān)于.NET 的 Windows Mobile 本機(jī)線程同步的文章中介紹的代碼。在閱讀本文之前,請(qǐng)先閱讀以上文章;我擴(kuò)展了上一篇文章中的代碼,您在閱讀本文之前需要熟悉它。
原生函數(shù)和結(jié)構(gòu)
有許多本機(jī)函數(shù)是本文的核心??梢栽?MSDN 庫(kù)中找到有關(guān)函數(shù)的詳細(xì)信息。功能如下:
關(guān)閉消息隊(duì)列創(chuàng)建消息隊(duì)列獲取消息隊(duì)列信息打開(kāi)消息隊(duì)列讀取消息隊(duì)列寫(xiě)消息隊(duì)列
下面列出了用于其中一些功能的結(jié)構(gòu):
消息隊(duì)列信息消息隊(duì)列選項(xiàng)什么是消息隊(duì)列
最簡(jiǎn)單的,隊(duì)列是一個(gè)有序列表??梢詫㈨?xiàng)目添加到列表中或從列表中移動(dòng)。但是,不能從列表中的任意位置添加或刪除項(xiàng)目。項(xiàng)目只能從列表的開(kāi)頭刪除,項(xiàng)目只能添加到列表的末尾。這些插入和刪除規(guī)則通常被標(biāo)記為 FIFO(先進(jìn)先出)或 FCFS(先到先服務(wù)器)。Windows CE 設(shè)備為消息隊(duì)列提供了兩種實(shí)現(xiàn)。一種實(shí)現(xiàn)是**作系統(tǒng)的一部分,用于同一設(shè)備上的進(jìn)程之間的通信。另一種是M**Q的實(shí)現(xiàn),可以安裝到 Windows CE 設(shè)備上,用于與其他機(jī)器進(jìn)行通信。本文圍繞這兩個(gè)實(shí)現(xiàn)中的第一個(gè)展開(kāi)。
消息隊(duì)列可以特定于某個(gè)進(jìn)程,也可以在進(jìn)程之間共享。在任何一種情況下,消息隊(duì)列的句柄都允許對(duì)隊(duì)列進(jìn)行只讀或只寫(xiě)訪問(wèn)。您不能使用同一個(gè)句柄同時(shí)讀取和寫(xiě)入消息隊(duì)列。如果您已經(jīng)擁有消息隊(duì)列的句柄,則可以創(chuàng)建額外的句柄來(lái)讀取或?qū)懭腙P(guān)聯(lián)的隊(duì)列。這對(duì)于未命名的隊(duì)列尤其重要。
如前所述,本文中的代碼基于上一篇文章中的代碼構(gòu)建。上一篇文章中的代碼和本文中的代碼之間的關(guān)系在下面的類(lèi)層次圖中可見(jiàn)。深藍(lán)色 ( MessageQueue, MessageQueueReader, MessageQueueWriter) 中的類(lèi)是本文要添加的類(lèi)。
創(chuàng)建和打開(kāi)消息隊(duì)列
通過(guò)本機(jī)函數(shù)創(chuàng)建或打開(kāi)消息隊(duì)列CreateMsgQueue。與上一篇文章中的同步對(duì)象一樣,必須為消息隊(duì)列分配一個(gè)名稱(chēng)才能在進(jìn)程之間共享。如果多個(gè)進(jìn)程創(chuàng)建同名的消息隊(duì)列,那么每個(gè)進(jìn)程都會(huì)收到同一個(gè)消息隊(duì)列的句柄。創(chuàng)建消息隊(duì)列的調(diào)用必須傳遞一個(gè)MSGQUEOPTIONS結(jié)構(gòu)來(lái)指定消息隊(duì)列中的最大項(xiàng)目數(shù)、隊(duì)列中每條消息的最大大小以及請(qǐng)求的是只讀句柄還是只寫(xiě)句柄.
如果您的消息隊(duì)列僅用于將信息移動(dòng)到同一進(jìn)程中的線程(在這種情況下,消息隊(duì)列可能沒(méi)有名稱(chēng)),您將需要使用您創(chuàng)建的第一個(gè)消息隊(duì)列的句柄來(lái)創(chuàng)建另一個(gè)句柄使用OpenMsgQueue附加到同一個(gè)隊(duì)列。如果在創(chuàng)建新消息句柄時(shí)沒(méi)有此函數(shù),則無(wú)法指定正在創(chuàng)建的句柄應(yīng)附加到已存在的先前隊(duì)列。
消息隊(duì)列由**作系統(tǒng)創(chuàng)建,是一種系統(tǒng)資源。當(dāng)您不再需要它時(shí),您省略該標(biāo)志。如果創(chuàng)建讀取器或 writer 導(dǎo)致創(chuàng)建隊(duì)列,則GetLastWin32Error返回SUCCESS(數(shù)值 0),否則返回,ERROR_ALREADY_EXISTS只要返回非零句柄CreateMsgQueue,則調(diào)用成功。GetLastWin32Error()隊(duì)列的句柄剛剛創(chuàng)建。下面是基本構(gòu)造函數(shù)的代碼:
C#
internal MessageQueue(string name, bool readAccess, int maxItems, int itemSizeInBytes)
{
MsgQueueOptions options = GetMessageQueueOptions
(readAccess, maxItems, itemSizeInBytes);
_hSyncHandle = CoreDLL.CreateMsgQueue(name, options);
int lastError = Marshal.GetLastWin32Error();
if (IntPtr.Zero.Equals(_hSyncHandle))
{
throw new ApplicationException(String.Format("Could not create or
open message queue {0}. LastWin32Error={1}", name, lastError));
}
_firstInstance = (0 == lastError);
}
另一個(gè)重要的構(gòu)造函數(shù)是使用另一個(gè)隊(duì)列端點(diǎn)作為參數(shù)創(chuàng)建一個(gè)連接到現(xiàn)有隊(duì)列的隊(duì)列端點(diǎn)。如果您正在使用沒(méi)有名稱(chēng)的隊(duì)列,那么這是您能夠?yàn)殛?duì)列創(chuàng)建另一個(gè)端點(diǎn)的唯一方法。本機(jī)函數(shù)OpenMsgQueue用于執(zhí)行此**作。像CreateMsgQueue這種方法需要一個(gè)MsgQueueOptions. 由OpenMsgQueue使用的選項(xiàng)結(jié)構(gòu)中唯一的參數(shù)是dwSize和bReadAccess?;鞠㈥?duì)列類(lèi)的另一個(gè)構(gòu)造函數(shù)如下:
C#
internal MessageQueue(MessageQueue source, int maxCount, bool readOnly)
{
_firstInstance = false;
MsgQueueOptions options = GetMessageQueueOptions(readOnly, maxCount, 0);
IntPtr processID = (IntPtr)Process.GetCurrentProcess().Id;
_hSyncHandle = CoreDLL.OpenMsgQueue(processID, source._hSyncHandle, options);
if (_hSyncHandle.Equals(IntPtr.Zero))
{
int errorCode = Marshal.GetLastWin32Error();
QueueResult result = Win32ErrorCodeToQueueResult(errorCode);
string errorMessage = String.Format("Error occurred opening
read message queue (Win32Error={0}, QueueResult={1}", errorCode, result);
if (result == QueueResult.InvalidHandle)
{
CoreDLL.CloseMsgQueue(_hSyncHandle);
_hSyncHandle = IntPtr.Zero;
}
throw new ApplicationException(errorMessage);
}
}
這個(gè)消息隊(duì)列實(shí)現(xiàn)是一次性的;當(dāng)它不再被使用時(shí),可以通過(guò)調(diào)用 dispose 消息來(lái)清理它的資源。
子類(lèi)化隊(duì)列
當(dāng)我為消息隊(duì)列功能編寫(xiě)包裝器時(shí),我考慮過(guò)在開(kāi)發(fā)人員嘗試從只寫(xiě)隊(duì)列讀取或?qū)懭胫蛔x隊(duì)列時(shí)拋出異常。根本不允許開(kāi)發(fā)人員執(zhí)行此類(lèi)無(wú)效**作更有意義。所以我把這個(gè)MessageQueue 類(lèi)分成兩個(gè)類(lèi);MessageQueueReader和MessageQueueWriter。每個(gè)都包含一組用于讀取或?qū)懭胂㈥?duì)列的方法,但不能同時(shí)包含兩者。這些類(lèi)的構(gòu)造函數(shù)只調(diào)用readOnly 參數(shù)設(shè)置為trueor的基本構(gòu)造函數(shù)false。
寫(xiě)入隊(duì)列
用于寫(xiě)入隊(duì)列的方法使用WriteMsgQueue. 如果隊(duì)列中沒(méi)有空間可用于寫(xiě)入消息,該方法將阻止調(diào)用者。CreateMsgQueue接受一個(gè)名為dwTimeout的參數(shù),用于指定調(diào)用者在寫(xiě)入請(qǐng)求被視為失敗之前將等待多長(zhǎng)時(shí)間。如果此值設(shè)置為INFINITE(數(shù)值 -1),則調(diào)用將無(wú)限期阻塞,直到有足夠的可用空間來(lái)執(zhí)行寫(xiě)入。
在我對(duì)這個(gè)包裝器的實(shí)現(xiàn)中,開(kāi)發(fā)人員只能以字節(jié)數(shù)組的形式傳遞要寫(xiě)入隊(duì)列的信息。我考慮過(guò)使用泛型來(lái)使代碼更靈活,但我發(fā)現(xiàn)了一些誤用和濫用這種實(shí)現(xiàn)的方法。將她/他的數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組是開(kāi)發(fā)人員的負(fù)擔(dān)。在我的實(shí)現(xiàn)中公開(kāi)了兩種寫(xiě)方法。第一個(gè)接受消息字節(jié)數(shù)組和超時(shí)值。第二個(gè)僅包含消息字節(jié)并假定超時(shí)值為INFINITE。
從隊(duì)列中讀取
Read方法反映到Write方法;開(kāi)發(fā)人員為該方法提供一個(gè)字節(jié)緩沖區(qū)和一個(gè)可選的超時(shí)值。如果沒(méi)有可讀取的內(nèi)容,則調(diào)用將阻塞,并且超時(shí)值控制方法在讀取嘗試被視為失敗之前等待消息的時(shí)間。
讀取和寫(xiě)入結(jié)果
由于嘗試從隊(duì)列讀取或?qū)懭腙?duì)列的失敗是正常執(zhí)行流程的一部分,因此我決定在寫(xiě)入請(qǐng)求失敗時(shí)不拋出異常。拋出異常會(huì)影響性能,所以我盡量不要不必要地拋出它們。相反,這些方法返回枚舉類(lèi)型的值QueueResult。QueueResult.OK表示讀取或?qū)懭胝?qǐng)求的成功完成,其他值表示失敗的原因(例如寫(xiě)入請(qǐng)求超時(shí))。
代碼示例讀取器/寫(xiě)入器客戶(hù)端
讀取器/寫(xiě)入器客戶(hù)端示例創(chuàng)建一個(gè)消息隊(duì)列,并允許用戶(hù)從主 (UI) 線程將消息添加到隊(duì)列,并在單獨(dú)的線程上處理來(lái)自隊(duì)列的消息。從用戶(hù)體驗(yàn)的角度來(lái)看,沒(méi)有太多可看的。有趣的工作都在代碼中。
C#
///
/// Waits on messages to be placed on the queue and displays them as they arrive
///
void ReaderThread()
{
using(_reader)
{
while (!_shuttingDown)
{
//The following call will block this thread until there is either a message
//on the queue to read or the thread is being signalled to run to prepare
//for program termination. Since the following call blocks the thread until
//it is time to do work it is not subject to the same batter killing
//affect of other similar looking code patterns
//( http://tinyurl.com/6rxoc6 ).
if (SyncBase.WaitForMultipleObjects(_readerWaitEvent, _reader) == _reader)
{
string msg;
_reader.Read(out msg); //Get the next message
AppendMessage(msg); //Display the thread to the user
}
}
}
}
/// <summary> /// Appends processed message to top of list box. /// </summary> /// <param name=""message""></param>public void AppendMessage(string message)
{
//If this is called from a secondary thread then marshal it to
//the primary thread.
if (this.InvokeRequired)
{
this.Invoke(_appendDelegate, new object[] { message });
}
else
{
this.lstReceivedMessages.Items.Insert(0, message);
}
} 寫(xiě)入客戶(hù)端
Writer 客戶(hù)端使用前面的代碼示例。它連接到與前面的代碼示例相同的隊(duì)列,并且用戶(hù)放置在隊(duì)列中的任何消息都將顯示在其他程序中(如果它正在運(yùn)行)。如果您在不啟動(dòng)閱讀器客戶(hù)端的情況下自行運(yùn)行編寫(xiě)器客戶(hù)端,則消息將在隊(duì)列中累積直到填滿(mǎn)。如果您嘗試在隊(duì)列已滿(mǎn)時(shí)將消息寫(xiě)入隊(duì)列,請(qǐng)求將阻塞 4 秒,第二返回超時(shí)結(jié)果。
電源通知隊(duì)列
電源通知隊(duì)列示例與我在Windows Mobile Power Management上發(fā)表的一篇文章有?關(guān)。該程序創(chuàng)建一個(gè)隊(duì)列閱讀器,并在調(diào)用本機(jī)函數(shù)時(shí)將句柄傳遞給閱讀器隊(duì)列RequestPowerNotifications。**作系統(tǒng)第二將消息寫(xiě)入隊(duì)列以通知程序電源狀態(tài)的變化。通知附加到列表視圖的開(kāi)頭。在列表中選擇一個(gè)項(xiàng)目將導(dǎo)致相關(guān)的電源標(biāo)志顯示在屏幕底部。
請(qǐng)求電源通知產(chǎn)生的消息作為結(jié)構(gòu)傳遞,但我提供的包裝器適用于字節(jié)數(shù)組。我創(chuàng)建了一個(gè)新的隊(duì)列類(lèi)型,它繼承自MessageQueueReader并提供了Read返回電源隊(duì)列消息的實(shí)現(xiàn)。重載Read方法重建PowerBroadcast結(jié)構(gòu)。
C#
public PowerBroadcast Read(){PowerBroadcast retVal = new PowerBroadcast();int bytesRead;QueueResult result;result = Read(readBuffer, out bytesRead);if (QueueResult.OK == result){int message = readBuffer[0] | readBuffer[1] << 8 |readBuffer[2] << 0x10 | readBuffer[3] << 0x18;int flags = readBuffer[4] | readBuffer[5] << 8 |readBuffer[6] << 0x10 | readBuffer[7] << 0x18;int length = readBuffer[8] | readBuffer[9] << 8 |readBuffer[10] << 0x10 | readBuffer[11] << 0x18;
retVal.Message = (PowerBroadCastMessageType)message;retVal.Flags = (PowerBroadcastFlags)flags;retVal.Length = length;if ((length > 0)&&( (retVal.Message&PowerBroadCastMessageType.PBT_TRANSITION)==PowerBroadCastMessageType.PBT_TRANSITION)){retVal.SystemPowerState = TextEncoder.GetString(readBuffer,12,length);}}return retVal;}
結(jié)束
我已經(jīng)介紹了 Windows 消息隊(duì)列的基本知識(shí),所提供的信息對(duì)于更多需要使用消息隊(duì)列的場(chǎng)景來(lái)說(shuō)應(yīng)該綽綽有余。但不要停留在這篇文章中。繼續(xù)閱讀有關(guān)消息隊(duì)列和 MSDN 庫(kù)中的其他命名對(duì)象的信息。
拓展知識(shí):
原創(chuàng)文章,作者:九賢生活小編,如若轉(zhuǎn)載,請(qǐng)注明出處:http://xiesong.cn/8966.html