前沿拓展:
#程序員##IT教育##軟件開發(fā)##.net##軟件工程師#
銳英源精品原創(chuàng),禁止全文或局部轉(zhuǎn)載,禁止任何形式的非法使用,侵權(quán)必究。點(diǎn)名“簡易百科”和閑暇巴盜用銳英源原創(chuàng)內(nèi)容。
最近開發(fā)平臺,需要進(jìn)程間通信,看了些命名管道和復(fù)雜的消息隊(duì)列例子及文章,一直想找輕量的平臺,本文里介紹說消息隊(duì)列可以跨進(jìn)程,非常感興趣,翻譯學(xué)習(xí)也,也供大家學(xué)習(xí)。請記住,看不懂codeproject,請找銳英源軟件,學(xué)用開源軟件,請找銳英源軟件。
介紹
當(dāng)需要在不同程序之間傳遞信息時(shí),Windows Mobile 和 Windows CE 提供了多種技術(shù)和解決方案來做到這一點(diǎn)。信息可以通過共享存儲位置傳遞,例如注冊表、文件或數(shù)據(jù)庫。對于小消息的頻繁通信,可以將消息放在應(yīng)用程序消息泵上或通過消息隊(duì)列使用。消息隊(duì)列與**、信號量和互斥鎖屬于同一對象族;它們被命名為內(nèi)核對象。目前 .NET Framework 不直接支持這些對象。但是通過一些 P/Invokes 聲明,可以輕松訪問該功能。在本文中,我將展示如何與消息隊(duì)列功能進(jìn)行交互。
我的目標(biāo)不是對消息隊(duì)列做詳盡的解釋,而是為讀者提供足夠的信息來了解這個概念并繼續(xù)下去。
先決條件
本文建立在其他一些 Windows CE 內(nèi)核對象的概念之上,即**、互斥體和信號量。它還基于我在最近一篇關(guān)于.NET 的 Windows Mobile 本機(jī)線程同步的文章中介紹的代碼。在閱讀本文之前,請先閱讀以上文章;我擴(kuò)展了上一篇文章中的代碼,您在閱讀本文之前需要熟悉它。
原生函數(shù)和結(jié)構(gòu)
有許多本機(jī)函數(shù)是本文的核心??梢栽?MSDN 庫中找到有關(guān)函數(shù)的詳細(xì)信息。功能如下:
關(guān)閉消息隊(duì)列創(chuàng)建消息隊(duì)列獲取消息隊(duì)列信息打開消息隊(duì)列讀取消息隊(duì)列寫消息隊(duì)列
下面列出了用于其中一些功能的結(jié)構(gòu):
消息隊(duì)列信息消息隊(duì)列選項(xiàng)什么是消息隊(duì)列
最簡單的,隊(duì)列是一個有序列表??梢詫㈨?xiàng)目添加到列表中或從列表中移動。但是,不能從列表中的任意位置添加或刪除項(xiàng)目。項(xiàng)目只能從列表的開頭刪除,項(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)行通信。本文圍繞這兩個實(shí)現(xiàn)中的第一個展開。
消息隊(duì)列可以特定于某個進(jìn)程,也可以在進(jìn)程之間共享。在任何一種情況下,消息隊(duì)列的句柄都允許對隊(duì)列進(jìn)行只讀或只寫訪問。您不能使用同一個句柄同時(shí)讀取和寫入消息隊(duì)列。如果您已經(jīng)擁有消息隊(duì)列的句柄,則可以創(chuàng)建額外的句柄來讀取或?qū)懭腙P(guān)聯(lián)的隊(duì)列。這對于未命名的隊(duì)列尤其重要。
如前所述,本文中的代碼基于上一篇文章中的代碼構(gòu)建。上一篇文章中的代碼和本文中的代碼之間的關(guān)系在下面的類層次圖中可見。深藍(lán)色 ( MessageQueue, MessageQueueReader, MessageQueueWriter) 中的類是本文要添加的類。
創(chuàng)建和打開消息隊(duì)列
通過本機(jī)函數(shù)創(chuàng)建或打開消息隊(duì)列CreateMsgQueue。與上一篇文章中的同步對象一樣,必須為消息隊(duì)列分配一個名稱才能在進(jìn)程之間共享。如果多個進(jìn)程創(chuàng)建同名的消息隊(duì)列,那么每個進(jìn)程都會收到同一個消息隊(duì)列的句柄。創(chuàng)建消息隊(duì)列的調(diào)用必須傳遞一個MSGQUEOPTIONS結(jié)構(gòu)來指定消息隊(duì)列中的最大項(xiàng)目數(shù)、隊(duì)列中每條消息的最大大小以及請求的是只讀句柄還是只寫句柄.
如果您的消息隊(duì)列僅用于將信息移動到同一進(jìn)程中的線程(在這種情況下,消息隊(duì)列可能沒有名稱),您將需要使用您創(chuàng)建的第一個消息隊(duì)列的句柄來創(chuàng)建另一個句柄使用OpenMsgQueue附加到同一個隊(duì)列。如果在創(chuàng)建新消息句柄時(shí)沒有此函數(shù),則無法指定正在創(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òu)造函數(shù)是使用另一個隊(duì)列端點(diǎn)作為參數(shù)創(chuàng)建一個連接到現(xiàn)有隊(duì)列的隊(duì)列端點(diǎn)。如果您正在使用沒有名稱的隊(duì)列,那么這是您能夠?yàn)殛?duì)列創(chuàng)建另一個端點(diǎn)的唯一方法。本機(jī)函數(shù)OpenMsgQueue用于執(zhí)行此**作。像CreateMsgQueue這種方法需要一個MsgQueueOptions. 由OpenMsgQueue使用的選項(xiàng)結(jié)構(gòu)中唯一的參數(shù)是dwSize和bReadAccess?;鞠㈥?duì)列類的另一個構(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);
}
}
這個消息隊(duì)列實(shí)現(xiàn)是一次性的;當(dāng)它不再被使用時(shí),可以通過調(diào)用 dispose 消息來清理它的資源。
子類化隊(duì)列
當(dāng)我為消息隊(duì)列功能編寫包裝器時(shí),我考慮過在開發(fā)人員嘗試從只寫隊(duì)列讀取或?qū)懭胫蛔x隊(duì)列時(shí)拋出異常。根本不允許開發(fā)人員執(zhí)行此類無效**作更有意義。所以我把這個MessageQueue 類分成兩個類;MessageQueueReader和MessageQueueWriter。每個都包含一組用于讀取或?qū)懭胂㈥?duì)列的方法,但不能同時(shí)包含兩者。這些類的構(gòu)造函數(shù)只調(diào)用readOnly 參數(shù)設(shè)置為trueor的基本構(gòu)造函數(shù)false。
寫入隊(duì)列
用于寫入隊(duì)列的方法使用WriteMsgQueue. 如果隊(duì)列中沒有空間可用于寫入消息,該方法將阻止調(diào)用者。CreateMsgQueue接受一個名為dwTimeout的參數(shù),用于指定調(diào)用者在寫入請求被視為失敗之前將等待多長時(shí)間。如果此值設(shè)置為INFINITE(數(shù)值 -1),則調(diào)用將無限期阻塞,直到有足夠的可用空間來執(zhí)行寫入。
在我對這個包裝器的實(shí)現(xiàn)中,開發(fā)人員只能以字節(jié)數(shù)組的形式傳遞要寫入隊(duì)列的信息。我考慮過使用泛型來使代碼更靈活,但我發(fā)現(xiàn)了一些誤用和濫用這種實(shí)現(xiàn)的方法。將她/他的數(shù)據(jù)轉(zhuǎn)換為字節(jié)數(shù)組是開發(fā)人員的負(fù)擔(dān)。在我的實(shí)現(xiàn)中公開了兩種寫方法。第一個接受消息字節(jié)數(shù)組和超時(shí)值。第二個僅包含消息字節(jié)并假定超時(shí)值為INFINITE。
從隊(duì)列中讀取
Read方法反映到Write方法;開發(fā)人員為該方法提供一個字節(jié)緩沖區(qū)和一個可選的超時(shí)值。如果沒有可讀取的內(nèi)容,則調(diào)用將阻塞,并且超時(shí)值控制方法在讀取嘗試被視為失敗之前等待消息的時(shí)間。
讀取和寫入結(jié)果
由于嘗試從隊(duì)列讀取或?qū)懭腙?duì)列的失敗是正常執(zhí)行流程的一部分,因此我決定在寫入請求失敗時(shí)不拋出異常。拋出異常會影響性能,所以我盡量不要不必要地拋出它們。相反,這些方法返回枚舉類型的值QueueResult。QueueResult.OK表示讀取或?qū)懭胝埱蟮某晒ν瓿桑渌当硎臼〉脑颍ɡ鐚懭胝埱蟪瑫r(shí))。
代碼示例讀取器/寫入器客戶端
讀取器/寫入器客戶端示例創(chuàng)建一個消息隊(duì)列,并允許用戶從主 (UI) 線程將消息添加到隊(duì)列,并在單獨(dú)的線程上處理來自隊(duì)列的消息。從用戶體驗(yàn)的角度來看,沒有太多可看的。有趣的工作都在代碼中。
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);
}
} 寫入客戶端
Writer 客戶端使用前面的代碼示例。它連接到與前面的代碼示例相同的隊(duì)列,并且用戶放置在隊(duì)列中的任何消息都將顯示在其他程序中(如果它正在運(yùn)行)。如果您在不啟動閱讀器客戶端的情況下自行運(yùn)行編寫器客戶端,則消息將在隊(duì)列中累積直到填滿。如果您嘗試在隊(duì)列已滿時(shí)將消息寫入隊(duì)列,請求將阻塞 4 秒,第二返回超時(shí)結(jié)果。
電源通知隊(duì)列
電源通知隊(duì)列示例與我在Windows Mobile Power Management上發(fā)表的一篇文章有?關(guān)。該程序創(chuàng)建一個隊(duì)列閱讀器,并在調(diào)用本機(jī)函數(shù)時(shí)將句柄傳遞給閱讀器隊(duì)列RequestPowerNotifications。**作系統(tǒng)第二將消息寫入隊(duì)列以通知程序電源狀態(tài)的變化。通知附加到列表視圖的開頭。在列表中選擇一個項(xiàng)目將導(dǎo)致相關(guān)的電源標(biāo)志顯示在屏幕底部。
請求電源通知產(chǎn)生的消息作為結(jié)構(gòu)傳遞,但我提供的包裝器適用于字節(jié)數(shù)組。我創(chuàng)建了一個新的隊(duì)列類型,它繼承自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ì)列的基本知識,所提供的信息對于更多需要使用消息隊(duì)列的場景來說應(yīng)該綽綽有余。但不要停留在這篇文章中。繼續(xù)閱讀有關(guān)消息隊(duì)列和 MSDN 庫中的其他命名對象的信息。
拓展知識:
原創(chuàng)文章,作者:九賢生活小編,如若轉(zhuǎn)載,請注明出處:http:///8966.html