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