windows ce 软件

前沿拓展:


#程序员##IT教育##软件开发##.net##软件工程师#

锐英源精品原创,禁止全文或局部转载,禁止任何形式的非法使用,侵权必究。点名“简易百科”和闲暇巴盗用锐英源原创内容。

最近开发平台,需要进程间通信,看了些命名管道和复杂的消息队列例子及文章,一直想找轻量的平台,本文里介绍说消息队列可以跨进程,非常感兴趣,翻译学习也,也供大家学习。请记住,看不懂codeproject,请找锐英源软件,学用开源软件,请找锐英源软件。

介绍

当需要在不同程序之间传递信息时,Windows Mobile 和 Windows CE 提供了多种技术和解决方案来做到这一点。信息可以通过共享存储位置传递,例如注册表、文件或数据库。对于小消息的频繁通信,可以将消息放在应用程序消息泵上或通过消息队列使用。消息队列与**、信号量和互斥锁属于同一对象族;它们被命名为内核对象。目前 .NET Framework 不直接支持这些对象。但是通过一些 P/Invokes 声明,可以轻松访问该功能。在本文中,我将展示如何与消息队列功能进行交互。

我的目标不是对消息队列做详尽的解释,而是为读者提供足够的信息来了解这个概念并继续下去。

先决条件

本文建立在其他一些 Windows CE 内核对象的概念之上,即**、互斥体和信号量。它还基于我在最近一篇关于.NET 的 Windows Mobile 本机线程同步的文章中介绍的代码。在阅读本文之前,请先阅读以上文章;我扩展了上一篇文章中的代码,您在阅读本文之前需要熟悉它。

原生函数和结构

有许多本机函数是本文的核心。可以在 MSDN 库中找到有关函数的详细信息。功能如下:

关闭消息队列创建消息队列获取消息队列信息打开消息队列读取消息队列写消息队列

下面列出了用于其中一些功能的结构:

消息队列信息消息队列选项什么是消息队列

最简单的,队列是一个有序列表。可以将项目添加到列表中或从列表中移动。但是,不能从列表中的任意位置添加或删除项目。项目只能从列表的开头删除,项目只能添加到列表的末尾。这些插入和删除规则通常被标记为 FIFO(先进先出)或 FCFS(先到先服务器)。Windows CE 设备为消息队列提供了两种实现。一种实现是**作系统的一部分,用于同一设备上的进程之间的通信。另一种是M**Q的实现,可以安装到 Windows CE 设备上,用于与其他机器进行通信。本文围绕这两个实现中的第一个展开。

消息队列可以特定于某个进程,也可以在进程之间共享。在任何一种情况下,消息队列的句柄都允许对队列进行只读或只写访问。您不能使用同一个句柄同时读取和写入消息队列。如果您已经拥有消息队列的句柄,则可以创建额外的句柄来读取或写入关联的队列。这对于未命名的队列尤其重要。

如前所述,本文中的代码基于上一篇文章中的代码构建。上一篇文章中的代码和本文中的代码之间的关系在下面的类层次图中可见。深蓝色 ( MessageQueue, MessageQueueReader, MessageQueueWriter) 中的类是本文要添加的类。

创建和打开消息队列

通过本机函数创建或打开消息队列CreateMsgQueue。与上一篇文章中的同步对象一样,必须为消息队列分配一个名称才能在进程之间共享。如果多个进程创建同名的消息队列,那么每个进程都会收到同一个消息队列的句柄。创建消息队列的调用必须传递一个MSGQUEOPTIONS结构来指定消息队列中的最大项目数、队列中每条消息的最大大小以及请求的是只读句柄还是只写句柄.

如果您的消息队列仅用于将信息移动到同一进程中的线程(在这种情况下,消息队列可能没有名称),您将需要使用您创建的第一个消息队列的句柄来创建另一个句柄使用OpenMsgQueue附加到同一个队列。如果在创建新消息句柄时没有此函数,则无法指定正在创建的句柄应附加到已存在的先前队列。

消息队列由**作系统创建,是一种系统资源。当您不再需要它时,您省略该标志。如果创建读取器或 writer 导致创建队列,则GetLastWin32Error返回SUCCESS(数值 0),否则返回,ERROR_ALREADY_EXISTS只要返回非零句柄CreateMsgQueue,则调用成功。GetLastWin32Error()队列的句柄刚刚创建。下面是基本构造函数的代码:

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);
}

另一个重要的构造函数是使用另一个队列端点作为参数创建一个连接到现有队列的队列端点。如果您正在使用没有名称的队列,那么这是您能够为队列创建另一个端点的唯一方法。本机函数OpenMsgQueue用于执行此**作。像CreateMsgQueue这种方法需要一个MsgQueueOptions. 由OpenMsgQueue使用的选项结构中唯一的参数是dwSize和bReadAccess。基本消息队列类的另一个构造函数如下:

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);
}
}

这个消息队列实现是一次性的;当它不再被使用时,可以通过调用 dispose 消息来清理它的资源。

子类化队列

当我为消息队列功能编写包装器时,我考虑过在开发人员尝试从只写队列读取或写入只读队列时抛出异常。根本不允许开发人员执行此类无效**作更有意义。所以我把这个MessageQueue 类分成两个类;MessageQueueReader和MessageQueueWriter。每个都包含一组用于读取或写入消息队列的方法,但不能同时包含两者。这些类的构造函数只调用readOnly 参数设置为trueor的基本构造函数false。

写入队列

用于写入队列的方法使用WriteMsgQueue. 如果队列中没有空间可用于写入消息,该方法将阻止调用者。CreateMsgQueue接受一个名为dwTimeout的参数,用于指定调用者在写入请求被视为失败之前将等待多长时间。如果此值设置为INFINITE(数值 -1),则调用将无限期阻塞,直到有足够的可用空间来执行写入。

在我对这个包装器的实现中,开发人员只能以字节数组的形式传递要写入队列的信息。我考虑过使用泛型来使代码更灵活,但我发现了一些误用和滥用这种实现的方法。将她/他的数据转换为字节数组是开发人员的负担。在我的实现中公开了两种写方法。第一个接受消息字节数组和超时值。第二个仅包含消息字节并假定超时值为INFINITE。

从队列中读取

Read方法反映到Write方法;开发人员为该方法提供一个字节缓冲区和一个可选的超时值。如果没有可读取的内容,则调用将阻塞,并且超时值控制方法在读取尝试被视为失败之前等待消息的时间。

读取和写入结果

由于尝试从队列读取或写入队列的失败是正常执行流程的一部分,因此我决定在写入请求失败时不抛出异常。抛出异常会影响性能,所以我尽量不要不必要地抛出它们。相反,这些方法返回枚举类型的值QueueResult。QueueResult.OK表示读取或写入请求的成功完成,其他值表示失败的原因(例如写入请求超时)。

代码示例读取器/写入器客户端

读取器/写入器客户端示例创建一个消息队列,并允许用户从主 (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上发表的一篇文章有​关。该程序创建一个队列阅读器,并在调用本机函数时将句柄传递给阅读器队列RequestPowerNotifications。**作系统第二将消息写入队列以通知程序电源状态的变化。通知附加到列表视图的开头。在列表中选择一个项目将导致相关的电源标志显示在屏幕底部。

请求电源通知产生的消息作为结构传递,但我提供的包装器适用于字节数组。我创建了一个新的队列类型,它继承自MessageQueueReader并提供了Read返回电源队列消息的实现。重载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;}

结束

我已经介绍了 Windows 消息队列的基本知识,所提供的信息对于更多需要使用消息队列的场景来说应该绰绰有余。但不要停留在这篇文章中。继续阅读有关消息队列和 MSDN 库中的其他命名对象的信息。

拓展知识:

原创文章,作者:九贤生活小编,如若转载,请注明出处:http://www.wangguangwei.com/8966.html