一.Band对象介绍
Windows的区(Bands)对象有三种:浏览栏(Explorer Bar)区对象,工具栏(Tools Bands)区对象,和桌面区对象(Desk Bands)。
本文主要讲一下桌面区对象的实现。
Desk Band出现在任务栏上,也可以是桌面浮动窗口。通过在任务栏上单击右键,然后在弹出的菜单中选择“工具栏”的子菜单项。如下图所示:
桌面区位于Windows外壳(Shell)中,它是由explorer.exe来调用的。它是一个COM对象,必须存在于某个容器中,这个容器就是任务栏。在这个COM对象(Band)注册时,可以选择不同的对象类型及容器,也就是说,在注册时除了注册它们的CLSID之外,浏览栏和桌面区对象还必须进行组件类别(category)的注册,它决定了对象的类型及其容器。工具需要进行种类注册。归纳起来,需要进行CATID注册的在种区对象是:
二.实现区对象
2.1 实现的接口
下图展示了Desk Band所实现的接口,包括必须实现的可选择实现的接口:
如果某个区对象接受用户输入,它还必须实现IInputObject接口。如果要往上下文菜单中添加菜单项目,还必须实现IContextMenu接口。注意:工具栏区对象不支持上下文菜单。
IDeskBand——这是最重要的接口,它派生于IDockingWIndow,而IDockingWIndow又派生于IOleWIndow。它有一个方法,GetBandInfo,这个方法可以得到窗体的大小,标题,背景色等信息。
IDeskBand2这个接口用于出现玻璃效果,有三个方法:CanRenderComposited,SetCompositionState和GetCompositionState。
IOleWindow有两个方法:GetWindow 和 ContextSensitiveHelp。GetWindow用于得到Band对象窗体的句柄。
IDockingWindow有三个方法:ShowDW,CloseDW和ResizeBorderDW。ShowDW用于显示或隐藏窗体,CloseDW用于关闭窗体,调用DestroyWindow来销毁窗体,ResizeBorderDW一般不用实现。
2.2 注册
区对象必须作为进程内服务器(in-process)注册。其线程模型必须为“Apartment”。也就是说区对象必须以DLL的形式来实现。用来描述服务器注册条目的缺省值是一个菜单文本串。
通常区对象的注册条目如下:
2.3 执行顺序
Windows(桌面band用于资源管理器,浏览栏用于IE)通过查找实现了CATID_DeskBand,,CATID_InfoBand,或 CATID_CommBand的COM对象来发现band对象,并将band的名字添加到工具栏菜单。
1、当用户从菜单选中band时,Windows调用CoCreateInstance或它的同等函数。COM则调用DLL中的 DllGetClassObject输出函数,COM用正确的 ID搜索到一个类工厂并将它返回。然后COM调用IClassFactory::CreateInstance进行一系列的COM常规处理。
关键就是类工厂能正确找到我们自己写的COM,那说明我们要重写IClassFactory::CreateInstance,让它能正确创建出我们写的COM的对象。
2、Windows调用IObjectWithSite::SetSite给出一个(IUnknown*)类型的指针指向对象容器,在这个函数里面我们就要创建BAND对象的窗口了。注意,此时创建的窗口一定要是不可见的,即保证不要使用WS_VISIBLE来创建窗口。
3、接下来,Windows调用IOleWindow::GetWindow来获得窗口的HWND。(这就是为什么你必须在SetSite中创建窗口的原因)。
4、接着,Windows调用IDeskBand::GetBandInfo请求关于band的信息,如大小,可变高度或者定高,以及背景颜色和标题。
5、接着,Windows调用IDockingWindow::ShowDW来显示窗体。
6、当用户关闭Band时,Windows调用IDockingWindow::CloseDW。
通常,IObjectWithSite::SetSite实现应该完成下列步骤:
1. 释放当前所把持的任何现场指针。
2. 如果传递到SetSite的指针被置为NULL,此则区对象被删除。SetSite可以返回S_OK。
3. 如果传递到SetSite的指针被置为非NULL,则建立新的现场。SetSite应该做以下的事情:
a)调用现场QueryInterface方法请求IOleWindow接口。
b)调用IOleWindow::GetWindow获取父窗口句柄,并存储它,以便以后使用。如果不再使用的话,就释放IOleWindow接口。
c)创建此band对象的窗口为一个子窗口,其父窗口就是上一步获得的那个窗口。注意在此不能将它创建成可见窗口。
d)如果此band对象实现IInputObject,调用现场QueryInterface方法请求IInputObjectSite接口,存储这个接口的指针以备后用。
e)如果所有步骤都成功,则返回S_OK,否则返回OLE定义的错误代码以指示错误类型。