Click here to Skip to main content
Click here to Skip to main content

Windows Mobile Interprocess Communication with Message Queues in .NET

By , 15 Nov 2008
 

Introduction

When the need arises to pass information between different programs, Windows Mobile and Windows CE offer a variety of technologies and solutions to do so. Information can be passed through shared storage locations such as the registry, files, or a database. For frequent communication of small messages, one can place messages on an applications message pump or use through message queues. Message Queues are in the same family of objects as events, semaphores, and mutexes; they are named kernel objects. Presently the .NET Framework does not support these objects directly. But through a few P/Invokes declarations, the functionality can easily be accessed. Within this article, I show how to interact with the message queue functionality.

My goal is not to do an exhaustive explanation of message queues, but to cover enough information for the readers to pick up the concept and carry on.

Prerequisites

This article builds upon the concepts of some other Windows CE kernel objects, namely the event, mutex, and semaphore. It also builds upon code that I presented within a recent article on Windows Mobile Native Thread Synchronization for .NET. Before reading through this article, please read the above article first; I extend the code from this previous article and you will want to have familiarity with it before reading this article.

Native Functions and Structures

There are a number of native functions that are central to this article. Detailed information on the functions can be found within the MSDN library. The functions are listed below:

The structures used for some of these functions are listed below:

What is a Message Queue

At its simplest, a queue is an ordered list. An item can be added to the list or moved from the list. However one cannot add or remove items from arbitrary positions within the list. Items can only be removed from the beginning of the list and items can only be added to the end of the list. These rules on insertion and removal are often labelled as FIFO (First In First Out) or FCFS (First Come First Server). Windows CE devices offer two implementations for message queues. One implementation is a part of the operation system and is used for communication among processes on the same device. The other is an implementation of MSMQ and can be installed onto a Windows CE device and can be used for communicating with other machines. This paper is centered around the first of the two implementations.

A message queue can be specific to a process or shared among processes. In either case, a handle to a message queue allows either read-only or write-only access to the queue. You cannot both read and write to a message queue using the same handle. If you already have a handle to a message queue, you can create extra handles for reading or writing to the associated queue. This is especially important for unnamed queues.

As previously mentioned, the code in this article builds upon the code from a previous article. The relationship between the code in the previous article and the code within this article is visible below in the class hierarchy diagram. The classes in the dark blue (MessageQueue, MessageQueueReader, MessageQueueWriter) are the classes being added in this article.

Wrapper Class Hierarchy

Creating and Opening a Message Queue

A message queue is created or opened through the native function CreateMsgQueue. Like the synchronization objects in the previous article, the message queue must be assigned a name to be shared among processes. If multiple processes create message queues with the same name, then each process will receive a handle to the same message queue. The call to create the message queue must pass a MSGQUEOPTIONS structure to specify the maximum number of items that can be in the message queue, the maximum size of each message on the queue, and whether a read-only or write-only handle is being requested.

If your message queues are only being used to move information around to threads within the same process (in which case the message queue probably has no name), you will need to use the handle to the first message queue that you created to create another handle that attaches to the same queue using OpenMsgQueue. Without this function as you create new message handles, you have no way to specify that the handle you are creating should be attached to a previous queue that already exists.

Message queues are created by the operating system and are a system resource. You must be sure to free your handle to the message queue when you no longer have a need for it. You can free a message queue handle with CloseMsgQueue.

The Base Class

Like the system event, semaphore, and mutex message queues are waitable, can optionally have names, and are tied to resources through a handle that must be manually cleared. The SyncBase class from the previous article was already designed to hold a handle to a system resource. The layout of the class is displayed below:

public abstract class SyncBase : IDisposable
    {
        protected IntPtr _hSyncHandle;
        protected bool _firstInstance;

        public bool FirstInstance
        {
            get { return _firstInstance; }
        }

        public IntPtr SyncHandle
        {
            get { return _hSyncHandle; }
        }

        public static SyncBase WaitForMultipleObjects
		(int timeout, params SyncBase[] syncObjectList) {...}

        public bool SetEvent() {...}


        public static SyncBase WaitForMultipleObjects
		(params SyncBase[] syncObjectList) {...}

        public WaitObjectReturnValue Wait(int timeout) {...}

        public WaitObjectReturnValue Wait() {...}


        #region IDisposable Members

        public virtual void Dispose()
        {
            if(!_hSyncHandle.Equals(IntPtr.Zero))
                CoreDLL.CloseHandle(_hSyncHandle);
        }

        #endregion
    }

As with the system event, when code waits on a queue it will be blocked until the queue is in a signalled state. For a read-only handle to a queue, the signalled state means that there is data ready to be read from the queue. For a write-only handle, the signalled state means that there is enough room in the queue for more messages. The handle is no longer signalled once the queue is full. So the Wait methods defined in the base class are usable without modification.

The constructor must be defined and call CreateMsgQueue. The handle returned from the call will be saved in the member _hSyncHandle.

Constructors

While there are several message queue constructors, they all call one of two foundational constructors. The native function CreateMsgQueue is used in the constructor and it requires the MSGQUEUEOPTIONS parameter for the creation parameters. I've implemented the managed class MsgQueueOptions to reflect the structure and have wrapped the creation and initiation of instances of this class in the method GetMessageQueueOptions. There is a message queue setting that I've now allowed the caller. It is the dwFlags member of MsgQueueOptions. The options that can be set through this flag are MSGQUEUE_NOPRECOMMIT and MSGQUEUE_ALLOW_BROKEN. MSGQUEUE_NOPRECOMMIT which prevents the system from preallocating the memory needed for the message queue and causes memory to be allocated as needed. MSGQUEUE_ALLOW_BROKEN is used to allow a writer queue to be usable even if there is no reader queue on the other end. IF this option is not specified and if there is an attempt to write to the queue before a reader is attached to the queue, then the handle becomes immediately unusable (for that reason, I would never omit that flag. If the creation of the reader or writer results in the creation of the queue, then GetLastWin32Error will return SUCCESS (numerical value 0). Otherwise it returns ERROR_ALREADY_EXISTS. As long as a non-zero handle is returned by CreateMsgQueue, then the call was successful. I use GetLastWin32Error() to determine whether or not the first handle to the queue was just created. Here is the code to the base constructor:

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

The other important constructor is to create a queue endpoint that is connected to an existing queue using another queue endpoint as an argument. If you are working with a queue that has no name, then this is the only way that you will be able to create another endpoint to the queue. The native function OpenMsgQueue is used to do this. Like CreateMsgQueue this method requires a MsgQueueOptions. The only arguments within the options structure that are used by OpenMsgQueue are dwSize and bReadAccess. The other constructor for the base message queue class follows:

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

This message queue implementation is disposable; when it is no longer being used its resources can be cleaned up by calling the dispose message.

Subclassing the Queue

When I was writing the wrapper for the message queue functionality, I considered throwing an exception if a developer tried to read from a write-only queue or write to a read-only queue. It made more sense to simply not allow a developer to perform such an invalid action. So I subclassed the MessageQueue class into two classes; MessageQueueReader and MessageQueueWriter. Each one either contains a set of methods for reading or writing to a message queue but not both. The constructors for these classes only call the base constructor with the readOnly argument set to true or false.

Writing to a Queue

The methods that are used for writing to a queue use WriteMsgQueue. The method will block the caller if there is no space on the queue in which to write a message. CreateMsgQueue accepts a parameter named dwTimeout for specifying how long a caller will wait before the write request is considered to be a failure. If this values is set to INFINITE (numerical value -1), then the call will block indefinitely until there is enough free space to perform the write.

Within my implementation of this wrapper, the developer can only pass information to be written to the queue in the form of an array of bytes. I thought about using generics to make the code more flexible but I found a few ways by which such an implementation is misused and abused. It is the burden of the developer to convert her/his data to a byte array. There are two write methods exposed in my implementation. The first accepts the message byte array and a timeout value. The second contains only the message bytes and assumes the timeout value to be INFINITE.

Reading from a Queue

The Read methods are reflections of the Write methods; the developer gives the method a byte buffer and optionally a timeout value. The call will block if there is nothing available to read and the timeout value controls how long the method will wait for a message before the read attempt is considered a failure.

Read and Write Results

Since failures in attempts to read from a queue or write to a queue are expected to be a part of the normal flow of execution, I have decided not to throw exceptions when a write request fails. There are performance implications with throwing exceptions so I try not to throw them unnecessarily. Instead these methods return a value of the enumeration type QueueResult. QueueResult.OK represents successful completion of a read or write request and the other values represent failure with a reason (such as the write request timing out).

Code Examples

Reader/Writer Client

The reader/writer client example creates a message queue and allows the user to add messages to the queue from the main (UI) thread and processes the messages from the queue on a separate thread. From the standpoint of the user experience, there's not much to look at. The interesting workings are all in the code.

/// <summary>
/// Waits on messages to be placed on the queue and displays them as they arrive
/// </summary>
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 Client

The Writer client works with the previous code example. It connects to the same queue as the previous code example and any messages that the user places on the queue will show up in the other program (if it is running). If you run the writer client by itself without starting the reader client, then the messages will accumulate on the queue until it is full. If you attempt to write a message to the queue while it is full, the request will block for 4 seconds and then return a timeout result.

Power Notification Queue

The power notification queue example has relevance to an article I posted on Windows Mobile Power Management. The program creates a queue reader and passes the handle to the reader queue in a call to the native function RequestPowerNotifications. The operating system will then write messages to the queue to notify the programs of changes in power state. The notifications are appended to beginning of a list view. Selecting an item in the list will cause the relevant power flags to be displayed at the bottom of the screen.

The messages that result from requesting power notifications are passed as structs, but the wrapper I have provided works with byte arrays. I've created a new queue type that inherits from the MessageQueueReader and provides an implementation of Read that returns power queue messages. The overloaded Read method reconstructs the PowerBroadcast structure.

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

Closing

I've covered the essentials of Windows messages queues and the information provided should be more than sufficient for more scenarios that require the use of message queues. But don't stop with this article. Continue to read on about message queues and the other named objects within the MSDN library.

History

  • 16th November, 2008 - Initial publication

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Joel Ivory Johnson
Software Developer
United States United States
Member
I attended Southern Polytechnic State University and earned a Bachelors of Science in Computer Science and later returned to earn a Masters of Science in Software Engineering.
 
For the past few years I've been providing solutions to clients using Microsoft technologies for web and Windows applications.
 
While most of my CodeProject.com articles are centered around Windows Phone it is only one of the areas in which I work and one of my interests. I also have interest in mobile development on Android and iPhone. Professionally I work with several Microsoft technologies including SQL Server technologies, Silverlight/WPF, ASP.Net and others. My recreational development interest are centered around Artificial Inteligence especially in the area of machine vision.
 

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralHelp, Project Type Not Supportedmembervictorasilvab14 Oct '10 - 3:04 
Hi, I am trying to open this solution but only the J2i.Net.PowerQueue opens ok. The others projects in the solution give me the error "Project Type Not Supported by this installation", I am using Visual Studio 2008 Professional Edition. What am I missing?
 
Thanks,
GeneralRe: Help, Project Type Not SupportedmemberJoel Ivory Johnson14 Oct '10 - 3:50 
Sounds like you don't have the Windows Mobile 6 Professional SDK installed.


J2i.net | @J2iNet | Mobile Device Development MVP

GeneralThis project has not worked for megrouphamityildirim21 Aug '10 - 14:24 
Hi, grate article,
 
I like this article Smile | :) but i did not work with source projects. I have downloaded and opened by VS2010. Now, i am seeing some projects under the solution explorer but they are empty. I think it is interesting with Windows Mobile Devices so i downloaded it from the Center and the version is 6.1.6965
i did not find the project's tool requirements at the Requirement part.
 
Pls. tell me how can i work this source files help me. i am new in mobile developments.
 
Thank you for any regards..
GeneralYou are using an incompatible version of Visual StudiomemberJoel Ivory Johnson22 Aug '10 - 0:38 
Microsoft announced several months ago that Visual Studio 2010 would not have support for Windows Mobile. Windows Mobile developers must continue to use Visual Studio 2008 using the professional edition or better.


J2i.net | @J2iNet | Mobile Device Development MVP

GeneralRe: You are using an incompatible version of Visual Studiogrouphamityildirim22 Aug '10 - 0:42 
oke, thank you
GeneralPOWER_BROADCAST_POWER_INFOmemberAlon Ronen26 Jan '10 - 14:12 
Hi Joel, hell of an article (and the "Windows Mobile Power Management" as well) - you got my 5 on both!
 
But I have a question - I'm using WinCE 6.0 device and I'm trying to get notification about both waking from Sleep mode (no problem overthere) and using charger/battery mode (POWER_BROADCAST_POWER_INFO).
 
for that I have combined code from your article with other code I found over the web:
 
                    ReadMsgQueue(ptrQueue, buf, CUInt(buf.Length), nRead, -1, flags)
 

                    Dim message As Integer = buf(0) Or buf(1) << 8 Or buf(2) << &H10 Or buf(3) << &H18
                    Dim flagX As Integer = buf(4) Or buf(5) << 8 Or buf(6) << &H10 Or buf(7) << &H18
                    Dim length As Integer = buf(8) Or buf(9) << 8 Or buf(10) << &H10 Or buf(11) << &H18
 

                    Dim PowerEventTypeMessage As PowerEventTypes = CType(message, PowerEventTypes)
                    Dim SystemPowerStatesMessage As SystemPowerStates = CType(flagX, SystemPowerStates)
 
                    RaiseEvent OnMessage2(SystemPowerStatesMessage, PowerEventTypeMessage)
 
                    Dim flag As UInteger = ConvertByteArray(buf, 4)
 
                    Select Case flag
                        Case 65536
                            msg = "Power On"
                            Exit Select
                        Case 131072
                            msg = "Power Off"
                            Exit Select
                        Case 262144
                            msg = "Power Critical"
                            Exit Select
                        Case 524288
                            msg = "Power Boot"
                            Exit Select
                        Case 1048576
                            msg = "Power Idle"
                            Exit Select
                        Case 2097152
                            msg = "Power Suspend"
                            Exit Select
                        Case 8388608
                            msg = "Power Reset"
                            Exit Select
                        Case 0
                            ' non power transition messages are ignored 
                            Exit Select
                        Case Else
                            msg = "Unknown Flag: " & flag
                            Exit Select
                    End Select
 

                    If msg IsNot Nothing Then ' AndAlso OnMessage IsNot Nothing Then
                        RaiseEvent OnMessage(msg)
                    End If
 
 
where ConvertByteArray(buf, 4) is
    
Private Function ConvertByteArray(ByVal array As Byte(), ByVal offset As Integer) As UInteger
        Dim res As UInteger = 0
        res += array(offset)
        res += array(offset + 1) * CUInt(&H100)
        res += array(offset + 2) * CUInt(&H10000)
        res += array(offset + 3) * CUInt(&H1000000)
        Return res
    End Function
 
so,
 
I'm getting notification about the sleep mode when the devcie wakes up (actually 3 notifications with the 'suspend' msg, why 3 ?!)
 
and I'm getting notifications with PBT_POWERINFOCHANGE when dis/connecting the charger but the 'SystemPowerStatesMessage' is always 0. If I got it right, I'm suppose to get there the 'POWER_BROADCAST_POWER_INFO' structure, no?
 
I hope I'm clear enough.
 
Thanks,
 
Alon

QuestionHelpmemberSunshine Always11 Dec '09 - 6:28 
Hi,
 
I need urgent help in fixing a problem. I am using this PowerManager.cs in my Application using VS2008 C#. This starts a thread to capture the notifications in a message queue. once i get the reset event of the device, i will show some dialog. all this is being done perfectly. The problem comes when i want to end this thread. The thread is not closing when i call the disableEvents function. The Thread.join is not working.
Can u please help me with this.I'd really appreciate it.
 
Its really urgent for me.
I'm attaching the code here.Thanks.
 

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.IO;
 
namespace WakeUp
{

public enum PowerState
{
On = 0x00010000,
Off = 0x00020000,
Critical = 0x00040000,
Boot = 0x00080000,
Idle = 0x00100000,
Suspend = 0x00200000,
Reset = 0x00800000
}
 

 
public partial class CPowerManager : Component
{
#region Power Management API Declarations
 
[Flags]
private enum PowerEventType
{
PBT_TRANSITION = 0x00000001,
PBT_RESUME = 0x00000002,
PBT_POWERSTATUSCHANGE = 0x00000004,
PBT_POWERINFOCHANGE = 0x00000008,
PBT_OEMBASE = 0x00010000,
POWER_NOTIFY_ALL = -1
}
 
private const UInt32 INFINITE = UInt32.MaxValue;
 
private class POWER_BROADCAST
{
byte[] buffer;
 
public POWER_BROADCAST(int size)
{
buffer = new byte[size];
}
 
public byte[] Data
{
get
{
return buffer;
}
}
 
public PowerEventType Message
{
get
{
return (PowerEventType)BitConverter.ToInt32(buffer, 0);
}
}
 
public PowerState Flags
{
get
{
return (PowerState)BitConverter.ToInt32(buffer, 4);
}
}
 
public int Length
{
get
{
return BitConverter.ToInt32(buffer, 8);
}
}
 
public byte[] SystemPowerState
{
get
{
byte[] data = new byte[Length];
Buffer.BlockCopy(buffer, 12, data, 0, Length);
return data;
}
}
}
 
[StructLayout(LayoutKind.Sequential)]
private struct MSGQUEUEOPTIONS
{
public UInt32 dwSize;
public UInt32 dwFlags;
public UInt32 dwMaxMessages;
public UInt32 cbMaxMessage;
public Int32 bReadAccess;
};
 
[StructLayout(LayoutKind.Sequential)]
struct MSGQUEUEINFO
{
public UInt32 dwSize;
public UInt32 dwFlags;
public UInt32 dwMaxMessages;
public UInt32 cbMaxMessage;
public UInt32 dwCurrentMessages;
public UInt32 dwMaxQueueMessages;
public UInt16 wNumReaders;
public UInt16 wNumWriters;
};
 
private enum PowerOptions
{
None = 0,
Force = 0x00001000
}
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static int GetSystemPowerState(string psState, UInt32 dwLength, out PowerState flags);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static int SetSystemPowerState(string psState, PowerState flags, PowerOptions options);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static IntPtr RequestPowerNotifications(IntPtr messageQueueHandle, int flags);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static bool StopPowerNotifications(IntPtr requestHandle);
 

[DllImport("coredll.dll", SetLastError = true)]
extern private static int ReleasePowerRequirement(IntPtr powerRequirementHandle);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static void SystemIdleTimerReset();

#endregion
 
#region MsgQueue API Declarations
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static IntPtr CreateMsgQueue(string lpName, ref MSGQUEUEOPTIONS lpOptions);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static IntPtr OpenMsgQueue(IntPtr hSrcProc, IntPtr hMsgQ, ref MSGQUEUEOPTIONS lpOptions);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static Int32 ReadMsgQueue(IntPtr hMsgQ, byte[] lpBuffer, UInt32 cbBufferSize,
out UInt32 lpNumberOfBytesRead, UInt32 dwTimeout, out UInt32 pdwFlags);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static Int32 WriteMsgQueue(IntPtr hMsgQ, byte[] lpBuffer, UInt32 cbDataSize,
UInt32 dwTimeout, UInt32 dwFlags);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static Int32 GetMsgQueueInfo(IntPtr hMsgQ, MSGQUEUEINFO lpInfo);
 
[DllImport("coredll.dll", SetLastError = true)]
extern private static Int32 CloseMsgQueue(IntPtr hMsgQ);
 
#endregion
 
#region Synchronization API Declarations
 
private enum EventModification
{
EventPulse = 1,
EventReset = 2,
EventSet = 3
}
 
private const int WAIT_OBJECT_0 = 0;
private const int EVENT_SET = 3;
 
[DllImport("coredll.dll")]
static extern IntPtr CreateEvent(int dwReserved1, bool bManualReset, bool bInitialState, string Name);
 
[DllImport("coredll.dll")]
static extern Int32 EventModify(IntPtr hEvent, EventModification modification);
 
[DllImport("coredll.dll", EntryPoint = "CloseHandle", SetLastError= true)]
static extern Int32 CloseHandle(IntPtr h);
 
[DllImport("coredll.dll")]
static extern int WaitForMultipleObjects(UInt32 nCount, IntPtr[] hHandles, Int32 fWaitAll, UInt32 dwMilliseconds);

#endregion
 

 
public event EventHandler PowerStateResume;

private IntPtr queueHandle;
private IntPtr notificationHandle;
private IntPtr closeEventHandle;
 
private Thread workerThread;
 

private bool notificationsEnabled;
public bool NotificationsEnabled
{
get
{
return notificationsEnabled;
}
}
 
public CPowerManager()
{
EnableEvents();
}

private AutoResetEvent sync;
 
public void EnableEvents()
{
if (!notificationsEnabled)
{
MSGQUEUEOPTIONS options = new MSGQUEUEOPTIONS();
 
options.dwSize = (UInt32)Marshal.SizeOf(options);
options.cbMaxMessage = 1024;
options.bReadAccess = 0;
 
queueHandle = CreateMsgQueue("PowerEventQueue", ref options);
 
closeEventHandle = CreateEvent(0, false, false, null);
 
workerThread = new Thread(new ThreadStart(WorkerThreadProc));
 
sync = new AutoResetEvent(false);
 
if (Site == null || !Site.DesignMode)
workerThread.Start();
 
sync.WaitOne();
 
notificationHandle = RequestPowerNotifications(queueHandle, (int)PowerEventType.POWER_NOTIFY_ALL);
 
notificationsEnabled = true;
}
}

private void WorkerThreadProc()
{
MSGQUEUEOPTIONS options = new MSGQUEUEOPTIONS();
 
options.dwSize = (UInt32)Marshal.SizeOf(options);
options.cbMaxMessage = 1024;
options.bReadAccess = 1;

IntPtr readHandle = CreateMsgQueue("PowerEventQueue", ref options);
 
sync.Set();
 
IntPtr[] handles = new IntPtr[] { closeEventHandle, readHandle };
for (; WaitForMultipleObjects(2, handles, 0, INFINITE) != WAIT_OBJECT_0; )
{

UInt32 bytesRead;
UInt32 flags;
 
POWER_BROADCAST pbr = new POWER_BROADCAST(1024);
 
if (ReadMsgQueue(readHandle, pbr.Data, 1024, out bytesRead, 0, out flags) == 0)
{
int r = Marshal.GetLastWin32Error();
continue;
}
 
ProcessPowerBroadcast(pbr);
}

CloseMsgQueue(readHandle);
}

public void DisableEvents()
{
System.Windows.Forms.MessageBox.Show("in disable events");
if (notificationsEnabled)
{
StopPowerNotifications(notificationHandle);
if (Site == null || !Site.DesignMode)
{
EventModify(closeEventHandle, EventModification.EventSet);
workerThread.Join();
System.Windows.Forms.MessageBox.Show("after thread join");
}
 
CloseMsgQueue(queueHandle);
CloseHandle(closeEventHandle);
 
notificationsEnabled = false;
System.Windows.Forms.MessageBox.Show("end of disable events");
}
}
private void ProcessPowerBroadcast(POWER_BROADCAST pbr)
{
switch (pbr.Message)
{

case PowerEventType.PBT_RESUME:
OnPowerStateResume(new PowerStateResumeEventArgs(pbr.Flags));
break;

}
}
 
protected virtual void OnPowerStateResume(PowerStateResumeEventArgs e)
{
if (PowerStateResume != null)
PowerStateResume(this, e);
}
 
}
 
public class PowerStateResumeEventArgs : EventArgs
{
public readonly PowerState PowerState;
 
internal PowerStateResumeEventArgs(PowerState state)
{
PowerState = state;
}
}
 

 
}
 


AnswerRe: HelpmemberJoel Ivory Johnson11 Dec '09 - 8:12 
By any chance is DisableEvents being called on a non-UI thread? How far into the function does it get before failing?
 

GeneralRe: HelpmemberSunshine Always12 Dec '09 - 21:17 
Thanks for the reply...
 
I kept debug points to get the exact point where it hangs, and I found that it doesnot return after
 
workerThread.Join(); .
I noticed this problem occurring in a specific manner. See the code below:

void pwmgr_PowerStateResume(object sender, EventArgs e)
{
MessageBox.Show(ps.PowerState.ToString());
if (ps.PowerState.ToString() == "Reset")
{
Form2 f2 = new Form2();
f2.Width = 290;
f2.Height = 203;
f2.ShowDialog();
button2_Click(sender,e);
}
}
 
private void button2_Click(object sender, EventArgs e)
{
MessageBox.Show("in button2");
pwmgr.DisableEvents();
this.Close();
}

I don't reach the this.close() in button2 click event. but strangely enough, if i comment out the button2_Click(sender,e) in pwmgr_PowerStateResume() method. and click on button2 explicitly,the thread closes perfectly.
 
Apart from this, do you have anyother way in which i can carry out my requirement. Is there any other way to capture the Reset event of the Handheld...?
GeneralRe: HelpmemberJoel Ivory Johnson13 Dec '09 - 2:42 
Correct me if I am wrong but your pwmgr_PowerStateResume method is being called from a thread that is different than your UI thread. You are not supposed to interact with UI elements from any thread other than the primary thread. You need to marshal the call to the primary thread by using Control.Invoke. So in pwmgr_PowerStateResume instead of calleding button2_Click you will need to call this.Invoke(button2_click, new object[]{this,new EventArgs() }); (button2_click may need to be wrapped in a delegate).
 


Joel Ivory Johnson

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130523.1 | Last Updated 15 Nov 2008
Article Copyright 2008 by Joel Ivory Johnson
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid