By default, when you create a new ATL EXE project, you are not given the chance to add MFC support to the project. This is because Microsoft is giving developers a nice, tight framework for creating ATL servers. Adding MFC could potentially slow things down.
However, there are times where MFC is required to be used by the ATL server, and using the ATL alternatives is not enough. I ran into this exact scenario: I had an existing codebase that I needed to be wrapped by a COM object. I wanted to use an ATL EXE so that I didn’t have to deal with component services, etc.
I found this article from 2005, How to add MFC support to an ATL project in Visual C++, however this article is outdated and does not work for Visual Studio 2008. What follows are the steps I found to have worked for my Visual Studio 2008 project.
1. Add MFC support to the project settings
This is done on the General tab in project properties. Set “Use of MFC” to either “Use MFC in a Shared DLL” or “Use MFC in a Static Library”.
2. Add the MFC headers to stdafx.h
Add the following code to your stdafx.h file above the ATL header files but after _ATL_APARTMENT_THREADED:
#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxdisp.h> // MFC Automation extensions
3. Remove the old COM module and WinMain
In your EXE’s main cpp file, remove the module object and the WinMain function. However, hold onto the DECLARE_LIBID and DECLARE_REGISTRY_APPID_RESOURCEID lines. You’ll need them in the next step.
4. Add the new COM module and CWinApp
In the same file, add code like the following:
class CMyProjectModule : public CAtlMfcModule
{
public:
DECLARE_LIBID(LIBID_MyProject)
DECLARE_REGISTRY_APPID_RESOURCEID(IDR_MYPROJECT,
"{00000000-0000-0000-0000-000000000000}")
};
CMyProjectModule _AtlModule;
class CMyProjectApp : public CWinApp
{
public:
virtual BOOL InitInstance();
virtual int ExitInstance();
};
CMyProjectApp theApp;
BOOL CMyProjectApp::InitInstance()
{
// Initialize OLE libraries.
if (!AfxOleInit())
{
AfxMessageBox(_T("OLE Initialization Failed!"));
return FALSE;
}
// Parse command line for standard shell commands,
// DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
#if !defined(_WIN32_WCE) || defined(_CE_DCOM)
// Register class factories via CoRegisterClassObject().
if (FAILED(_AtlModule.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE)))
return FALSE;
#endif // !defined(_WIN32_WCE) || defined(_CE_DCOM)
//// App was launched with /Embedding or /Automation switch.
//// Run app as automation server.
if (cmdInfo.m_bRunEmbedded || cmdInfo.m_bRunAutomated)
{
// Don't show the main window
return TRUE;
}
// App was launched with /Unregserver or /Unregister switch.
if (cmdInfo.m_nShellCommand == CCommandLineInfo::AppUnregister)
{
_AtlModule.UpdateRegistryAppId(FALSE);
_AtlModule.UnregisterServer(TRUE);
return FALSE;
}
// App was launched with /Register or /Regserver switch.
if (cmdInfo.m_nShellCommand == CCommandLineInfo::AppRegister)
{
_AtlModule.UpdateRegistryAppId(TRUE);
_AtlModule.RegisterServer(TRUE);
return FALSE;
}
return TRUE;
}
int CMyProjectApp::ExitInstance()
{
#if !defined(_WIN32_WCE) || defined(_CE_DCOM)
_AtlModule.RevokeClassObjects();
#endif
return CWinApp::ExitInstance();
}
The DECLARE_LIBID and DECLARE_REGISTRY_APPID_RESOURCE should be copied from what was saved in step 3.
5. Use AFX_MANAGE_STATE
You’ll need to make use of the following in your exported functions:
AFX_MANAGE_STATE(AfxGetAppModuleState());
That should do it. Compile and link and you should have MFC support.
One problem that I am working on is that after this change, the EXE shuts down immediately after the last COM object has been destroyed. I am trying to figure out how to maintain the timeout feature that the original ATL EXE project had.