Raymond Chen 2008年01月29日
Windows XP公共控件的历史
最初,存在一个名为USER
的控件库,它由窗口管理器提供,包括按钮、静态控件、编辑控件、滚动条、列表框和组合框等。这些控件由窗口管理器团队负责。
在Windows 3.1中,增加了第二个公共控件库,称为外壳公共控件,但直到Windows 95时期,它才真正成熟,包括了列表视图、标题栏、树视图、工具提示、工具栏、状态栏、滑动条、标签页、上下调节控件、进度条、热键和动画控件等。这些控件最初是外壳团队为资源管理器Explorer编写的自定义控件,但因为它们看起来很有普遍用途,所以安排了额外的时间来使这些控件更适合一般使用,测试这些控件在Explorer未使用的组合和场景中的表现,并编制正式文档。
外壳公共控件库多年来经历了许多变化,而窗口管理器中的核心内置控件变化则更为保守。
随着Windows XP的到来,视觉设计团队希望为Windows的外观注入新的活力。更改非客户区域(例如窗口框架)相对简单,因为程序对这部分窗口没有太多控制权。因此,它们自动获得了Windows XP的外观。
客户端区域则是另一回事。程序控制着自己的客户端区域,在这里它们可以放置Windows自带的控件、自己的自定义控件或从第三方库获取的控件。对核心控件或公共控件进行重大更改将是一项高风险的尝试,因为有成千上万的Windows程序不仅使用它们,而且以各种方式使用它们。
解决这两个相互冲突的目标(一方面对这些控件进行重大更改以提高视觉吸引力和功能,另一方面不更改它们以保持兼容性)的最初尝试是创建一个新的DLL,其中包含核心控件和外壳公共控件的“花哨新版本”。旧的DLL USER32
和 COMCTL32
保持原样,以便旧程序继续获得它们预期的行为,而新的XP风格控件被放置在一个名为 UXCTRL.DLL
的DLL中。UX代表“用户体验”,这是当时的热门新词汇。
为了避免与旧风格控件的名称冲突,新控件获得了以Ux开头的新名称。例如,按钮控件的 UXCTRL
版本被称为UxButton。
这些新的Ux控件可以毫无顾忌地添加新功能,而无需考虑向后兼容性,因为它们是全新的控件,无需与任何旧版本兼容。资源管理器Explorer被更改为使用这些新控件而不是旧的控件,一切似乎都运作良好。
或者看起来是这样。
我们认为通过创建全新的控件巧妙地避开了向后兼容性问题,但这样做却创造了一个全新的兼容性错误类别。即使这是完全未记录和不支持的,程序也喜欢深入其他程序的内部数据结构,或以其他方式操作这些程序的窗口。在Explorer的情况下,许多程序喜欢在Explorer的窗口层次结构中搜寻,并使用诸如 FindWindow
和 EnumChildWindows
等函数来找到它们感兴趣的对象。
例如,一个程序可能使用 EnumChildWindows
枚举Explorer浏览器的所有子窗口,然后使用 GetClassName
和 lstrcmpi(szClassName, TEXT("button"))
来寻找特定的控件。在这个例子中,目标是一个按钮,但也可能是列表视图或工具栏。由于所有新的XP风格控件都被命名为UxButton和UxListView等,这些通过比较字符串“button”来查找按钮的程序停止了工作。
当然,没有保证Explorer会使用按钮;Explorer完全有权彻底改造其用户界面。但对于那些花费了大量金钱购买这些程序的客户来说,这并没有太大的安慰,特别是因为杂志专栏作家是最有可能运行(甚至编写!)这些奇怪的、非主流的程序的人。
好的,现在所有新窗口类的名称必须与其旧对应物的名称相同,这是一个兼容性要求。这造成了僵局,因为这些控件需要由对话框创建,因此它们必须是全局注册的窗口类。但你不能有两个具有相同名称的全局窗口类,因为那将造成对调用者所请求的是哪个的歧义。
更多的头脑风暴接踵而至,C计划出现了。公共控件库将利用并行程序集,并使用应用程序清单来控制给定窗口类名称将解析到哪个DLL。因此诞生了一个新的DLL,也被称为 COMCTL32
,但具有新的版本号—版本6。旧程序将像在Windows 2000中一样获得5.82版本。新程序必须使用清单来指定他们想要版本6。
再次,解决方案带来了一个新问题。由于整个 COMCTL32
库被拆分为两个版本,这意味着有两个版本的图像列表代码。全新的场景出现了,例如在版本6的树视图中放置一个版本5的图像列表,或反之亦然。(正如链接的线程笔记所示,并非所有跨版本场景的问题在最初发布时都被捕捉到,不得不等待服务包才能获得修复。)