65.9K
CodeProject 正在变化。 阅读更多。
Home

如何在 MFC 中使用 ATL 控件

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (10投票s)

2000年12月12日

viewsIcon

251385

downloadIcon

2166

一个一步步的教程,解释如何在 MFC 中使用 ATL 控件

  • 下载演示应用程序 - 46 Kb
  • 引言

    本文介绍如何将 ATL 控件与 MFC 一起使用。我收到了很多关于如何使用 FileMonitor 控件与 MFC 的电子邮件。我使用的例子是一个简单的基于 MFC 对话框的程序,当临时文件目录中有文件被创建、修改或删除时,它会通知用户。

    步骤 1:创建一个 MFC 对话框程序。

    • 创建一个新项目。在 AppWizard 中选择 MFC(exe) 程序。接受所有默认设置。无需选择自动化。

    步骤 2:添加一个接收器对象。

    要接收来自 ATL 控件的事件,您必须创建一个 ATL 对象来链接该控件。这个对象称为接收器对象。

    • 选择 插入 / 插入新的 ATL 对象
    • 在接下来的问题中点击“是”

      AddATLtoMFC.JPG (22397 bytes)

    • 选择单个对象

      ATLSimpleObject.JPG (58674 bytes)

    • 将对象的短名称设置为 FileMonitorSink。不要更改其他字段。

      ATLObjectWizard1.JPG (68460 bytes)

      在 Attributes 选项卡中接受默认设置。

      ATLObjectWizard2.JPG (61288 bytes)

    步骤 3:更改 OBJECT-map。

    • 因为您不希望其他应用程序创建您的接收器对象,所以您必须像下面这样更改 TempMonitor.cpp 中的 OBJECT-map。
      BEGIN_OBJECT_MAP(ObjectMap)
            OBJECT_ENTRY_NON_CREATEABLE(CFileMonitorSink)
      END_OBJECT_MAP()

    步骤 4:导入 FileMonitor 的类型库

    通过导入类型库(.tlb 文件),类型库的内容被转换为 C++ 类,主要描述接口。这使我们能够轻松使用 FileMonitor 控件的接口。

    • 将以下代码添加到 stdafx.h
      #import  "..\\FileMonitor\\FileMonitor.tlb"  named_guids

    有关 #import 的更多信息,请参阅您的 MSDN 库。如果您不想使用命名空间,可以在语句中添加 no_namespace 参数。我喜欢使用命名空间,所以我不使用 no_namespaces。

    步骤 5:完成 FileMonitorSink 对象的代码。

    选择 FileMonitorSink 的头文件。在我们的例子中是 FileMonitorSink.h。

    • 将您的类派生自
      public IDispEventImpl<1, CFileMonitorSink, 
                    &FILEMONITOR::DIID__IWatchEvents, &FILEMONITOR::LIBID_FILEMONITOR, 1, 0>

      第一个参数是一个 ID。您也可以在 SINK-map 中使用此 ID。

    • 将以下代码添加到 COM-map 中
      COM_INTERFACE_ENTRY_IID(FILEMONITOR::DIID__IWatchEvents, IDispatch)
    • 创建一个接收器映射
      BEGIN_SINK_MAP(CFileMonitorSink)
         SINK_ENTRY_EX(1, FILEMONITOR::DIID__IWatchEvents, 1, OnNotify)
      END_SINK_MAP()

      Filemonitor 发送一个带有 BSTR 参数和整数参数的 Notify 事件。第一个参数是我们已经在 IDispEventImpl 中使用的 ID。第三个参数是接口 IWatchEvents 中方法的 DISDIP。

    • 将 OnNotify() 方法添加到您的类中
      void __stdcall OnNotify(BSTR sPathName, short nType)
      {
          AFX_MANAGE_STATE(AfxGetAppModuleState())
          MessageBox(NULL, _T("File notification"), _T("FileMonitorApp"), MB_OK);
      } 

      注意 AFX_MANAGE_STATE 宏的使用。您必须在此类的每个方法中使用它。有关详细信息,请参见第 7 步。目前我们使用 MessageBox 来通知我们在接收器接口收到事件时。稍后我们将更改该代码。

    步骤 6:更改应用程序。

    选择定义对话框的头文件。在我们的例子中是 TempMonitorDlg.h。

    • 添加一个包含 FileMonitor 控件 IWatch 接口指针的成员。
      CComPtr<FILEMONITOR::IWatch> m_FileMonitor;
    • 添加一个接收器接口的 COM 对象成员。
      CComObject<CFileMonitorSink>  *m_FileMonitorSink;
    • 将以下代码添加到 OnInitDialog 方法中。
      m_FileMonitor.CoCreateInstance(__uuidof(FILEMONITOR::Watch), NULL, CLSCTX_INPROC_SERVER);
      CComObject<CFileMonitorSink>::CreateInstance(&m_FileMonitorSink);

    步骤 7:将接收器接口链接到 FileMonitor 控件。

    在接收 FileMonitor 的事件之前,我们必须将接收器接口链接到 FileMonitor 控件。这也被称为 Advising。因为我们在接收器对象中实现了 IDispEventImpl,所以我们可以使用 DispEventAdvise 方法。

    • 在 CFileMonitorSink 类中添加一个成员来保存 FileMonitor 的 IUnknown 接口。
      CComPtr<IUnknown> m_Object;
    • 像下面这样向 IFileMonitorSink 接口添加一个 Start 方法。

    StartMethod.JPG (53493 bytes)

    第一个参数是我们想要通知的对象。第二个参数将通知我们通知是否成功。此方法将用于通知 FileMonitor。

    这是 Start 方法的代码。

    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;
    } 

    关于 AFX_MANAGE_STATE() 宏的说明。Visual C++ 为其生成了错误的 C++ 代码。您必须将 AFX_MANAGE_STATE(AfxGetStaticModuleState()) 更改为上面的代码。这是 Visual C++ 中的一个 bug。每次将方法添加到 MFC 应用程序中的 ATL 对象时都要检查此项。您可以在 Microsoft 的 Q231592 文章中阅读更多关于此的信息。

    • 像下面这样向 IFileMonitorSink 接口添加一个 Stop 方法。

      StopMethod.JPG (42132 bytes)

      此方法将用于取消通知 FileMonitor。
      这是方法的代码。
      STDMETHODIMP CFileMonitorSink::Stop()
      {
          AFX_MANAGE_STATE(AfxGetAppModuleState())
      
          if ( m_Object )
          {
              DispEventUnadvise(m_Object);
          }
          return S_OK;
      }

    步骤 8:开始接收 FileMonitor 事件。

    • 在创建接收器对象后更改 OnInitDialog 方法。
      VARIANT_BOOL succeeded;
      m_FileMonitorSink->Start(m_FileMonitor, &succeeded);
    • 像下面这样处理 WM_DESTROY 消息。
      void CTempMonitorDlg::OnDestroy() 
      {
         CDialog::OnDestroy();
         m_FileMonitorSink->Stop();     
      }

    步骤 9:添加监控临时文件目录的代码。

    • 在调用 Start() 方法后,将以下代码添加到 OnInitDialog() 中。
      // 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;
      }

    步骤 10:编译并运行第一次测试。

    • 当程序运行时,尝试在临时目录中更改、删除或创建文件。在我的系统上是 C:\Windows\Temp。执行此操作时,您应该会看到消息框。

    步骤 11:完成我们的应用程序。

    注意:如果您找到更好的方法,请通知。我创建此示例时使用了大量从互联网和《Professional ATL COM Programming》一书中找到的信息。

    © . All rights reserved.