Making Multiple Instances of an Application run in the Same Process Space as different Threads

If you are familiar with Internet Explorer 4.0 you would find that in the Advanced Internet Settings there is an option "Browse in a New Process". If you uncheck this check box and run IExplorer.exe and then start task manager (or Process Viewer) you would find that there is no IExplorer in the processes yet the Internet Explorer appears. Checking "Browse in a new process" and starting Internet Explorer runs it as IExplorer.exe again.

In the first case instead of a separate process being created for Internet Explorer a thread is created in explorer.exe and instances of Internet Explorer run in different threads within the process space of explorer.exe. Recently, I also found the need to do the same in my application. This article demonstrates how this could be achieved.

This is particularly useful when lots of data need to be shared among instances of applications. Typically the first instance of the application loads data from the disk file or initializes through other methods and this data needs to be more or less constant throughout the application. Memory mapped file is obviously an alternative but it requires quite a bit of pointer arithmetic and to a lot of extent convenience is lost.This alternative is convenient but has certain risks associated like if a single instance of the application crashes then all the instances are gone and also lot of defensive coding is required for ensuring thread safety.


Coming to the implementation, it's quite simple. Any instance of the application launched either through Explorer or through an API call such as CreateProcess, first detects if a window of a particular class exists. If it does not, a new window is created. If the window does exists, the newly created process simple sends a message to the window and transfers the control to the already running instance of the application. The already running instance of the application launches a separate thread but the end user feels that different instances of application are running.

In my demonstration I take an example of a simple MFC MDI application. Even if multiple instances of the application are launched, Task Manager or Process Viewer will show only one process. When observed with SPY++, it would be seen that the process has many threads and different set of MDI windows belong to the different threads as shown in the following screen shot.

Screen Shot

Let me now explain stepwise how this has been achieved.

1. In InitInstance function of the main CWinApp derived class, following code has been added.

	CWnd* pWnd = FindOrCreateAppWnd();

	if (!pWnd) //Creation failed
		return FALSE; //Terminate safely
	// m_pMainWnd is NULL if either we are not the first instance
	// or if window creation failed somehow
	if (m_pMainWnd == NULL)
		return FALSE;
	// Register the application's document templates.  Document templates
	//  serve as the connection between documents, frame windows and views.
	CMultiDocTemplate* pDocTemplate;
	pDocTemplate = new CMultiDocTemplate(
		RUNTIME_CLASS(CChildFrame), // custom MDI child frame

	//Finally send a message to create a thread

The FindOrCreateAppWnd function first finds whether a window of a class "SomeProcClass" exists if it does it returns the pointer to that window else a new window of that class is created and the pointer is returned (m_pMainWnd is also set to the pointer to newly created window).

If a window already exists then the command line information is conveyed to the window through WM_COPYDATA message so that the already running process to which the execution would now switch make use of them. In the sample application I have not used them but it's not a big deal. Here is the listing of function SendCopyDataMsg.


void CSameprocApp::SendCopyDataMsg(CWnd* pWnd)
	//Send the command line info to the other application

	cds.dwData = m_nCmdShow ;

	cds.cbData		 = _tcslen(m_lpCmdLine ) + 1;
	cds.lpData       = reinterpret_cast<LPVOID>(m_lpCmdLine);
	pWnd->SendMessage(WM_COPYDATA, 0, (LPARAM)(&cds));

2. If   FindWindow fails to locate the window the special window of "SameProcClass" encapsulated in C++ class CAppWnd is created.

bool CAppWnd::CreateAppWnd()
	//Register SameProcClass
	WNDCLASS wndclass;
	::ZeroMemory(&wndclass, sizeof(WNDCLASS));

	wndclass.lpfnWndProc = AfxWndProc;
	wndclass.lpszClassName = szClassName;// "SameProcClass"
	wndclass.hInstance = AfxGetInstanceHandle();

	if (!AfxRegisterClass(&wndclass))
		return false;	

	// Now create the window
	if (!CFrameWnd::Create(szClassName, _T("SameProcMainWnd"), 0))
		return false;	

	return true;

Everything is self explanatory. The important point to be observed is that the lpfnWndProc member of WNDCLASS should be AfxWndProc as MFC message mapping architecture is being used.

3. CAppWnd handles WM_COPYDATA as following :-

BOOL CAppWnd::OnCopyData(CWnd* pWnd, COPYDATASTRUCT* pCopyDataStruct) 
	// See that we don't close
	m_bCanClose = false;
	// Command line info from another instance is communicated here
	// If you are interested in having the command line processing you have to add
	// a bit of code to pass it to CAppThread class
	DWORD nCmdShow = pCopyDataStruct->dwData ;
	LPTSTR szCmdLine = reinterpret_cast<LPTSTR>(pCopyDataStruct->lpData);

	return CFrameWnd::OnCopyData(pWnd, pCopyDataStruct);

It gets back the nCmdShow and the command line passed from other application and it creates a thread CAppThread.

4. Finally the class CAppThread which represents each individual thread creates the MDI windows (which is traditionally done in the application class).

BOOL CAppThread::InitInstance()
	// create main MDI Frame window
	CMainFrame* pMainFrame = new CMainFrame;
	if (!pMainFrame->LoadFrame(IDR_MAINFRAME))
		return FALSE;
	m_pMainWnd = pMainFrame;

	CCommandLineInfo cmdInfo;

	// Dispatch commands specified on the command line
	if (!AfxGetApp()->ProcessShellCommand(cmdInfo))
		return FALSE;

	// The main window has been initialized, so show and update it.
	pMainFrame->ShowWindow( SW_SHOWNORMAL/* m_nCmdShow */);

	return TRUE;

The point to mention here is that a static counter variable s_nInstances is being maintained in the CAppThread class which maintains a count of number of "virtual instances" (i.e. the number of CAppThreads running in the process). This is needed so that the main application knows when to shutdown.

5. When each thread exits it decrements the count s_nInstances and when the count reaches zero the application main window (CAppWnd) should be notified that it is time to close.

int CAppThread::ExitInstance()

	if (s_nInstances == 0) // No more Instances left

	return CWinThread::ExitInstance();
6. Coming to the OnClose member function of CAppWnd some strange jugglery is being done.

void CAppWnd::OnClose() 
	static bool bTimerSet = false;
	if (!m_bCanClose)
		if (bTimerSet)

		m_bCanClose = true;
		//Set a timer after which we can close
		SetTimer(1, 1000, NULL);

		bTimerSet = true;

void CAppWnd::OnTimer(UINT nIDEvent) 

	if (nIDEvent == 1)

First time when the function reaches OnClose m_bCanClose is false. This makes the application set a timer for 1 second. Next in the WM_TIMER handler WM_CLOSE message is being posted again. This makes the application close. In the mean time if WM_COPYDATA message arrives m_bCanClose is set to false in the WM_COPYDATA handler so that application doesnot close down.

7. One final point remaining to be mentioned is that the command routing must be done to the CAppThreads so that they also get their share of WM_COMMAND messages.

BOOL CSameprocApp::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo) 
	// The current thread should have the first preference

	CWinThread* pThread = AfxGetThread();

	if (pThread != this) // Not the main thread
		if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
			return TRUE;

	return CWinApp::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);

The advantage of running multiple instances in different threads in the same application rather than in separate processes are :-

  • Quick initialization of Application esp. in those cases when application has one time initialization work which is quite costly.
  • Easy sharing of data.

Finally, a word of caution. Multithreaded applications are very hard to debug and test. A great deal of defensive coding is required to protect the global data.

Download demo project - 55 KB

Date Last Updated: March 3, 1999