Commits

Afriza N. Arief committed d44ca2b

Initial Commit: Some utility classes

These codes are published with permission from AGIS Pte Ltd

Comments (0)

Files changed (9)

P2PMsgQueueReader.cpp

+#include "StdAfx.h"
+#include "P2PMsgQueueReader.h"
+
+#include <msgqueue.h>
+
+#if _MSC_VER >= 1400 // if VS2005 and above
+#  include <atlsync.h>
+#else // older than VS2005: eVC++ 4
+#  include "AgisSync.h"
+#endif
+
+// We are not using containers from C++ Standard Template Library (STL) for
+// source code compatibility with eVC++ 4
+#include <afxtempl.h> // for Template-based MFC containers
+
+#include <utility> // std::pair
+
+class CP2PMsgQueueReader_Impl
+{
+	friend class CP2PMsgQueueReader;
+	CP2PMsgQueueReader* q;
+
+	LPCTSTR m_lpszP2PMsgQueue;
+	HANDLE m_hMsgQ;
+
+	CEvent m_hEventTerminate;
+	CHandle m_hThread;
+
+	//CP2PMsgQueueReader::TMessage m_msg;
+	DWORD m_nMsgSize;
+	DWORD m_nMsgQCapacity;
+	void* m_pMsg;
+
+	// Use a critical section as a giant lock until we find any problem which
+	// requires other workarounds
+	CCriticalSection m_CS;
+
+	typedef std::pair<CP2PMsgQueueReader::MsgQReaderCallback, LPVOID> TListener;
+	typedef CList<TListener,const TListener&> TListenerList;
+	TListenerList m_listeners;
+	void NotifyListeners(CP2PMsgQueueReader::Event evnt);
+
+	CP2PMsgQueueReader_Impl(DWORD nMsgSize, DWORD dwCapacity, LPCTSTR lpszMsgQueueName)
+		: q(NULL)
+		, m_nMsgSize(nMsgSize)
+		, m_nMsgQCapacity(dwCapacity)
+		, m_pMsg(malloc(nMsgSize))
+		, m_lpszP2PMsgQueue(lpszMsgQueueName)
+		, m_hMsgQ(NULL)
+
+		, m_hEventTerminate(CreateEvent(NULL,TRUE,FALSE,NULL))
+	{
+		ASSERT(nMsgSize > 0);
+		ASSERT((m_pMsg)!=NULL && AfxIsValidAddress(m_pMsg,m_nMsgSize,TRUE));
+	}
+public:
+	//friend inline void boost::checked_delete<>(CGpsP2PMsgQueueWriter_Impl * x);
+	~CP2PMsgQueueReader_Impl()
+	{
+		CCritSecLock csLock(m_CS);
+		if (m_pMsg)
+		{
+			free(m_pMsg);
+			m_pMsg = NULL;
+			m_nMsgSize = 0;
+		}
+	}
+private:
+	static DWORD ThreadProc(LPVOID pVoid)
+	{
+		return reinterpret_cast<CP2PMsgQueueReader_Impl*>(pVoid)->
+			ThreadProc();
+	};
+	DWORD ThreadProc();
+	BOOL OpenQueue(LPCTSTR lpszName);
+	BOOL CloseQueue();
+};
+
+DWORD CP2PMsgQueueReader_Impl::ThreadProc()
+{
+	if(!OpenQueue(m_lpszP2PMsgQueue))
+	{
+		TRACE(_T("Unable to Open Point-to-point MsgQueue(%s)"),m_lpszP2PMsgQueue);
+		return 1;
+	}
+
+	// Let others do additional initializations if they need to. They can also
+	// cancel this thread by calling SignalToTerminate(). They should _NOT_
+	// call Stop() within the callback because the callback is called from this
+	// thread and Stop() will wait until this thread stops
+	// (i.e. the thread will wait for itself to die)
+	NotifyListeners(CP2PMsgQueueReader::Event_Initialize);
+
+	enum TPEvents {
+		TPWAIT_TERMINATE = WAIT_OBJECT_0,
+		TPWAIT_MSG_QUEUE,
+		TPWAIT_COUNT,
+	};
+	// TPWAIT_COUNT will trigger compile error if there are less enums than
+	// the handles. TPWAIT_COUNT is also useful when used with MsgWait*
+	HANDLE handles[TPWAIT_COUNT] = {
+		m_hEventTerminate,
+		m_hMsgQ,
+	};
+	ASSERT(TPWAIT_COUNT == sizeof(handles)/sizeof(handles[0]));
+
+	void* pMsg = malloc(m_nMsgSize);
+	DWORD wait = WAIT_TIMEOUT;
+	BOOL bContinue = TRUE;
+	do {
+		wait = WaitForMultipleObjects(TPWAIT_COUNT, handles, FALSE, INFINITE);
+		switch (wait)
+		{
+		case WAIT_FAILED:
+			TRACE(_T("Wait 0x%x failed @ thread id %ld"),m_hMsgQ,GetCurrentThreadId());
+		case TPWAIT_TERMINATE:
+			TRACE(_T("Terminating thread id %ld"),GetCurrentThreadId());
+			bContinue = FALSE;
+			break;
+		case TPWAIT_MSG_QUEUE:
+			DWORD dwBytesRead = 0;
+			DWORD dwFlags = 0;
+			BOOL bReadOk =
+			ReadMsgQueue(m_hMsgQ,pMsg,m_nMsgSize,&dwBytesRead, 1000,&dwFlags);
+			//TRACE(_T("wait:%d bReadOk:%d cbRead:%d flags: 0x%x"),wait,bReadOk,dwBytesRead,dwFlags);
+			if (bReadOk) {
+				CCritSecLock csLock(m_CS);
+				memcpy(m_pMsg,pMsg,m_nMsgSize);
+
+				NotifyListeners(
+					(dwFlags & MSGQUEUE_MSGALERT) != 0 ?
+					CP2PMsgQueueReader::Event_AlertMessage :
+					CP2PMsgQueueReader::Event_NewMessage );
+				// Sleep for a moment to allow other readers to fetch their share of data
+				// The sleeping interval needs to be the same among readers to be fair
+				// as well as thread's priority
+				Sleep(10);
+			}
+			break;
+		}
+	} while (bContinue);
+	if (pMsg)
+		free(pMsg);
+
+	// Let others do additional uninitialization if they need to.
+	NotifyListeners(CP2PMsgQueueReader::Event_Uninitialize);
+
+	// close the queue if it's not already closed by others
+	if(m_hMsgQ)
+		CloseQueue();
+	return 0;
+}
+
+void CP2PMsgQueueReader_Impl::NotifyListeners(CP2PMsgQueueReader::Event evnt)
+{
+	CCritSecLock csLock(m_CS);
+
+	POSITION pos = m_listeners.GetHeadPosition();
+	for(int i=0; i<m_listeners.GetCount(); i++)
+	{
+		TListener& pair = m_listeners.GetAt(pos);
+		pair.first(q, evnt, pair.second);
+	}
+}
+
+BOOL CP2PMsgQueueReader_Impl::OpenQueue(LPCTSTR lpszName)
+{
+	MSGQUEUEOPTIONS msgQueOpt = { sizeof(MSGQUEUEOPTIONS) };
+	msgQueOpt.dwFlags = MSGQUEUE_NOPRECOMMIT|MSGQUEUE_ALLOW_BROKEN;
+	msgQueOpt.dwMaxMessages = m_nMsgQCapacity;
+	msgQueOpt.cbMaxMessage = m_nMsgSize;
+	msgQueOpt.bReadAccess = TRUE;
+	// CreateMsgQueue(): NULL indicates failure. To obtain extended error information, call the GetLastError function.
+	m_hMsgQ = CreateMsgQueue(m_lpszP2PMsgQueue,&msgQueOpt);
+	TRACE(_T("CreateMsgQueue(%s): 0x%x"),lpszName,m_hMsgQ);
+	return m_hMsgQ != NULL;
+}
+
+BOOL CP2PMsgQueueReader_Impl::CloseQueue()
+{
+	HANDLE hMsgQ = m_hMsgQ;
+	m_hMsgQ = NULL;
+	BOOL bRet = 
+	CloseMsgQueue(hMsgQ);
+	TRACE(_T("CloseMsgQueue(0x%x): %d"),hMsgQ,bRet);
+	return bRet;
+}
+
+CP2PMsgQueueReader::CP2PMsgQueueReader(DWORD nMsgSize, DWORD dwCapacity, LPCTSTR lpszMsgQueueName)
+: d(new CP2PMsgQueueReader_Impl(nMsgSize,dwCapacity,lpszMsgQueueName))
+{
+	d->q = this;
+}
+
+CP2PMsgQueueReader::~CP2PMsgQueueReader(void)
+{
+	Stop();
+}
+
+UINT CP2PMsgQueueReader::GetMessageSize()
+{
+	return d->m_nMsgSize;
+}
+
+HANDLE CP2PMsgQueueReader::GetMsgQueueHandle()
+{
+	return d->m_hMsgQ;
+}
+
+BOOL CP2PMsgQueueReader::Start()
+{
+	CCritSecLock csLock(d->m_CS);
+
+	if (IsRunning())
+		return FALSE;
+
+	d->m_hEventTerminate.Reset();
+	d->m_hThread.Close();
+	d->m_hThread.Attach(
+		CreateThread(NULL, 0, &CP2PMsgQueueReader_Impl::ThreadProc, d.get(), 0, NULL)
+		);
+	return (HANDLE)d->m_hThread != NULL;
+}
+
+BOOL CP2PMsgQueueReader::IsRunning()
+{
+	return WaitForSingleObject(d->m_hThread,0) == WAIT_TIMEOUT;
+}
+
+void CP2PMsgQueueReader::SignalToStop()
+{
+	// if not signaled
+	if (WaitForSingleObject(d->m_hEventTerminate,0) != WAIT_OBJECT_0)
+	{
+		d->m_hEventTerminate.Set();
+		// Quickly Close the queue because the writer will duplicate a message
+		// based on the number of Queue Readers.
+		d->CloseQueue();
+		SetThreadPriority(d->m_hThread,THREAD_PRIORITY_ABOVE_NORMAL);
+	}
+}
+
+void CP2PMsgQueueReader::Stop()
+{
+	SignalToStop();
+
+	WaitForSingleObject(d->m_hThread, 3000);
+}
+
+int CP2PMsgQueueReader::GetNumWriters()
+{
+	if (d->m_hMsgQ == NULL)
+		return -1;
+
+	MSGQUEUEINFO msgQInfo = { sizeof(MSGQUEUEINFO) };
+	if (GetMsgQueueInfo(d->m_hMsgQ,&msgQInfo))
+		return msgQInfo.wNumWriters;
+	else
+		return -2;
+}
+
+BOOL CP2PMsgQueueReader::GetLastMessage(void* pMsg)
+{
+	ASSERT((pMsg)!=NULL && AfxIsValidAddress(pMsg,d->m_nMsgSize,TRUE));
+	
+	CCritSecLock csLock(d->m_CS);
+	// memcpy_s() returns:
+	// + Zero if successful; an error code on failure.
+	// + EINVAL if destination or source is NULL
+	// + ERANGE if destination is smaller in size than source
+	//return 0 == memcpy_s(pMsg,d->m_nMsgSize,d->m_pMsg,d->m_nMsgSize);
+
+	if (pMsg == NULL || d->m_pMsg == NULL)
+		return FALSE;
+
+	memcpy(pMsg,d->m_pMsg,d->m_nMsgSize);
+	return TRUE;
+}
+
+void* CP2PMsgQueueReader::GetLastMessagePtr()
+{
+	return d->m_pMsg;
+}
+
+VOID CP2PMsgQueueReader::AddListener(CP2PMsgQueueReader::MsgQReaderCallback func, LPVOID lpVoid)
+{
+	CCritSecLock csLock(d->m_CS);
+	d->m_listeners.AddTail(std::make_pair(func,lpVoid));
+}
+
+VOID CP2PMsgQueueReader::RemoveListener(CP2PMsgQueueReader::MsgQReaderCallback func, LPVOID lpVoid)
+{
+	CCritSecLock csLock(d->m_CS);
+	POSITION pos = d->m_listeners.Find(std::make_pair(func,lpVoid));
+	if (pos)
+		d->m_listeners.RemoveAt(pos);
+}
+
+VOID CP2PMsgQueueReader::RemoveAllListeners()
+{
+	CCritSecLock csLock(d->m_CS);
+	d->m_listeners.RemoveAll();
+}

P2PMsgQueueReader.h

+#pragma once
+
+// This class focuses on handling the details of Point-to-point Message Queue
+// Author: Afriza N. Arief (afriza.arief@asiagis.com)
+// Date: August 2010
+
+// Get Boost C++ Library from http://www.boost.org/ and add it to the "Additional include directories"
+// Alternatively, copy only necessary files to the MSVC Solution.
+#include <boost/scoped_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+class CP2PMsgQueueReader_Impl;
+class CP2PMsgQueueReader
+	: boost::noncopyable
+{
+	// D-Pointer ( http://www.google.com/search?q=D-Pointer , http://en.wikipedia.org/wiki/Opaque_pointer )
+	boost::scoped_ptr<CP2PMsgQueueReader_Impl> d;
+public:
+	// Specify/declare the type of the message
+	CP2PMsgQueueReader(DWORD nMsgSize, DWORD dwCapacity = 0, LPCTSTR lpszMsgQueueName = NULL);
+	~CP2PMsgQueueReader(void);
+	UINT GetMessageSize();
+	HANDLE GetMsgQueueHandle();
+
+	// Start a new thread and listen to incoming Message
+	BOOL Start();
+	// Check if thread is still running
+	BOOL IsRunning();
+	// Instruct the thread to terminate
+	void SignalToStop();
+	// Instruct the thread to terminate if it is not already terminating and
+	// wait the thread to stop completely
+	void Stop();
+
+	// Get the number of writers for the associated point-to-point message queue
+	// returns:
+	// -1, if the message queue handle is NULL (not created/opened yet)
+	// -2, if failed to get message queue information
+	// else, the number of writers for the message queue
+	int GetNumWriters();
+
+	// Retrieve the last message received
+	BOOL GetLastMessage(void* pMsg);
+	void* GetLastMessagePtr();
+	template<class T> T GetLastMessagePtr()
+	{
+		return reinterpret_cast<T>(GetLastMessagePtr());
+	}
+
+	// Register a listener that will be called back when there is a new message
+	// received.
+	// Note:
+	// Please do not do any heavy processing in the callback since it
+	// may interfere with the main loop for receiving message. It may lead
+	// to old message being kept in the queue and old message being received in 
+	// the next call.
+	// If you want to do heavy tasks during callback, you can create a new 
+	// wrapper class that has its own thread to do the callback.
+	// WARNING:
+	// Add/Remove may NOT be called inside OnNewMessageFunc callback or it may
+	// lead to undefined behaviour
+	typedef enum Event {
+		Event_Initialize,
+		Event_Uninitialize,
+		Event_NewMessage,
+		Event_AlertMessage,
+	} Event;
+	typedef void (*MsgQReaderCallback)(CP2PMsgQueueReader* pMQReader, CP2PMsgQueueReader::Event evnt, LPVOID lpVoid);
+	void AddListener(MsgQReaderCallback func, LPVOID lpVoid);
+	void RemoveListener(MsgQReaderCallback func, LPVOID lpVoid);
+	void RemoveAllListeners();
+};

P2PMsgQueueWriter.cpp

+#include "StdAfx.h"
+#include "P2PMsgQueueWriter.h"
+
+#include <msgqueue.h>
+
+#if _MSC_VER >= 1400 // if VS2005 and above
+#  include <atlsync.h>
+#else // older than VS2005: eVC++ 4
+#  include "AgisSync.h"
+#endif
+
+class CP2PMsgQueueWriter_Impl
+{
+	friend class CP2PMsgQueueWriter;
+	CP2PMsgQueueWriter* q;
+
+	HANDLE m_hMsgQ;
+	DWORD m_nMsgSize;
+
+	CP2PMsgQueueWriter_Impl(DWORD nMsgSize, DWORD dwCapacity, LPCTSTR lpszMsgQueueName)
+		: q(NULL)
+		, m_hMsgQ(NULL)
+		, m_nMsgSize(nMsgSize)
+	{
+		MSGQUEUEOPTIONS msgQueOpt = { sizeof(MSGQUEUEOPTIONS) };
+		msgQueOpt.dwFlags = MSGQUEUE_NOPRECOMMIT|MSGQUEUE_ALLOW_BROKEN;
+		msgQueOpt.dwMaxMessages = dwCapacity;
+		msgQueOpt.cbMaxMessage = nMsgSize;
+		msgQueOpt.bReadAccess = FALSE;
+		// CreateMsgQueue(): NULL indicates failure. To obtain extended error information, call the GetLastError function.
+		m_hMsgQ = CreateMsgQueue(lpszMsgQueueName,&msgQueOpt);
+		TRACE(_T("CreateMsgQueue(%s): 0x%x"),lpszMsgQueueName,m_hMsgQ);
+	}
+public:
+	//friend inline void boost::checked_delete<>(CP2PMsgQueueWriter_Impl * x);
+	~CP2PMsgQueueWriter_Impl()
+	{
+		BOOL bRet = 
+		CloseMsgQueue(m_hMsgQ);
+		TRACE(_T("CloseMsgQueue(0x%x): %d"),m_hMsgQ,bRet);
+	}
+private:
+};
+
+
+CP2PMsgQueueWriter::CP2PMsgQueueWriter(DWORD nMsgSize, DWORD dwCapacity, LPCTSTR lpszMsgQueueName)
+: d(new CP2PMsgQueueWriter_Impl(nMsgSize, dwCapacity, lpszMsgQueueName))
+{
+	d->q = this;
+}
+
+CP2PMsgQueueWriter::~CP2PMsgQueueWriter(void)
+{
+}
+
+int CP2PMsgQueueWriter::GetNumReaders()
+{
+	if (d->m_hMsgQ == NULL)
+		return -1;
+
+	MSGQUEUEINFO msgQInfo = { sizeof(MSGQUEUEINFO) };
+	if (GetMsgQueueInfo(d->m_hMsgQ,&msgQInfo))
+		return msgQInfo.wNumReaders;
+	else
+		return -2;
+}
+
+int CP2PMsgQueueWriter::GetNumWriters()
+{
+	if (d->m_hMsgQ == NULL)
+		return -1;
+
+	MSGQUEUEINFO msgQInfo = { sizeof(MSGQUEUEINFO) };
+	if (GetMsgQueueInfo(d->m_hMsgQ,&msgQInfo))
+		return msgQInfo.wNumWriters;
+	else
+		return -2;
+}
+
+BOOL CP2PMsgQueueWriter::WriteOnce(const void* pMsg, DWORD dwTimeout, DWORD dwFlags)
+{
+	return WriteMsgQueue(d->m_hMsgQ,LPVOID(pMsg),d->m_nMsgSize,dwTimeout,dwFlags);
+}
+
+BOOL CP2PMsgQueueWriter::WriteToOne(const void* pMsg, int* pNumReaders, DWORD dwTimeout, DWORD dwFlags)
+{
+	BOOL nCount = FALSE;
+	int nNumReaders = 0;
+	if (pNumReaders == NULL)
+	{
+		pNumReaders = &nNumReaders;
+	}
+	ASSERT_POINTER(pNumReaders,int);
+
+	MSGQUEUEINFO msgQInfo = { sizeof(MSGQUEUEINFO) };
+	if (GetMsgQueueInfo(d->m_hMsgQ,&msgQInfo)) {
+		*pNumReaders = msgQInfo.wNumReaders;
+		if(msgQInfo.wNumReaders > 0)
+		{
+			if (WriteMsgQueue(d->m_hMsgQ,LPVOID(pMsg),d->m_nMsgSize,dwTimeout,dwFlags))
+				nCount = TRUE;
+		}
+	}
+	return nCount;
+}
+
+DWORD CP2PMsgQueueWriter::WriteToAll(const void* pMsg, int* pNumReaders, DWORD dwTimeout, DWORD dwFlags)
+{
+	int nCount = 0;
+	int nNumReaders = 0;
+	if (pNumReaders == NULL)
+	{
+		pNumReaders = &nNumReaders;
+	}
+	ASSERT_POINTER(pNumReaders,int);
+
+	MSGQUEUEINFO msgQInfo = { sizeof(MSGQUEUEINFO) };
+	if (GetMsgQueueInfo(d->m_hMsgQ,&msgQInfo)) {
+		*pNumReaders = msgQInfo.wNumReaders;
+		for(int i=0; i<msgQInfo.wNumReaders; i++)
+		{
+			if (WriteMsgQueue(d->m_hMsgQ,LPVOID(pMsg),d->m_nMsgSize,dwTimeout,dwFlags))
+				nCount++;
+		}
+	}
+	return nCount;
+}

P2PMsgQueueWriter.h

+#pragma once
+
+// This class focuses on handling the details of Point-to-point Message Queue
+// Author: Afriza N. Arief (afriza.arief@asiagis.com)
+// Date: August 2010
+
+// Get Boost C++ Library from http://www.boost.org/ and add it to the "Additional include directories"
+// Alternatively, copy only necessary files to the MSVC Solution.
+#include <boost/scoped_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+class CP2PMsgQueueWriter_Impl;
+class CP2PMsgQueueWriter
+	: boost::noncopyable
+{
+	// D-Pointer ( http://www.google.com/search?q=D-Pointer , http://en.wikipedia.org/wiki/Opaque_pointer )
+	boost::scoped_ptr<CP2PMsgQueueWriter_Impl> d;
+public:
+	CP2PMsgQueueWriter(DWORD nMsgSize, DWORD dwCapacity = 0, LPCTSTR lpszMsgQueueName = NULL);
+	~CP2PMsgQueueWriter(void);
+
+	int GetNumReaders();
+	int GetNumWriters();
+
+	DWORD WriteToAll(const void* pMsg, int* pNumReaders=NULL, DWORD dwTimeout=0, DWORD dwFlags=0);
+	BOOL  WriteToOne(const void* pMsg, int* pNumReaders=NULL, DWORD dwTimeout=0, DWORD dwFlags=0);
+	BOOL  WriteOnce (const void* pMsg, DWORD dwTimeout=0, DWORD dwFlags=0);
+};
+#include "StdAfx.h"
+#include "PowerMonitor.h"
+
+#include <pm.h>
+#include "P2PMsgQueueReader.h"
+
+#include <utility> // for std::pair
+
+#if _MSC_VER >= 1400 // VS2005 or above
+#  include <atlsync.h>
+#else // older than VS2005: VC6 or eVC++ 4
+#  include "AgisSync.h"
+#endif
+
+#include <afxtempl.h>
+
+class CPowerMonitor_Impl
+{
+	friend class CPowerMonitor;
+	CPowerMonitor* q;
+
+	HANDLE m_hPowerNotification;
+	static void MsgQReaderCallback(CP2PMsgQueueReader* pMQReader, CP2PMsgQueueReader::Event evnt, LPVOID lpVoid);
+	CP2PMsgQueueReader m_PowerNotificationReader;
+	CCriticalSection m_CS;
+	typedef std::pair<CPowerMonitor::PowerMonitorCallback,LPVOID> TListener;
+	CList<TListener,const TListener&> m_listeners;
+
+	void NotifyListeners(PPOWER_BROADCAST pPowerBroadcast);
+
+	CPowerMonitor_Impl()
+		: q(NULL)
+		, m_hPowerNotification(NULL)
+		, m_PowerNotificationReader(sizeof(POWER_BROADCAST)+MAX_PATH,5) // MAX_PATH is determined by reading <pm.h>
+	{
+		m_PowerNotificationReader.AddListener(&CPowerMonitor_Impl::MsgQReaderCallback, this);
+	}
+};
+
+void CPowerMonitor_Impl::MsgQReaderCallback(CP2PMsgQueueReader* pMQReader, CP2PMsgQueueReader::Event evnt, LPVOID lpVoid)
+{
+	CPowerMonitor_Impl* d = reinterpret_cast<CPowerMonitor_Impl*>(lpVoid);
+
+	if (evnt == CP2PMsgQueueReader::Event_NewMessage ||
+		evnt == CP2PMsgQueueReader::Event_AlertMessage)
+	{
+		d->NotifyListeners(
+#if _MSC_VER >= 1400
+		pMQReader->GetLastMessagePtr<PPOWER_BROADCAST>()
+#else
+		(PPOWER_BROADCAST)pMQReader->GetLastMessagePtr()
+#endif
+		);
+	}
+	else if (evnt == CP2PMsgQueueReader::Event_Initialize)
+	{
+		if (d->m_hPowerNotification)
+		{
+			StopPowerNotifications(d->m_hPowerNotification);
+			d->m_hPowerNotification = NULL;
+		}
+		HANDLE hMsgQ = pMQReader->GetMsgQueueHandle();
+		if (hMsgQ)
+			d->m_hPowerNotification = RequestPowerNotifications(hMsgQ,POWER_NOTIFY_ALL);
+	}
+	else if (evnt == CP2PMsgQueueReader::Event_Uninitialize)
+	{
+		if (d->m_hPowerNotification)
+		{
+			StopPowerNotifications(d->m_hPowerNotification);
+			d->m_hPowerNotification = NULL;
+		}
+	}
+}
+
+void CPowerMonitor_Impl::NotifyListeners(PPOWER_BROADCAST pPowerBroadcast)
+{
+	CCritSecLock csLock(m_CS);
+
+	TListener listener;
+	POSITION pos = m_listeners.GetHeadPosition();
+	while (pos)
+	{
+		listener = m_listeners.GetNext(pos);
+		ASSERT(listener.first != NULL);
+		listener.first(pPowerBroadcast,listener.second);
+	}
+}
+
+CPowerMonitor* CPowerMonitor::GetInstance()
+{
+	static CPowerMonitor powerMonitor;
+	return &powerMonitor;
+}
+
+CPowerMonitor::CPowerMonitor(void)
+: d(new CPowerMonitor_Impl())
+{
+	d->q = this;
+}
+
+CPowerMonitor::~CPowerMonitor(void)
+{
+	d->m_PowerNotificationReader.Stop();
+}
+
+void CPowerMonitor::AddListener(PowerMonitorCallback fnCallback, LPVOID lpVoid)
+{
+	CCritSecLock csLock(d->m_CS);
+	
+	d->m_listeners.AddTail(std::make_pair(fnCallback,lpVoid));
+
+	d->m_PowerNotificationReader.Start();
+}
+
+void CPowerMonitor::RemoveListener(PowerMonitorCallback fnCallback, LPVOID lpVoid)
+{
+	CCritSecLock csLock(d->m_CS);
+	
+	POSITION pos = d->m_listeners.Find(std::make_pair(fnCallback,lpVoid));
+	if (pos)
+	{
+		d->m_listeners.RemoveAt(pos);
+	}
+
+	if (d->m_listeners.GetCount() == 0)
+		d->m_PowerNotificationReader.SignalToStop();
+}
+#pragma once
+
+// This class focuses on handling the details of Point-to-point Message Queue.
+// It also maintains the life time of PowerNotification HANDLE by calling
+// RequestPowerNotifications() and StopPowerNotifications() automatically.
+// Author: Afriza N. Arief (afriza.arief@asiagis.com)
+// Date: October 2010
+
+// Get Boost C++ Library from http://www.boost.org/ and add it to the "Additional include directories"
+// Alternatively, copy only necessary files to the MSVC Solution.
+#include <boost/scoped_ptr.hpp>
+#include <boost/noncopyable.hpp>
+
+#include <pm.h>
+
+class CPowerMonitor_Impl;
+class CPowerMonitor
+	: boost::noncopyable
+{
+	// D-Pointer ( http://www.google.com/search?q=D-Pointer , http://en.wikipedia.org/wiki/Opaque_pointer )
+	boost::scoped_ptr<CPowerMonitor_Impl> d;
+public:
+	// allow singleton-style of using default instance
+	static CPowerMonitor* GetInstance();
+	// unlike real singleton, allow multiple instances to be created using constructor/destructor
+	CPowerMonitor(void);
+	~CPowerMonitor(void);
+
+	typedef void (*PowerMonitorCallback)(PPOWER_BROADCAST pPowerBroadcast, LPVOID lpVoid);
+
+	void AddListener(PowerMonitorCallback fnCallback, LPVOID lpVoid);
+	void RemoveListener(PowerMonitorCallback fnCallback, LPVOID lpVoid);
+};
+// Reg.cpp: implementation of the CReg class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "Reg.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+#define MyFree(p) { if(p) LocalFree(p); }
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CReg::CReg()
+{
+	m_hKey = NULL;
+	m_Index = 0;
+	m_lpbValue = NULL;
+}
+
+CReg::CReg(HKEY hkRoot, LPCTSTR pszKey)
+{
+	m_hKey = NULL;
+	m_Index = 0;
+	m_lpbValue = NULL;
+	Open(hkRoot, pszKey);
+}
+
+
+CReg::~CReg()
+{
+	if(m_hKey)
+	{
+		RegCloseKey(m_hKey);
+	}
+	MyFree(m_lpbValue);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Create the key
+//-------------------------------------------------------------------
+BOOL CReg::Create(HKEY hkRoot, LPCTSTR pszKey)
+{
+	DWORD dwDisp;
+	return ERROR_SUCCESS == RegCreateKeyEx(hkRoot, pszKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &m_hKey, &dwDisp);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Open the key
+//-------------------------------------------------------------------
+BOOL CReg::Open(HKEY hkRoot, LPCTSTR pszKey, REGSAM sam)
+{
+	return ERROR_SUCCESS == RegOpenKeyEx(hkRoot, pszKey, 0, sam, &m_hKey);
+}
+
+//-------------------------------------------------------------------
+//Description:
+// Reset the value
+//-------------------------------------------------------------------
+void CReg::Reset()
+{
+	if(m_hKey)
+	{
+		RegCloseKey(m_hKey);
+	}
+	MyFree(m_lpbValue);
+	m_hKey = NULL;
+	m_Index = 0;
+	m_lpbValue = NULL;
+}
+
+//-------------------------------------------------------------------
+//Description:
+// Operator overload
+//-------------------------------------------------------------------
+CReg::operator HKEY()
+{
+	return m_hKey;
+}
+
+//-------------------------------------------------------------------
+//Description:
+// Test whether is the handle of the key OK for next operate
+//-------------------------------------------------------------------
+BOOL CReg::IsOK()
+{
+	return m_hKey != NULL;
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Enum the key
+//-------------------------------------------------------------------
+BOOL CReg::EnumKey(LPTSTR psz, DWORD dwLen)
+{
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	return ERROR_SUCCESS == RegEnumKeyEx(m_hKey, m_Index++, psz, &dwLen, NULL, NULL, NULL, NULL);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Enum registry Value
+//-------------------------------------------------------------------
+BOOL CReg::EnumValue(LPTSTR pszName, DWORD dwLenName, LPTSTR pszValue, DWORD dwLenValue)
+{
+	DWORD dwType;
+	
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	dwLenValue *= sizeof(TCHAR); // convert length in chars to bytes
+	
+	return ERROR_SUCCESS == RegEnumValue(m_hKey, m_Index++, pszName, &dwLenName, NULL, &dwType, (LPBYTE)pszValue, &dwLenValue);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Get the string value
+//-------------------------------------------------------------------
+BOOL CReg::GetValueSZ(LPCTSTR szName, LPTSTR szValue, DWORD dwLen)
+{
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	dwLen *= sizeof(TCHAR); // convert length in chars to bytes
+	
+	return ERROR_SUCCESS == RegQueryValueEx(m_hKey, szName, NULL, NULL, (LPBYTE)szValue, &dwLen);
+}
+
+//-------------------------------------------------------------------
+//Description:
+// Get the binary value
+//-------------------------------------------------------------------
+DWORD CReg::GetValueBinary(LPCTSTR szName, LPBYTE lpbValue, DWORD dwLen)
+{
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	DWORD dwLenWant;
+	dwLenWant = dwLen;
+	if(ERROR_SUCCESS == RegQueryValueEx(m_hKey, szName, NULL, NULL, lpbValue, &dwLen))
+	{
+		return dwLen;
+	}
+	else
+	{
+		return 0;
+	}
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Get the binary value
+//-------------------------------------------------------------------
+LPBYTE CReg::GetValueBinary(LPCTSTR szName)
+{
+	return (LPBYTE)GetValueSZ(szName);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Get the string value
+// The returned string is allocated using malloc(). Call free() on the returned
+// string after use. See implementation of GetValueCString() for sample usage.
+// Author: Afriza N. Arief
+//-------------------------------------------------------------------
+LPCTSTR CReg::GetValueSZ(LPCTSTR szName)
+{
+	DWORD dwLen = 0;
+	DWORD dwType = REG_SZ;
+	LPTSTR lpszValue = NULL;
+	if (ERROR_SUCCESS == RegQueryValueEx(m_hKey, szName, NULL, &dwType, (LPBYTE)NULL, &dwLen))
+	{
+		if (dwType != REG_SZ)
+			return NULL;
+		lpszValue = (LPTSTR)malloc(dwLen);
+		if (ERROR_SUCCESS == RegQueryValueEx(m_hKey, szName, NULL, NULL, (LPBYTE)lpszValue, &dwLen))
+		{
+			return lpszValue;
+		}
+		else
+		{
+			free(lpszValue);
+			return NULL;
+		}
+	}
+	return lpszValue;
+}
+
+//-------------------------------------------------------------------
+//Description:
+// Get the string value
+// Requires MFC or ATL for CString. Remove this function if you do not want MFC
+// nor ATL.
+// Author: Afriza N. Arief
+//-------------------------------------------------------------------
+CString CReg::GetValueCString(LPCTSTR szName)
+{
+	CString sValue;
+	LPCTSTR lpszValue = GetValueSZ(szName);
+	if (lpszValue != NULL)
+	{
+		sValue = lpszValue;
+		free((void*)lpszValue);
+	}
+	return sValue;
+}
+
+//-------------------------------------------------------------------
+//Description:
+// Get the DWORD value
+//
+//Parameters:
+// szName:[in] The value of registry
+// dwDefault:[in] The default value return when failed in getting the
+//DWORD value.
+//-------------------------------------------------------------------
+DWORD CReg::GetValueDW(LPCTSTR szName, DWORD dwDefault)
+{
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	DWORD dwValue = dwDefault;
+	DWORD dwLen = sizeof(DWORD);
+	RegQueryValueEx(m_hKey, szName, NULL, NULL, (LPBYTE)&dwValue, &dwLen);
+	return dwValue;
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Set the string value
+//-------------------------------------------------------------------
+BOOL CReg::SetSZ(LPCTSTR szName, LPCTSTR szValue, DWORD dwLen)
+{
+	//Prefix
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	return ERROR_SUCCESS == RegSetValueEx(m_hKey, szName, 0, REG_SZ, (LPBYTE)szValue, sizeof(TCHAR)*dwLen);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Set the string value
+//-------------------------------------------------------------------
+BOOL CReg::SetSZ(LPCTSTR szName, LPCTSTR szValue)
+{
+	return SetSZ(szName, szValue, 1+lstrlen(szValue));
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Get the DWORD value
+//-------------------------------------------------------------------
+BOOL CReg::SetDW(LPCTSTR szName, DWORD dwValue)
+{
+	//Prefix
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	return ERROR_SUCCESS==RegSetValueEx(m_hKey, szName, 0, REG_DWORD, (LPBYTE)&dwValue, sizeof(DWORD));
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Get the binary value
+//-------------------------------------------------------------------
+BOOL CReg::SetBinary(LPCTSTR szName, LPBYTE lpbValue, DWORD dwLen)
+{
+	//Prefix
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	return ERROR_SUCCESS == RegSetValueEx(m_hKey, szName, 0, REG_BINARY, lpbValue, dwLen);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Set the Multi value
+//-------------------------------------------------------------------
+BOOL CReg::SetMultiSZ(LPCTSTR szName, LPCTSTR lpszValue, DWORD dwLen)
+{
+	return ERROR_SUCCESS == RegSetValueEx(m_hKey, szName, 0, REG_MULTI_SZ, (LPBYTE)lpszValue, sizeof(TCHAR)*dwLen);
+}
+
+
+//-------------------------------------------------------------------
+//Description:
+// Delete the value
+//-------------------------------------------------------------------
+BOOL CReg::DeleteValue(LPCTSTR szName)
+{
+	//Prefix
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	//
+	return ERROR_SUCCESS == RegDeleteValue(m_hKey, szName);
+}
+
+
+
+//-------------------------------------------------------------------
+//Description:
+// Delete Key
+//-------------------------------------------------------------------
+BOOL CReg::DeleteKey(LPCTSTR szName)
+{
+	if(!m_hKey)
+	{
+		return FALSE;
+	}
+	
+	return ERROR_SUCCESS == RegDeleteKey(m_hKey, szName);
+}
+// Reg.h: interface for the CReg class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_REG_H__53A2E50F_2BD0_4F3A_B3A9_6EF7BC75FBF3__INCLUDED_)
+#define AFX_REG_H__53A2E50F_2BD0_4F3A_B3A9_6EF7BC75FBF3__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define MySucceed(h) (h != NULL && h != INVALID_HANDLE_VALUE)
+#define MyFAILED(h) (!MySucceed(h))//(h == NULL || h == INVALID_HANDLE_VALUE)
+
+#define MyFreeLibrary(h) if (MySucceed(h)){FreeLibrary(h);h=NULL;}
+#define MyCloseHandle(h) if (MySucceed(h)){CloseHandle(h);h=NULL;}
+
+class CReg  
+{
+public:
+	CReg();
+	CReg(HKEY hkRoot, LPCTSTR pszKey);
+	virtual ~CReg();
+	
+public:
+	BOOL DeleteKey(LPCTSTR szName);
+	BOOL DeleteValue(LPCTSTR szName);
+	BOOL SetMultiSZ(LPCTSTR szName, LPCTSTR lpszValue, DWORD dwLen);
+	BOOL SetBinary(LPCTSTR szName, LPBYTE lpbValue, DWORD dwLen);
+	BOOL SetDW(LPCTSTR szName, DWORD dwValue);
+	BOOL SetSZ(LPCTSTR szName, LPCTSTR szValue);
+	BOOL SetSZ(LPCTSTR szName, LPCTSTR szValue, DWORD dwLen);
+	DWORD GetValueDW(LPCTSTR szName, DWORD dwDefault=0);
+//-------------------------------------------------------------------
+//Description:
+// Get the string value
+// The returned string is allocated using malloc(). Call free() on the returned
+// string after use. See implementation of GetValueCString() for sample usage.
+// Author: Afriza N. Arief
+//-------------------------------------------------------------------
+	LPCTSTR GetValueSZ(LPCTSTR szName);
+	CString GetValueCString(LPCTSTR szName);
+	LPBYTE GetValueBinary(LPCTSTR szName);
+	DWORD GetValueBinary(LPCTSTR szName, LPBYTE lpbValue, DWORD dwLen);
+	BOOL GetValueSZ(LPCTSTR szName, LPTSTR szValue, DWORD dwLen);
+	BOOL EnumValue(LPTSTR pszName, DWORD dwLenName, LPTSTR pszValue, DWORD dwLenValue);
+	BOOL EnumKey(LPTSTR psz, DWORD dwLen);
+	BOOL IsOK();
+	operator HKEY();
+	void Reset();
+	BOOL Open(HKEY hkRoot, LPCTSTR pszKey, REGSAM sam = KEY_READ);
+	BOOL Create(HKEY hkRoot, LPCTSTR pszKey);
+	
+private:
+	HKEY m_hKey;
+	int m_Index;
+	LPBYTE m_lpbValue; // last value read, if any
+};
+
+#endif // !defined(AFX_REG_H__53A2E50F_2BD0_4F3A_B3A9_6EF7BC75FBF3__INCLUDED_)
+// sqlite_utils.h: Utility classes / functions to ease development with SQLite
+// Author: Afriza N. Arief (afriza.arief@asiagis.com)
+// Date: October 2010
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_SQLITE_UTILS_H__794ECA4E_4C85_45D6_A760_C555426B79E1__INCLUDED_)
+#define AFX_SQLITE_UTILS_H__794ECA4E_4C85_45D6_A760_C555426B79E1__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include <sqlite/sqlite3.h>
+#include <boost/noncopyable.hpp>
+
+class scoped_sqlite3_ptr
+	: boost::noncopyable
+{
+public:
+//	scoped_sqlite3_ptr() : db_(NULL) {}
+	explicit scoped_sqlite3_ptr(sqlite3* db=NULL)
+		: db_(db)
+	{}
+	~scoped_sqlite3_ptr()
+	{
+		close();
+	}
+	int open(
+		const char *filename   /* Database filename (UTF-8) */
+		)
+	{
+		int rc = close();
+		if (rc != SQLITE_OK)
+			return rc;
+		return sqlite3_open(filename,&db_);
+	}
+	int open16(
+		const void *filename   /* Database filename (UTF-16) */
+		)
+	{
+		int rc = close();
+		if (rc != SQLITE_OK)
+			return rc;
+		return sqlite3_open16(filename,&db_);
+	}
+	int open_v2(
+		const char *filename,   /* Database filename (UTF-8) */
+		int flags=SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE,              /* Flags */
+		const char *zVfs=0        /* Name of VFS module to use */
+		)
+	{
+		int rc = close();
+		if (rc != SQLITE_OK)
+			return rc;
+		return sqlite3_open_v2(filename,&db_,flags,zVfs);
+	}
+	int close()
+	{
+		// The C parameter to sqlite3_close(C) must be either a NULL pointer 
+		// or an sqlite3 object pointer that has not been previously closed. 
+		// Calling sqlite3_close() with a NULL pointer argument is a harmless no-op.
+		// Source: http://sqlite.org/c3ref/close.html
+		int rc = sqlite3_close(db_);
+		if (rc == SQLITE_OK)
+			db_ = NULL;
+		return rc;
+	}
+	int set(sqlite3* db)
+	{
+		int rc = sqlite3_close(db_);
+		if (rc == SQLITE_OK)
+			db_ = db;
+		return rc;
+	}
+	//operator sqlite3*()	{ return db_; }
+	sqlite3* get() { return db_; }
+	sqlite3* release()
+	{
+		sqlite3* db = db_;
+		db_ = NULL;
+		return db;
+	}
+	int busy_timeout(int ms)
+	{
+		return sqlite3_busy_timeout(db_,ms);
+	}
+	int exec(
+		const char *zSql,           /* The SQL to be executed in UTF8 */
+		sqlite3_callback xCallback=0, /* Invoke this callback routine */
+		void *pArg=0,                 /* First argument to xCallback() */
+		char **pzErrMsg=0             /* Write error messages here */
+		)
+	{
+		return sqlite3_exec(db_,zSql,xCallback,pArg,pzErrMsg);
+	}
+	int changes() {
+		return sqlite3_changes(db_);
+	}
+	__int64 last_insert_rowid() {
+		return sqlite3_last_insert_rowid(db_);
+	}
+protected:
+	sqlite3* db_;
+};
+
+class scoped_sqlite3_stmt_ptr
+	: boost::noncopyable
+{
+public:
+//	scoped_sqlite3_stmt_ptr() : stmt_(NULL) {}
+	explicit scoped_sqlite3_stmt_ptr(sqlite3_stmt* stmt=NULL)
+		: stmt_(stmt)
+	{}
+	~scoped_sqlite3_stmt_ptr()
+	{
+		finalize();
+	}
+	int prepare_v2(
+	  sqlite3 *db,            /* Database handle */
+	  const char *zSql,       /* SQL statement, UTF-8 encoded */
+	  int nByte=-1,              /* Maximum length of zSql in bytes. */
+	  const char **pzTail=0     /* OUT: Pointer to unused portion of zSql */
+	)
+	{
+		int rc = finalize();
+		if (rc == SQLITE_OK)
+			rc = sqlite3_prepare_v2(db,zSql,nByte,&stmt_,pzTail);
+		return rc;
+	}
+	int prepare16_v2(
+	  sqlite3 *db,            /* Database handle */
+	  const void *zSql,       /* SQL statement, UTF-16 encoded */
+	  int nByte=-1,              /* Maximum length of zSql in bytes. */
+	  const void **pzTail=0     /* OUT: Pointer to unused portion of zSql */
+	)
+	{
+		int rc = finalize();
+		if (rc == SQLITE_OK)
+			rc = sqlite3_prepare16_v2(db,zSql,nByte,&stmt_,pzTail);
+		return rc;
+	}
+	int prepare_v2(
+	  scoped_sqlite3_ptr &db,            /* Database handle */
+	  const char *zSql,       /* SQL statement, UTF-8 encoded */
+	  int nByte=-1,              /* Maximum length of zSql in bytes. */
+	  const char **pzTail=0     /* OUT: Pointer to unused portion of zSql */
+	)
+	{
+		return prepare_v2(db.get(),zSql,nByte,pzTail);
+	}
+	int prepare16_v2(
+	  scoped_sqlite3_ptr &db,            /* Database handle */
+	  const void *zSql,       /* SQL statement, UTF-16 encoded */
+	  int nByte=-1,              /* Maximum length of zSql in bytes. */
+	  const void **pzTail=0     /* OUT: Pointer to unused portion of zSql */
+	)
+	{
+		return prepare16_v2(db.get(),zSql,nByte,pzTail);
+	}
+	int reset() { return sqlite3_reset(stmt_); }
+	int step() { return sqlite3_step(stmt_); }
+	int bind_text16(
+	  int i, 
+	  const void *zData, 
+	  int nData = -1, 
+	  void (*xDel)(void*) = SQLITE_TRANSIENT
+	){
+		return sqlite3_bind_text16(stmt_,i,zData,nData,xDel);
+	}
+	int bind_text( 
+		int i, 
+		const char *zData, 
+		int nData = -1, 
+		void (*xDel)(void*) = SQLITE_TRANSIENT
+	){
+		return sqlite3_bind_text(stmt_, i, zData, nData, xDel);
+	}
+	int bind_int(int i, int iValue) {
+		return sqlite3_bind_int(stmt_, i, iValue);
+	}
+	int bind_int64(int i, sqlite_int64 iValue) {
+		return sqlite3_bind_int64(stmt_, i, iValue);
+	}
+	int bind_double(int i, double rValue) {
+		return sqlite3_bind_double(stmt_, i, rValue);
+	}
+	int column_type(int i) {
+		return sqlite3_column_type(stmt_, i);
+	}
+	const void *column_text16(int i) {
+		return sqlite3_column_text16(stmt_, i);
+	}
+	const unsigned char *column_text(int i) {
+		return sqlite3_column_text(stmt_, i);
+	}
+	__int64 column_int64(int i) {
+		return sqlite3_column_int64(stmt_, i);
+	}
+	int column_int(int i) {
+		return sqlite3_column_int(stmt_, i);
+	}
+	double column_double(int i) {
+		return sqlite3_column_double(stmt_, i);
+	}
+	int column_count() {
+		return sqlite3_column_count(stmt_);
+	}
+	int set(sqlite3_stmt* stmt)
+	{
+		int rc = finalize();
+		if (rc == SQLITE_OK)
+			stmt_ = stmt;
+		return rc;
+	}
+	sqlite3_stmt* get() const
+	{
+		return stmt_;
+	}
+	sqlite3_stmt* release()
+	{
+		sqlite3_stmt* stmt = stmt_;
+		stmt_ = NULL;
+		return stmt;
+	}
+	int finalize()
+	{
+		int rc = sqlite3_finalize(stmt_);
+		if (rc == SQLITE_OK)
+			stmt_ = NULL;
+		return rc;
+	}
+protected:
+	sqlite3_stmt* stmt_;
+};
+
+class auto_reset_sqlite3_stmt_ptr
+	: boost::noncopyable
+{
+public:
+//	auto_reset_sqlite3_stmt_ptr() : stmt_(NULL) { }
+	explicit auto_reset_sqlite3_stmt_ptr(sqlite3_stmt* stmt=NULL)
+		: stmt_(stmt)
+	{}
+	~auto_reset_sqlite3_stmt_ptr()
+	{
+		sqlite3_reset(stmt_);
+	}
+protected:
+	sqlite3_stmt* stmt_;
+};
+
+class CSQLite3Transaction
+	: boost::noncopyable
+{
+	sqlite3* db;
+	BOOL bActive;
+	BOOL bSavepoint;
+public:
+	// References:
+	// - http://sqlite.org/lockingv3.html#transaction_control
+	// - http://sqlite.org/lang_transaction.html
+	// - http://sqlite.org/lang_savepoint.html
+	typedef enum EBehavior {
+		// Lock_DoNotBegin:
+		// Do NOT begin a transaction
+		Lock_DoNotBegin,
+		// Lock_Deferred:
+		// Allow other transactions to read/write. We acquire SHARED/RESERVED
+		// lock only when needed.
+		// Need to handle when some statements failed due to unable to acquire 
+		// lock and probably need to handle some dead-lock and do some rollback.
+		// NOTE: This is the most troublesome mode to handle. Do not use unless
+		// you know what you are doing.
+		Lock_Deferred,
+		// Lock_Immediate:
+		// Do not allow other transactions to write. We immediately acquire
+		// RESERVED lock.
+		// + Note:
+		// It is probably a good idea to acquire RESERVED lock in a function
+		// that involves more than one SQL statements and one or more of them
+		// are write operations.
+		Lock_Immediate,
+		// Lock_Exclusive:
+		// Do not allow other transactions to read/write. Least concurrency.
+		Lock_Exclusive,
+	} EBehavior;
+	CSQLite3Transaction(sqlite3* db, EBehavior eBehavior = Lock_Immediate)
+		: db(db)
+		, bActive(FALSE)
+		, bSavepoint(FALSE)
+	{
+		Begin(eBehavior);
+	}
+	CSQLite3Transaction(scoped_sqlite3_ptr& db, EBehavior eBehavior = Lock_Immediate)
+		: db(db.get()) // only differ in this line
+		, bActive(FALSE)
+		, bSavepoint(FALSE)
+	{
+		Begin(eBehavior);
+	}
+	~CSQLite3Transaction()
+	{
+		End();
+	}
+	BOOL IsActive()
+	{
+		return bActive;
+	}
+	BOOL IsSavepoint()
+	{
+		return bSavepoint;
+	}
+	BOOL Begin(EBehavior eBehavior = Lock_Deferred)
+	{
+		if (!bActive)
+		{
+			BOOL bNotNested = sqlite3_get_autocommit(db);
+			const char* zSql = NULL;
+			int rc;
+
+			if (bNotNested)
+			{
+				if (eBehavior == Lock_Immediate)
+					zSql = "BEGIN IMMEDIATE";
+				else if (eBehavior == Lock_Exclusive)
+					zSql = "BEGIN EXCLUSIVE";
+				else if (eBehavior == Lock_Deferred)
+					zSql = "BEGIN DEFERRED";
+
+				if (zSql)
+				{
+					rc = sqlite3_exec(db,zSql,NULL,NULL,NULL);
+					if (SQLITE_OK==rc)
+					{
+						bActive = TRUE;
+						return TRUE;
+					}
+				}
+			}
+			else
+			{
+				zSql = "SAVEPOINT csqlite3transaction";
+				rc = sqlite3_exec(db,zSql,0,0,0);
+				if (rc==SQLITE_OK)
+				{
+					bSavepoint = bActive = TRUE;
+					return TRUE;
+				}
+			}
+		}
+		return FALSE;
+	}
+	BOOL End(BOOL bRollbackOnFailure = TRUE, BOOL bAlsoRollbackParentsOnFailure = FALSE)
+	{
+		if (bActive)
+		{
+			if (bSavepoint)
+			{
+				if (SQLITE_OK==sqlite3_exec(db,"RELEASE csqlite3transaction",0,0,0))
+				{
+					bActive = bSavepoint = FALSE;
+					return TRUE;
+				}
+				else if (bRollbackOnFailure)
+				{
+					Rollback(bAlsoRollbackParentsOnFailure);
+				}
+			}
+			else
+			{
+				if (SQLITE_OK==sqlite3_exec(db,"END",NULL,NULL,NULL))
+				{
+					bActive = FALSE;
+					return TRUE;
+				}
+				else if (bRollbackOnFailure)
+				{
+					Rollback(bAlsoRollbackParentsOnFailure);
+				}
+			}
+		}
+		return FALSE;
+	}
+	// In a nested transactions (using Savepoint), the parent transactions will
+	// be rolled back if bRollbackParentTransactions is TRUE.
+	// If bRollbackParentTransactions is FALSE, only current transaction 
+	// (savepoint to be precise) will be rolled back (released to be precise).
+	BOOL Rollback(BOOL bRollbackParentTransactions = FALSE)
+	{
+		if (bActive)
+		{
+			if (!bSavepoint || bRollbackParentTransactions)
+			{
+				if (SQLITE_OK == sqlite3_exec(db,"ROLLBACK",0,0,0))
+				{
+					bActive = bSavepoint = FALSE;
+					return TRUE;
+				}
+			}
+			else // rollback savepoint
+			{
+				if (SQLITE_OK == sqlite3_exec(db,"ROLLBACK TO csqlite3transaction",0,0,0))
+				{
+					bActive = bSavepoint = FALSE;
+					return TRUE;
+				}
+			}
+		}
+		return FALSE;
+	}
+};
+
+template <class T>
+class auto_free {
+public:
+	explicit auto_free(T* ptr=0) : ptr_(ptr) {}
+	~auto_free() {
+		free();
+	}
+	T* get() {
+		return ptr_;
+	}
+	T* release() {
+		T* ptr = ptr_;
+		ptr_ = 0;
+		return ptr;
+	}
+	void free() {
+		// If memblock is NULL, the pointer is ignored and free() immediately returns.
+		// http://msdn.microsoft.com/en-us/library/we1whae7.aspx
+		::free((void*)ptr_);
+		ptr_ = 0;
+	}
+	void reset(T* ptr=0) {
+		free();
+		ptr_ = ptr;
+	}
+protected:
+	T* ptr_;
+};
+
+#endif // !defined(AFX_SQLITE_UTILS_H__794ECA4E_4C85_45D6_A760_C555426B79E1__INCLUDED_)