Introduction
This article will explain implementing an ATL COM EXE server with MFC support. As an example, we create an ATL COM EXE server that can create an MFC child window and an ATL child window in the client side�s parent window.
Background
In a recent project, we are trying to add MFC Support to an ATL EXE server. We want to create both MFC window using CWnd
derived class and ATL window using CWindow
in our server. There was an article in MSDN about this :'Q 173974 : HOWTO: Add MFC Support to an ATL Project'. And this is the main reference from which we created our example application.
Using the code
The code consists of two Microsoft Visual C++ 6.0 projects:
- ATLMFCCOMClient - Client side implementation. This is a simple SDI application.
- ATLMFCCOMServer - Server side implementation.
These projects support both non-Unicode and Unicode compilation.
Server side implementation
This project was first created using ATL COM Wizard, and then modified to support MFC according MSDN's article Q 173974. However, this is not enough; there are other three modifications we should add:
- Add
AtlAxWinInit()
in CMyApp::InitInstance()
.
This will allow us create ATL window correctly. See Figure 1.
- Add
_Module.StartMonitor()
in CMyApp::InitInstance()
.
2 and 3 allow the EXE server exit correctly when the client exits. See Figure 1.
- Add
theApp.PostThreadMessage( WM_QUIT, 0, 0)
in CExeModule::MonitorShutdown()
.
See Figure 2.
BOOL CMyApp::InitInstance()
{
if (!AfxOleInit())
{
AfxMessageBox(_T("OLE Initialization Failed!"));
return FALSE;
}
AtlAxWinInit();
_Module.Init(ObjectMap,m_hInstance);
#ifdef _AFXDLL
Enable3dControls();
#else
Enable3dControlsStatic();
#endif
COleObjectFactory::UpdateRegistryAll();
VERIFY(SUCCEEDED(_Module.RegisterServer(TRUE)));
COleObjectFactory::RegisterAll();
VERIFY(SUCCEEDED(_Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE)));
_Module.StartMonitor();
if (RunEmbedded() || RunAutomated())
{
return TRUE;
}
return FALSE;
}
Figure 1
void CExeModule::MonitorShutdown()
{
while (1)
{
WaitForSingleObject(hEventShutdown, INFINITE);
DWORD dwWait=0;
do
{
bActivity = false;
dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
} while (dwWait == WAIT_OBJECT_0);
if (!bActivity && m_nLockCnt == 0)
{
#if _WIN32_WINNT >= 0x0400 & defined(_ATL_FREE_THREADED)
CoSuspendClassObjects();
if (!bActivity && m_nLockCnt == 0)
#endif
break;
}
}
CloseHandle(hEventShutdown);
PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
theApp.PostThreadMessage( WM_QUIT, 0, 0);
}
Figure 2
We created two COM objects: ATLWindowObject
and MFCWindowObject
.
ATLWindowObject
has two interfaces: CreateATLWindow
and Update
.
MFCWindowObject
has two interfaces: CreateMFCWindow
and Update
.
Both Update()
interfaces are used to refresh the window when the client side parent window needs to be refreshed. See Client side implementation section.
We also created two window classes :CMFCWnd
derived from CWnd
and CATLWnd
derived from CWindow
. CMFCWnd
will be created by MFCWindowObject
while CATLWnd
will be created by ATLWindowObject
.
CMFCWnd
is easy, but CATLWnd
needs adding a message map manually, See Figure 3.
class CATLWnd : public CWindowImpl<CATLWND, CWindow>
{
public:
DECLARE_WND_CLASS(_T("ATL Window Class"))
CATLWnd() { m_hbrBkgnd = CreateSolidBrush(RGB(0,0,255)); }
~CATLWnd() { DeleteObject ( m_hbrBkgnd ); }
BEGIN_MSG_MAP(CATLWnd)
MESSAGE_HANDLER(WM_PAINT, OnPaint)
END_MSG_MAP()
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
RECT rcClient;
PAINTSTRUCT ps;
BeginPaint(&ps);
HDC dc = GetDC();
GetClientRect ( &rcClient );
FillRect ( dc, &rcClient, m_hbrBkgnd );
TextOut(dc,2,10,_T("ATL Window"),10);
EndPaint(&ps);
return 0;
}
protected:
HBRUSH m_hbrBkgnd;
};
Figure 3
Client side implementation
The function OnMfcWindow
will be called, when user select �Create MFC Window� menu. MFCWindowObject
will first be instantiated, and its interface CreateMFCWindow
will be called to create a MFC window.
Likely, the function OnATLWindow
will be called, when user select �Create ATLWindow� menu. ATLWindowObject
will first be instantiated, and its interface CreateATLWindow
will be called to create an ATL window. See Figure 4.
Note that both windows are the child windows of CATLMFCCOMClientView
void CATLMFCCOMClientView::OnMfcWindow()
{
if(m_pMFCWindowObject) return;
HRESULT hResult = S_OK;
hResult = CoCreateInstance(CLSID_MFCWindowObject,
NULL,CLSCTX_LOCAL_SERVER,IID_IMFCWindowObject,
(LPVOID*)&m_pMFCWindowObject);
m_pMFCWindowObject->CreateMFCWindow((long)m_hWnd);
}
void CATLMFCCOMClientView::OnAtlWindow()
{
if(m_pATLWindowObject) return;
HRESULT hResult = S_OK;
hResult = CoCreateInstance(CLSID_ATLWindowObject,
NULL,CLSCTX_LOCAL_SERVER,IID_IATLWindowObject,
(LPVOID*)&m_pATLWindowObject);
m_pATLWindowObject->CreateATLWindow((long)m_hWnd);
}
Figure 4
In server side implementation section, we mentioned that both the COM objects: ATLWindowObject
and MFCWindowObject
provided interface Update
. This interface is used in CATLMFCCOMClientView::OnDraw
to avoid window refresh issue, See Figure 5.
void CATLMFCCOMClientView::OnDraw(CDC* pDC)
{
CATLMFCCOMClientDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(m_pMFCWindowObject)
m_pMFCWindowObject->Update();
if(m_pATLWindowObject)
m_pATLWindowObject->Update();
}
Figure 5