如何在 MFC 中使用 ATL 控件





3.00/5 (10投票s)
2000年12月12日

251385

2166
一个一步步的教程,解释如何在 MFC 中使用 ATL 控件
引言
本文介绍如何将 ATL 控件与 MFC 一起使用。我收到了很多关于如何使用 FileMonitor 控件与 MFC 的电子邮件。我使用的例子是一个简单的基于 MFC 对话框的程序,当临时文件目录中有文件被创建、修改或删除时,它会通知用户。
步骤 1:创建一个 MFC 对话框程序。
- 创建一个新项目。在 AppWizard 中选择 MFC(exe) 程序。接受所有默认设置。无需选择自动化。
步骤 2:添加一个接收器对象。
要接收来自 ATL 控件的事件,您必须创建一个 ATL 对象来链接该控件。这个对象称为接收器对象。
- 选择 插入 / 插入新的 ATL 对象。
- 在接下来的问题中点击“是”
- 选择单个对象
- 将对象的短名称设置为 FileMonitorSink。不要更改其他字段。
在 Attributes 选项卡中接受默认设置。
步骤 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 方法。
第一个参数是我们想要通知的对象。第二个参数将通知我们通知是否成功。此方法将用于通知 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 方法。
此方法将用于取消通知 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:完成我们的应用程序。
- 您可以在此处下载完整的应用程序:TempMonitor.zip
注意:如果您找到更好的方法,请通知我。我创建此示例时使用了大量从互联网和《Professional ATL COM Programming》一书中找到的信息。