Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

How to use an ATL-control with MFC

0.00/5 (No votes)
11 Dec 2000 1  
A step by step tutorial explaining how to use an ATL-control with MFC
  • Download the demo application - 46 Kb
  • 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:

      AddATLtoMFC.JPG (22397 bytes)

    • Select a single object

      ATLSimpleObject.JPG (58674 bytes)

    • Use FileMonitorSink as Short Name of the object. Don't change the other fields.

      ATLObjectWizard1.JPG (68460 bytes)

      Accept the default in the Attributes tab.

      ATLObjectWizard2.JPG (61288 bytes)

    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 a MessageBox 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:

    StartMethod.JPG (53493 bytes)

    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 change   AFX_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:

      StopMethod.JPG (42132 bytes)

      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 the Start() 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.

    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.

    License

    This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

    A list of licenses authors might use can be found here