COM Foundations Inside MFC
Every COM-aware MFC class ultimately derives from CCmdTarget, which supplies the plumbing for OLE_MAP entries the way CWnd supplies plumbing for message maps. Instead of writing QueryInterface, AddRef, and Release by hand for each interface a class supports, MFC generates them from a BEGIN_INTERFACE_PART/END_INTERFACE_PART block combined with a DECLARE_INTERFACE_MAP and BEGIN_INTERFACE_MAP in the .cpp file, and the framework's nested-class trick (an 'XInterfaceName' inner class) implements the interface's vtable while forwarding calls back to the outer CCmdTarget-derived object. Before any COM work can run, the application must call AfxOleInit() (which wraps CoInitialize) in InitInstance, or COM calls will fail with CO_E_NOTINITIALIZED.
Cricket analogy: AfxOleInit is like a match not being able to start until the toss and pitch inspection are formally completed - just as no COM call can proceed until CoInitialize (wrapped by AfxOleInit) has run for that thread.
Automation Servers with IDispatch
For late-bound Automation (the kind VBA or a scripting host uses), a CCmdTarget-derived class adds a dispatch map with DECLARE_DISPATCH_MAP and DISP_FUNCTION/DISP_PROPERTY entries, each assigned a DISPID that matches the .odl or .idl type library definition; MFC's built-in IDispatch implementation (via ColeDispatchImpl internally) then handles GetIDsOfNames and Invoke automatically by walking that map. A dual interface - exposing both IDispatch and a custom vtable-based interface - lets early-bound C++ clients call methods directly through the vtable for speed while late-bound scripting clients still go through Invoke, and the ClassWizard/Class View 'Automation' tab historically generated most of this boilerplate. Exposed properties and methods declared this way automatically show up correctly typed in the type library, which is what lets Excel VBA, for example, get IntelliSense against a custom MFC automation server.
Cricket analogy: A dual interface is like a commentator who can be understood both by hardcore fans reading detailed scorecards (early-bound vtable calls) and casual viewers just hearing simplified spoken updates (late-bound Invoke calls), from the same underlying data.
Consuming COM Objects from MFC
An MFC client consumes another COM server either through the ClassWizard-generated COleDispatchDriver-derived wrapper (built by pointing 'Add Class > From a Type Library' at the server's .tlb, producing typed InvokeHelper calls) or through the Visual C++ #import directive, which generates smart-pointer wrapper classes (like CComPtr-compatible _com_ptr_t types with a .tlh/.tli pair) directly from the type library at compile time. The #import approach is generally preferred in modern MFC code because the generated smart pointers throw _com_error exceptions on HRESULT failures and handle AddRef/Release automatically, whereas raw COleDispatchDriver code requires manually checking return codes and calling ReleaseDispatch. Regardless of the approach, every apartment-threaded COM object obtained on a given thread must only be used on that same thread unless properly marshaled, which is a frequent source of bugs when COM objects created on the UI thread are later touched from a worker thread.
Cricket analogy: #import's smart pointers auto-managing AddRef/Release is like a well-drilled fielding unit automatically covering each other's positions without needing the captain to shout instructions for every single ball, unlike manual COleDispatchDriver bookkeeping.
// Exposing an Automation method via a dispatch map (server side)
BEGIN_DISPATCH_MAP(CReportServer, CCmdTarget)
DISP_FUNCTION(CReportServer, "GenerateReport", GenerateReport, VT_BOOL, VTS_BSTR VTS_I4)
DISP_PROPERTY(CReportServer, "LastError", m_lastError, VT_BSTR)
END_DISPATCH_MAP()
BOOL CReportServer::GenerateReport(LPCTSTR lpszPath, long nFormat)
{
// ... build the report ...
return TRUE;
}
// Consuming it from an MFC client using #import
#import "reportserver.tlb" no_namespace
void CClientDlg::OnGenerate()
{
try
{
IReportServerPtr pServer;
pServer.CreateInstance(__uuidof(ReportServer));
pServer->GenerateReport(L"C:\\out\\report.pdf", 1);
}
catch (_com_error& e)
{
AfxMessageBox(e.ErrorMessage());
}
}Forgetting to call AfxOleInit() in CWinApp::InitInstance is one of the most common causes of mysterious early-startup COM failures in MFC apps; CoCreateInstance and similar calls will return CO_E_NOTINITIALIZED (or crash) until it has run on that thread.
A dual interface is generally preferred over pure IDispatch when designing a new MFC automation server, because it gives early-bound C++/#import clients direct vtable-speed calls while still supporting VBA/VBScript clients through the same object's IDispatch::Invoke.
- CCmdTarget is the base for all COM-aware MFC classes, supplying interface maps analogous to how CWnd supplies message maps.
- AfxOleInit() must run (wrapping CoInitialize) before any COM calls succeed on that thread; forgetting it causes CO_E_NOTINITIALIZED errors.
- BEGIN_INTERFACE_MAP/BEGIN_INTERFACE_PART generate nested XInterfaceName classes implementing each supported interface's vtable.
- Dispatch maps (DECLARE_DISPATCH_MAP, DISP_FUNCTION/DISP_PROPERTY) implement IDispatch automatically for late-bound Automation clients like VBA.
- A dual interface exposes both a custom vtable and IDispatch, giving early-bound speed and late-bound scripting compatibility from one object.
- #import generates typed smart-pointer wrapper classes with automatic AddRef/Release and _com_error exceptions, preferred over manual COleDispatchDriver code.
- COM objects are apartment-threaded; using one from a thread other than the one that created it requires proper marshaling.
Practice what you learned
1. What must be called before any COM operation will succeed in an MFC application?
2. Which MFC macro pair implements late-bound Automation support via IDispatch?
3. What is the main advantage of #import over COleDispatchDriver-based wrapper classes?
4. What is a dual interface?
5. What base class supplies the interface-map plumbing for COM-aware MFC classes?
Was this page helpful?
You May Also Like
Multithreading in MFC
How MFC wraps Win32 threading into CWinThread, worker vs. UI threads, and the synchronization classes (CCriticalSection, CMutex, CEvent, CSemaphore) used to coordinate them safely.
ActiveX Controls with MFC
How MFC's COleControl base class and the MFC ActiveX Control Wizard produce reusable OCX controls with properties, methods, events, and property pages, and how MFC apps host such controls.
MFC Database Access
MFC's two parallel database frameworks - ODBC-based CDatabase/CRecordset and DAO-based CDaoDatabase/CDaoRecordset - and how record field exchange (RFX/DFX) binds recordset columns to member variables.
Related Reading
Related Study Notes in Microsoft Technologies
Browse all study notesWindows 10 / UWP Development Study Notes
.NET · 30 topics
Microsoft TechnologiesWindows Batch Scripting Study Notes
Batch · 30 topics
Microsoft TechnologiesSilverlight Study Notes
.NET · 30 topics
Microsoft TechnologiesXAML Study Notes
.NET · 30 topics
Microsoft TechnologiesWPF (Windows Presentation Foundation) Study Notes
.NET · 30 topics
Microsoft TechnologiesMVVM Design Pattern Study Notes
.NET · 30 topics