How to use an ATL-control with MFC





3.00/5 (10 votes)
Dec 12, 2000

250846

2166
A step by step tutorial explaining how to use an ATL-control with MFC
Introduction
This article shows you how can use an ATL-control with MFC. I've received a lot of e-mail with the question how you can use the FileMonitor-control with MFC. The example I'm using is a simple MFC-dialog based program that notifies the user when there are files created, modified or deleted in the temporary-file directory.
Step 1: Create a MFC-dialog program.
- Create a new project. Select MFC(exe) program in the AppWizard. Accept all the defaults. It's not necessary to select automation.
Step 2: Add a sink-object.
To receive events from an ATL-control you have to create an ATL-object that links with the control. This object is called a sink-object.
- Select Insert / Insert New ATL Object.
- Click 'Yes' on the following question:
- Select a single object
- Use FileMonitorSink as Short Name of the object. Don't change the
other fields.
Accept the default in the Attributes tab.
Step 3: Change the OBJECT-map.
- Because you don't want to let other applications create your
sink-object, you have to change the OBJECT-map in the TempMonitor.cpp as
follows:
BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY_NON_CREATEABLE(CFileMonitorSink)
END_OBJECT_MAP()
Step 4: Import the type-library of FileMonitor
By importing the type-library (the .tlb file), the content of the type-library is converted to C++ classes, mostly describing the interfaces. This makes it easy for us to use the interfaces of the FileMonitor-control.
- Add the following code to stdafx.h
#import "..\\FileMonitor\\FileMonitor.tlb" named_guids
For more information on #import see your MSDN library. If you don't want to use namespaces you could add the parameter no_namespace to the statement. I like the use of namespaces, so I don't use no_namespaces.
Step 5: Finishing the code for the FileMonitorSink object.
Select the headerfile of the FileMonitorSink. In our example this is FileMonitorSink.h
- Derive your class from
public IDispEventImpl<1, CFileMonitorSink, &FILEMONITOR::DIID__IWatchEvents, &FILEMONITOR::LIBID_FILEMONITOR, 1, 0>
The first parameter is an ID. You use this also in the SINK-map.
- Add the following to the COM-map
COM_INTERFACE_ENTRY_IID(FILEMONITOR::DIID__IWatchEvents, IDispatch)
- Create a Sink map
BEGIN_SINK_MAP(CFileMonitorSink) SINK_ENTRY_EX(1, FILEMONITOR::DIID__IWatchEvents, 1, OnNotify)
END_SINK_MAP()The Filemonitor sends an event Notify with a BSTR parameter and an integer parameter. The first parameter is the ID we've used in the IDispEventImpl. The third parameter is the DISDIP of the method in the interface IWatchEvents.
- Add the OnNotify() method to your class
void __stdcall OnNotify(BSTR sPathName, short nType) { AFX_MANAGE_STATE(AfxGetAppModuleState()) MessageBox(NULL, _T("File notification"), _T("FileMonitorApp"), MB_OK); }
Note the use of
AFX_MANAGE_STATE
macro. You have to use this in every method of this class. See for more information about this in step 7. At the moment we use aMessageBox
to notify us when the sink-interface receives an event. At a later time we will change that code.
Step 6: Change the Application.
Select the headerfile which define the dialog. In our example this is TempMonitorDlg.h.
- Add a member which contains a pointer to the
IWatch
interface of the FileMonitor control
CComPtr<FILEMONITOR::IWatch> m_FileMonitor;
- Add a member which is a COM-object of the sink interface.
CComObject<CFileMonitorSink> *m_FileMonitorSink;
- Add the following code to the OnInitDialog-method
m_FileMonitor.CoCreateInstance(__uuidof(FILEMONITOR::Watch), NULL, CLSCTX_INPROC_SERVER); CComObject<CFileMonitorSink>::CreateInstance(&m_FileMonitorSink);
Step 7: Link the Sink-interface to the FileMonitor-control.
Before we can receive events from the FileMonitor, we've to link the Sink-interface to the FileMonitor-control. This is also called Advising. Because we've implemented IDispEventImpl in the sink object, we can use the method DispEventAdvise.
- Add a member to CFileMonitorSink class to hold the IUnknown
interface of FileMonitor
CComPtr<IUnknown> m_Object;
- Add a method Start to the IFileMonitorSink interface as follows:
The first parameter is the object we want to advise. The second parameter will notify us if the advise succeeded or not. This method will be used to advise the FileMonitor.
This is the code for the Start-method:STDMETHODIMP CFileMonitorSink::Start(IUnknown *pSinkThisObject, VARIANT_BOOL *succeeded) { AFX_MANAGE_STATE(AfxGetAppModuleState()) if ( DispEventAdvise(pSinkThisObject) == S_OK) { m_Object = pSinkThisObject; *succeeded = VARIANT_TRUE; } else { *succeeded = VARIANT_FALSE; } return S_OK; }
A Note about the
AFX_MANAGE_STATE()
macro. Visual C++ generates the wrong code for it. You have to changeAFX_MANAGE_STATE(AfxGetStaticModuleState())
to the code above. This is a bug in Visual C++. Check this every time you add a method to an ATL-object in a MFC application. You can read more about this in the Q231592 article of Microsoft.- Add a method Stop to the IFileMonitorSink interface as
follows:
This method will be used to unadvise the FileMonitor.
This is the code for the method:
STDMETHODIMP CFileMonitorSink::Stop() { AFX_MANAGE_STATE(AfxGetAppModuleState()) if ( m_Object ) { DispEventUnadvise(m_Object); } return S_OK; }
Step 8: Start the sinking of the FileMonitor events.
- Change the
OnInitDialog
method after the creation of the sink-object.
VARIANT_BOOL succeeded; m_FileMonitorSink->Start(m_FileMonitor, &succeeded);
- Handle the WM_DESTROY-message as follows:
void CTempMonitorDlg::OnDestroy() { CDialog::OnDestroy(); m_FileMonitorSink->Stop(); }
Step 9: Add the code for monitoring the temporary file directory.
- Add the following code to the
OnInitDialog()
after the call of theStart()
method
// Monitor the temporaryfile directory TCHAR tszBuf[MAX_PATH]; if (0 == GetTempPath(MAX_PATH, tszBuf)) // This should never fail { MessageBox("Unable to locate the temporaryfile directory !"); } else { m_FileMonitor->AddPath(tszBuf); m_FileMonitor->Start = VARIANT_TRUE; }
Step 10: Compile and run the first test.
- When the program is running, then try to change, delete or create a file in the temporary directory. On my system this was the C:\Windows\Temp. You should see the messagebox, when you do this.
Step 11: Finishing our application.
- Here you can download the full application: TempMonitor.zip
Note : If you find another way which is better than this, please notify me. I've created this example with lots of information I've find on the internet and in the "Professional ATL COM" Programming book.
- Add a member which is a COM-object of the sink interface.