上一次说到处理
WM_CHAR
消息,当用户每键入一个字符时,万能连接框就会去进行一次查找的过程,然后把智能提示信息显示出来。说到
AutocompleteEdit::HandleKeystroke
函数的操作,那么它为什么需要冻结这个函数的使用呢?现在就来分析这部份的内容。如下:
ScopedFreeze freeze(this, GetTextObjectModel());
在这行代码里,首先会调用函数
GetTextObjectModel()
来获取一个文档
ITextDocument
接口,然后再使用它的功能。这个函数的代码如下:
#001
ITextDocument* AutocompleteEdit::GetTextObjectModel() const {
先判断这个接口是否获取到,如果已经获取到就不再去重复获取了。
#002
if (!text_object_model_) {
#003
// This is lazily initialized, instead of being initialized in the
#004
// constructor, in order to avoid hurting startup performance.
这里使用了智能指针来获取
IRichEditOle
接口。
#005
CComPtr<IRichEditOle> ole_interface;
获取到的
IRichEditOle
接口绑定到智能指针里。
#006
ole_interface.Attach(GetOleInterface());
下面通过
=
操作符获取
ITextDocument
接口,如果你深入去分析这个赋值操作符,会看到它自动去调用
IRichEditOle
的接口
IUnknown::QueryInterface
来查询到
ITextDocument
接口,这个过程对于程序员来说是完全不用关心的,这就是使用
mutable CComQIPtr<ITextDocument> text_object_model_
定义的作用。
#007
text_object_model_ = ole_interface;
#008
}
#009
return text_object_model_;
#010
}
通过上面的分析,可见使用
CComQIPtr<ITextDocument>
智能指针可以省了很多
COM
调用的操作,这真是模板类的强大功能的使用之处。当把
ITextDocument
接口获取回来之后,对于
RichEdit
操作就可以轻松访问了,
ScopedFreeze
类生成一个局部对象,这个对象实现了对
RichEdit
自动冻结和解冻结的功能,这个过程是通过局部对象在栈里生命周期的特性应用。如下面的代码:
#001
AutocompleteEdit::ScopedFreeze::ScopedFreeze(AutocompleteEdit* edit,
#002
ITextDocument* text_object_model)
#003
: edit_(edit),
#004
text_object_model_(text_object_model) {
#005
// Freeze the screen.
#006
if (text_object_model_) {
#007
long count;
#008
text_object_model_->Freeze(&count);
#009
}
#010
}
#011
#012
AutocompleteEdit::ScopedFreeze::~ScopedFreeze() {
#013
// Unfreeze the screen.
#014
// NOTE: If this destructor is reached while the edit is being destroyed (for
#015
// example, because we double-clicked the edit of a popup and caused it to
#016
// transform to an unconstrained window), it will no longer have an HWND, and
#017
// text_object_model_ may point to a destroyed object, so do nothing here.
#018
if (edit_->IsWindow() && text_object_model_) {
#019
long count;
#020
text_object_model_->Unfreeze(&count);
#021
if (count == 0) {
这里需要手动地更新窗口的显示。
#022
// We need to UpdateWindow() here instead of InvalidateRect() because, as
#023
// far as I can tell, the edit likes to synchronously erase its background
#024
// when unfreezing, thus requiring us to synchronously redraw if we don't
#025
// want flicker.
#026
edit_->UpdateWindow();
#027
}
#028
}
#029
}
从上面的代码可以看到构造函数里冻结,析构造函数里解冻结,如果需要就会自动更新窗口的显示。
通过上面的分析,学会使用
RichEdit
的冻结窗口的输入,并且解冻结和更新窗口的显示,也同时学会使用智能指针来操作
COM
接口的方便性,最后还学会了使用栈对象的生命周期来方便对加锁和解锁的操作,以便降低代码的出错率。