`
ldb19890624
  • 浏览: 229809 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

TabBars代码解读之——Tabbar栏与其他窗口之间的关系

 
阅读更多
<!-- Search Google -->
Google 输入您的搜索字词 提交搜索表单
<!-- Search Google -->

集成开发环境中各个窗口的行为都是事先实现的,比如,文档编辑窗口对应着多文档类的视图窗口,负责对用户的输入进行响应,更新窗口的内容,主窗口负责调度各个视图窗口。Visual Studio的集成开发环境(IDE)没有给我们提供任何接口实现在IDE中添加一个窗口,并且这个窗口能够和IDE中其他窗口分享消息的功能,也就是说正常情况下插件无法干涉IDE环境中窗口的正常行为。但是,这并不意味着我们只能老老实实的作些本分工作,因为通过窗口的子类化技术(Subclassing),我们一样可以Hook窗口的消息。Visual Studio的插件是工作在IDE的进程空间的,这就给我们提供了Hook IDE内任何窗口的机会。从理论上讲,我们可以使用自己的窗口消息处理过程代替IDE中各个窗口的标准窗口消息处理过程,完成对特定消息的响应,甚至完全改变某个窗口的行为。著名的VC商业插件软件Visual Assist就是替换了文档编辑的视图窗口,从而使其比默认的文档编辑窗口提供更多的便利功能。
Visual Studio的IDE环境有很多窗口,我们关心的只有三个(类)窗口,一个是主框架窗口,一个是MDI子框架窗口,一个是文档窗口。TabBars的标签栏其实并不依赖主框架窗口的消息约束自己的行为,子类化主框架窗口主要是为了处理WM_GETTEXT消息。当用户在不同的文档窗口之间切换时,主框架窗口就会相应的改变窗口标题栏上显示的文字,Hook WM_GETTEXT消息可以使Tabbars插件自己创建的窗口能够在第一时间获得这个消息并做出相应的处理。子类化MDI子框架窗口有两个主要目的,一个是截获窗口大小和位置变更的消息以便能够适时的调整标签栏的大小和位置;另一个是向文档窗口报告MDI子框架窗口新的客户区位置,新客户区排除了标签栏窗口占用的位置,如果不处理这个消息,框架窗口会将文档窗口会按照默认的位置摆放,这会遮挡标签栏窗口。子类化文档窗口是为了处理WM_DESTROY消息,以便能够在用户关闭文档窗口时有机会更新标签栏上的按钮。下图是TabBars插件的窗口类关系图:


图1. Tabbars窗口类关系图

关系图中的核心是CTabManagerWindow类,这个类虽然名为TabManager,但是并不直接管理标签栏,它通过CTabBarsWnd管理标签栏。CTabBarsWnd是一个MFC的CWnd派生类,他的作用有两个,一个是周期的产生定时器事件,驱动TabBars插件完成文件自动保存之类的工作;另一个作用就是作为容器窗口承载一个Table控件。

子类化窗口的关键是找到窗口的句柄,Visual Studio的内置插件接口没有提供获取这些窗口句柄的方法,TabBars使用了很多小的Trick来达到这个目的,首先是hook主框架窗口,先看下面的代码:

pApp->put_Active(VARIANT_FALSE);
pApp->put_Active(VARIANT_TRUE);
hWnd = ::GetActiveWindow();
while (hWnd && hWnd != hDesktopWnd)
{
hDevStudioWnd = hWnd;
hWnd = ::GetParent(hWnd);
}
g_pDevStudioWnd = new CDevStudioWnd(hDevStudioWnd); //CWnd::FromHandle(hDevStudioWnd);

put_Active是操作IApplication接口的Active属性,确保Visual Studio的IDE窗口是当前活动窗口,紧跟着调用GetActiveWindow获得这个窗口。不过,GetActiveWindow通常得到的是IDE下的活动子窗口,所以通过向上查找父窗口最终可以得到真正的主框架窗口。

得到主框架窗口之后就可以顺藤摸瓜得到MDI子框架窗口,MDI子框架窗口使用了固定的类名:MDIClient,所以遍历主框架窗口的所有子窗口就可以找到MDI客户区窗口:

char cClassName[256];

hMDIWnd = g_pDevStudioWnd->GetTopWindow()->m_hWnd;
::GetClassName(hMDIWnd, (LPTSTR)cClassName, sizeof(cClassName));
while (strcmp(cClassName, "MDIClient") != 0)
{
hMDIWnd = ::GetNextWindow(hMDIWnd, GW_HWNDNEXT);
ASSERT(hMDIWnd);
GetClassName(hMDIWnd, (LPTSTR)cClassName, sizeof(cClassName));
}


子类化这些关键窗口,使得TabBars插件自己创建的窗口可以hook适当的消息,通过响应这些消息,TabBars插件可以调整标签栏的位置,更新标签栏并通知其他窗口MDI客户区的改变,从而使TabBars窗口能够融入到Visual Studio的集成开发环境(IDE)中。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics