News | Articles | Libraries | Developer Tools | Books | Forum Links | Search   
Sections:
 

QA: How can I handle command line parameters in MFC based application?

By Alexander Shargin, March 21, 2001.
Print version

Question

I want to handle command line parameters in my MFC based application. The problem is that MFC based programs have only one copy and when I start my program and it's already running I cannot handle the command like parameters.

Let's say instance A of my program is already running. I start instance B with some command line parameters. Instance B checks that instance A is running, sends A foreground and exits. So I cannot access command line parameters of B from instance A.

Answer

By default MFC checks if the application is already running in AfxWinMain function (MFC equivalent of WinMain). If it finds other running instance, it makes its window foreground and terminates. So the command line parameters are lost. MFC allows you yo replace AfxWinMain function with your own to override the default behaviour. Your version of AfxWinMain can pass command line to the first instance by using some IPC mechanism. I suggest using WM_COPYDATA message. After command line is sent AfxWinMain just returns.

To implement your own AfxWinMain function follow these steps.

  1. Add winmain.cpp file to your project.
  2. Copy MFC implementation of AfxWinMain (along with FindApplicationWindowProc and other supporting code) from %WinCE Tools%\wce300\MS Pocket PC\mfc\src\winmain.cpp file.
  3. Edit FindApplicationWindowProc function. It is used to find a window of running instance. This is not necessarily the main window of your application (it may be command bar or some other window). Since we want to send WM_COPYDATA message and process it in the first instance we must make sure that the main window is found. Checking window title is generally sufficient to filter out all other windows.
  4. Edit AfxWindMain function. After main window of the first instance is found we must send WM_COPYDATA to it and pass a pointer to command line as one of message parameters. Note that Windows does all work required to pass the data through process boundaries.
  5. Add OnCopyData handler to the main window's class (typically CMainFrame). Alas the wizard can not do this for you so you will have to add the handler and message map entry manually.

Source code

This section contains fragments of winmain.cpp, mainfrm.cpp and mainfrm.h files implementing the steps described above. Key parts are marked bold.

winmain.cpp:

#include "stdafx.h"
#if defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 300)
#define STRIKENUM_MAX 3
typedef struct _FindAppT
{
	TCHAR *pzExeName;
	HWND hwnd;
} 
FindAppT, *pFindAppT;

BOOL CALLBACK FindApplicationWindowProc(HWND hwnd, LPARAM lParam)
{
	DWORD		dwProcessID;
	INT		    iLen;
	TCHAR	    szTempName[MAX_PATH]=TEXT("\0");
	pFindAppT	pFindApp=(pFindAppT)lParam;

	::GetWindowThreadProcessId(hwnd,&dwProcessID);
	if (!dwProcessID) 
		return TRUE;

	iLen=::GetModuleFileName((HMODULE)dwProcessID,szTempName,MAX_PATH);
	if (!iLen) 
		return TRUE;

	if (!_tcsicmp(szTempName, pFindApp->pzExeName) )
	{	
		// Check window title to make sure the main window is found.
		::GetWindowText(hwnd, szTempName, MAX_PATH);
		if (!_tcsicmp(szTempName, _T("CmdLineDemo")) ) // specify your title here.
		{	
			pFindApp->hwnd=hwnd;		
			return FALSE;
		}
	}

	return TRUE;
}
#endif
/////////////////////////////////////////////////////////////////////////////
// Standard WinMain implementation
//  Can be replaced as long as 'AfxWinInit' is called first

int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPTSTR lpCmdLine, int nCmdShow)
{
	ASSERT(hPrevInstance == NULL);

	int nReturnCode = -1;
	CWinThread* pThread = AfxGetThread();
	CWinApp* pApp = AfxGetApp();

	// AFX internal initialization
	if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
		goto InitFailure;

	// App global initializations (rare)
	if (pApp != NULL && !pApp->InitApplication())
		goto InitFailure;

#if defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 300)
	// WinCE: Only one application instance can be run
	HANDLE hMuTex;
	TCHAR szTempName[MAX_PATH], szExeName[MAX_PATH];
	INT	i, iValue;
	FindAppT findApp;
	BOOL bGoAway;
	
	iValue=::GetModuleFileName(NULL,szExeName,MAX_PATH);			
	_tcscpy(szTempName,szExeName);
	for(i = 0; i < iValue; i++) 
	{
		if (szTempName[i]=='\\') 
			szTempName[i]='/';
	}

	hMuTex = ::CreateMutex(NULL, FALSE, szTempName);
	if (hMuTex!=NULL)
	{
		if (::GetLastError() == ERROR_ALREADY_EXISTS) 
		{
			bGoAway = FALSE;
			memset(&findApp,0,sizeof(FindAppT));
			findApp.pzExeName=(TCHAR *)szExeName;
			iValue=pThread->GetThreadPriority();

			for (i=0; i< STRIKENUM_MAX; i++) {
				::EnumWindows(FindApplicationWindowProc,(LPARAM)&findApp);

				if (findApp.hwnd) 
				{
					HWND hWnd = (HWND)findApp.hwnd;
					::SetForegroundWindow(hWnd);

					// Send WM_COPYDATA to the first instance.
					COPYDATASTRUCT cds;
					cds.dwData = 0;
					cds.cbData = (_tcslen(lpCmdLine)+1)*sizeof(TCHAR);
					cds.lpData = lpCmdLine;
					::SendMessage(hWnd, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds);

					bGoAway=TRUE;
					break;
				}
				// wait for other app to finish starting or stopping
				pThread->SetThreadPriority(THREAD_PRIORITY_IDLE);
				Sleep(1000);    
				pThread->SetThreadPriority(iValue);
			}
			if (bGoAway) 
				goto InitFailure;
		}
	}
#endif	

	// Perform specific initializations
	if (!pThread->InitInstance())
	{
		if (pThread->m_pMainWnd != NULL)
		{
			TRACE0("Warning: Destroying non-NULL m_pMainWnd\n");
			pThread->m_pMainWnd->DestroyWindow();
		}
		nReturnCode = pThread->ExitInstance();
		goto InitFailure;
	}
	nReturnCode = pThread->Run();

InitFailure:
#ifdef _DEBUG
	// Check for missing AfxLockTempMap calls
	if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
	{
		TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
			AfxGetModuleThreadState()->m_nTempMapLock);
	}
	AfxLockTempMaps();
	AfxUnlockTempMaps(-1);
#endif

	AfxWinTerm();
	return nReturnCode;
}

/////////////////////////////////////////////////////////////////////////////

mainfrm.h:

class CMainFrame : public CFrameWnd
{
...

// Generated message map functions
protected:
	//{{AFX_MSG(CMainFrame)
	afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
	afx_msg void OnSetFocus(CWnd *pOldWnd);
		// NOTE - the ClassWizard will add and remove member functions here.
		//    DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG
	afx_msg LRESULT OnCopyData(WPARAM, LPARAM);
	DECLARE_MESSAGE_MAP()
};

mainfrm.cpp:

...

IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
	//{{AFX_MSG_MAP(CMainFrame)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		//    DO NOT EDIT what you see in these blocks of generated code !
	ON_WM_CREATE()
	ON_WM_SETFOCUS()
	//}}AFX_MSG_MAP
	ON_MESSAGE(WM_COPYDATA, OnCopyData)
END_MESSAGE_MAP()

...

LRESULT CMainFrame::OnCopyData(WPARAM wParam, LPARAM lParam)
{
	COPYDATASTRUCT *cds = (COPYDATASTRUCT *)lParam;

	// Process command line (contained in (TCHAR*)cds->lpData member).
	// In this example we just show command line parameters
	// in a message box.
	AfxMessageBox((TCHAR*)cds->lpData);

	return 0;
}

Related resources:

Discuss

Discuss this article. Here you can write your comments and read comments of other developers.
Rate this article:     Poor Excellent    
 12345 
© 2001-2005 Pocket PC Developer Network, a division of Spb Software House