Click here to Skip to main content
15,885,278 members
Articles / Programming Languages / C++

Avoid Multiple Instances of Windows Application with SingleInstanceGuard

Rate me:
Please Sign up or sign in to vote.
3.92/5 (20 votes)
21 Oct 2010CPOL16 min read 53.8K   629   27  
A simple utility class that helps maintain one instance of your application and allows other instances to transfer the data processing to it before they close
#include "sig.h"

////////////////////////////////////////////////////////////////////////////////////////////
// Global objects involved in the implementation
////////////////////////////////////////////////////////////////////////////////////////////

// The minimum size of the data to be shared
#define MINIMUM_MMF_SIZE	4096

// The dummy event object whose existence indicates that an instance is already running
HANDLE g_hGuardEvent = NULL;

// The main flag that indicates if an instance is already running
bool g_bRunning = false;

// The memory mapped file used to communicate
HANDLE g_hMMF = NULL;

// The size of the data to be shared
long g_nSharedDataSize = MINIMUM_MMF_SIZE;//default

// The size of the data actually present in the MMF (used in reading data)
long g_nWrittenDataSize = -1;

// Total bytes read from or written into the buffer
long g_nBytesRW = 0;

// The event used to grant access to reading the MMF
HANDLE g_hCanReadData = NULL;

// The event used to grant access to writing the MMF
HANDLE g_hCanWriteData = NULL;

// The flag that allows write operations go on in sequence
bool g_bHasWrittenPreviously = false;

// The ID of the first instance thread to which alert would be sent
DWORD g_dwFirstInstanceThreadId = 0;

// The handle of the first instance main window (set by the client) to which alert would be sent
HWND g_hWndFirstInstanceWindow = NULL;

// The ID to recognise the guard
const char *g_pid = NULL;

////////////////////////////////////////////////////////////////////////////////////////////
// SingleInstanceGuard implementation
////////////////////////////////////////////////////////////////////////////////////////////

bool IsAlreadyRunning(const char*pid)
{
	g_hGuardEvent = CreateEvent(NULL, FALSE, FALSE, pid);
	return ERROR_ALREADY_EXISTS==GetLastError();;
}

unsigned long _stdcall AlertMonitor(void*)
{
	while(1)
	{
		WaitForSingleObject(g_hCanReadData, INFINITE);

		g_nBytesRW = 0;
		g_nWrittenDataSize = -1;
		
		if(NULL != g_hWndFirstInstanceWindow)
			PostMessage(g_hWndFirstInstanceWindow, SingleInstanceGuard::SIGM_ALERT, 0, 0);
		else
			PostThreadMessage(g_dwFirstInstanceThreadId, SingleInstanceGuard::SIGM_ALERT, 0, 0);
	}
	return 0;
}

UINT SingleInstanceGuard::SIGM_ALERT = 0;

SingleInstanceGuard::SingleInstanceGuard(const char *pID, long nSharedDataSize/*-1*/)
{
    g_pid = pID;

	if(nSharedDataSize > MINIMUM_MMF_SIZE)
		g_nSharedDataSize = nSharedDataSize;
	g_bRunning = IsAlreadyRunning(g_pid);

	/* create/open the other kernel objects */

	std::string strMMF = g_pid;
	strMMF += "MMF";

	std::string strCanRead = g_pid;
	strCanRead += "CanRead";

	std::string strCanWrite = g_pid;
	strCanWrite += "CanWrite";
	
	std::string strAlert = g_pid;
	strAlert += "AlertMessage";

	if(g_bRunning)
	{
		/* Open the MMF */

		g_hMMF = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, strMMF.c_str());
		
		/* Read out the communication information */
		
		char *pBytes = (char*)MapViewOfFile(g_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);		
		
		if(0==pBytes)
			throw Exception(Exception::UNABLE_TO_CHECK);

		long nSize;
		memcpy((void*)&nSize, pBytes, sizeof(nSize));

		if(nSize != g_nSharedDataSize)
			throw Exception(Exception::DIFFERENT_GUARDS);

		g_nBytesRW += sizeof(nSize);

		UnmapViewOfFile(pBytes);

		/* open the control kernel objects */

		g_hCanReadData = CreateEvent(NULL, FALSE, FALSE, strCanRead.c_str());
		g_hCanWriteData = CreateEvent(NULL, FALSE, FALSE, strCanWrite.c_str());
	}
	else
	{
		/* register the alert mesage */
				
		SIGM_ALERT = ::RegisterWindowMessage(strAlert.c_str());

		/* Create the control kernel objects */

		g_hCanReadData = CreateEvent(NULL, FALSE, FALSE, strCanRead.c_str());
		
		g_hCanWriteData = CreateEvent(NULL, FALSE, 
			TRUE, // allow the first copy to proceed to write without waiting
			strCanWrite.c_str());

		/* create the MMF (backed by the system paging file) */

		long nMMFSize = sizeof(long) // space for the said size value itself, to verify later
			+ sizeof(long)// size of data actually shared 
			+ g_nSharedDataSize;// the said size itself

		g_hMMF = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, nMMFSize, strMMF.c_str());
		
		/* Write out the communication information */

		char *pBytes = (char*)MapViewOfFile(g_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);	
	
		if(0==pBytes)
			throw Exception(Exception::UNABLE_TO_CHECK);
		
		memcpy(pBytes, (void*)&g_nSharedDataSize, sizeof(g_nSharedDataSize));
		g_nBytesRW += sizeof(g_nSharedDataSize);
		
		long W = 0;// written size
		memcpy(pBytes+g_nBytesRW, (void*)&W, sizeof(W));
		g_nBytesRW += sizeof(W);

		UnmapViewOfFile(pBytes);		

		/* mark the thread id and create the alert monitor */

		g_dwFirstInstanceThreadId = GetCurrentThreadId();

		DWORD tid;
		CreateThread(NULL, 0, AlertMonitor, NULL, 0, &tid); 
	}
	
	g_nBytesRW = 0;
}

SingleInstanceGuard::~SingleInstanceGuard()
{
	// This is necessary when other instances won't share data and simply alert.
	if(g_bRunning && (0==g_nBytesRW))
		SetEvent(g_hCanWriteData);

	CloseHandle(g_hGuardEvent);
	CloseHandle(g_hCanWriteData);
	CloseHandle(g_hCanReadData);
	CloseHandle(g_hMMF);
}

bool SingleInstanceGuard::AlreadyRunning()
{
	return g_bRunning;
}

SingleInstanceGuard& SingleInstanceGuard::operator << (const std::string &s)
{
	int n = s.size();
	Write((void*)&n, sizeof(n));
	Write((void*)s.c_str(), s.size());
	return *this;
}

bool SingleInstanceGuard::operator >> (std::string &s)
{
	int n;
	if(!Read((void*)&n, sizeof(n)))
		return false;
	char *buffer = new char[n+1];
	bool bMore = Read(buffer, n);
	buffer[n] = 0;
	s = buffer;
	delete [] buffer;
	return bMore;
}

void operator<<(SingleInstanceGuard &t, char *p)
{
	throw SingleInstanceGuard::Exception(SingleInstanceGuard::Exception::OPERATION_NOT_SUPPORTED);
}

void operator>>(SingleInstanceGuard &t, char *p)
{
	throw SingleInstanceGuard::Exception(SingleInstanceGuard::Exception::OPERATION_NOT_SUPPORTED);
}

SingleInstanceGuard& SingleInstanceGuard::Write(void *pData, int nSize)
{	
	if(!g_bRunning)// first instance needn't write anything
		throw Exception(Exception::SHARING_WITH_SELF);
	
	if(!g_bHasWrittenPreviously)
		WaitForSingleObject(g_hCanWriteData, INFINITE);

	g_bHasWrittenPreviously = true;

	if(g_nBytesRW + nSize > g_nSharedDataSize)
		throw Exception(Exception::DATA_TOO_BIG);
	
	char *pBytes = (char*)MapViewOfFile(g_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);

	if(0==pBytes)
		throw Exception(Exception::UNABLE_TO_WRITE);
	
	memcpy(pBytes+sizeof(long)+sizeof(long)+g_nBytesRW, pData, nSize);
	g_nBytesRW += nSize;
	UnmapViewOfFile(pBytes);
	
	return *this;
}

bool SingleInstanceGuard::Read(void *pData, int nSize)
{
	// This is called only after successful wait on g_hCanRead from the monitor thread.
	// So no need to wait here.
	
	if(g_bRunning)// only first instance must read
		throw Exception(Exception::SHARING_WITH_SELF);

	if(g_nBytesRW == g_nWrittenDataSize)
		return false;// no more read

	if(g_nBytesRW + nSize > g_nSharedDataSize)
		throw Exception(Exception::DATA_TOO_BIG);
	
	char *pBytes = (char*)MapViewOfFile(g_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);
	
	if(0==pBytes)
		throw Exception(Exception::UNABLE_TO_READ);

	if(g_nWrittenDataSize < 0)
	{
		g_nWrittenDataSize = *(long*)(pBytes+sizeof(long));
		// reset so that an empty alert after a shared data alert won't read wrong data
		*(long*)(pBytes+sizeof(long)) = 0;
	}
	
	if(0 == g_nWrittenDataSize)
	{
		SetEvent(g_hCanWriteData);// let others write
		return false;
	}
	
	memcpy(pData, pBytes+sizeof(long)+sizeof(long)+g_nBytesRW, nSize);
	g_nBytesRW += nSize;
	UnmapViewOfFile(pBytes);	

	if(g_nBytesRW == g_nWrittenDataSize)
		SetEvent(g_hCanWriteData);// let others write

	return true;
}

void SingleInstanceGuard::AlertTheRunningInstance()
{
	if(!g_bHasWrittenPreviously)
		WaitForSingleObject(g_hCanWriteData, INFINITE);
	else
	{
		// write the number of bytes written into the MMF into the MMF
		char *pBytes = (char*)MapViewOfFile(g_hMMF, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if(0==pBytes)
			throw Exception(Exception::UNABLE_TO_ALERT);
		*(long*)(pBytes + sizeof(long)) = g_nBytesRW;
		UnmapViewOfFile(pBytes);
	}
	g_bHasWrittenPreviously = false;
	// alert
	SetEvent(g_hCanReadData);
}

void SingleInstanceGuard::SetTargetWindow(HWND h)
{
	if(g_bRunning)// only the first instance needs this
		throw Exception(Exception::OPERATION_NOT_SUPPORTED);

	g_hWndFirstInstanceWindow = h;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

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



Comments and Discussions