65.9K
CodeProject 正在变化。 阅读更多。
Home

Windows XP 主题 API 的托管 C++ 包装器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (29投票s)

2003年8月16日

CPOL

5分钟阅读

viewsIcon

317540

downloadIcon

5474

安全地从 C# 在任何操作系统上使用 XP 主题 API。

Sample image

引言

Managed UxTheme 程序集是对 Windows XP 主题 API 的 .NET 封装。它可以在 C# 中安全使用,适用于支持 .NET 框架的任何 Windows 平台。除了暴露 UxTheme API 外,它还暴露了 TmSchema.h (Platform SDK 的一部分) 中的静态数据,这些数据用于定义哪些窗口类可以被主题化、这些类的哪些部分以及每个部分可以具有自定义外观的状态。

背景

Windows XP 使用一个 C 风格的 DLL 来暴露其主题功能,名为 UxTheme.dll ("Ux" 代表 User eXperience)。在我日常工作中,我正在用 C# 编写一些 .NET 自定义控件。当然,这些控件的一个要求是它们在适当的时候使用 Windows XP 主题,并反映用户当前的 the me 设置。

UxTheme.dll 的问题在于它只在 Windows XP 上可用,所以任何尝试直接使用它的 P/Invoke 代码在其他版本的 Windows 上都会失败。 Pierre Arnaud 编写了一个 C++ 封装 DLL,可以通过 P/Invoke 在任何支持 .NET 的 Windows 版本上安全调用。他的实现使用了 David Y. Zhao 的那个小巧的 C++ 封装类,该类动态链接到 UxTheme.dll,同时如果无法加载 UxTheme 库,还提供每个方法的安全故障转移实现。

我喜欢 Pierre 的解决方案,并开始使用它。然而,它并没有提供我需要的 UxTheme.dll 的功能级别。我的下一个想法是扩展他的工作,并继续从 C# 使用 P/Invoke。但我一直在寻找一个机会去做一些超越 "Hello World!" 的事情,而 Managed C++ 似乎是完美的时机。

此实现也使用了 David 的封装类 (CodeProject 上几乎所有 XP 主题相关的代码示例都是如此。这确实是一项很棒的工作!)。

实现

从外部视角来看,这个程序集有两个独立的部分。第一部分是 UxTheme 类。这个类提供了一个托管的 HTHEME 句柄封装。它暴露了 UxTheme DLL 中所有接受 HTHEME 作为实例方法的函数。不接受主题句柄的方法被暴露为静态方法。使用 DLL API 可以做到的任何事情,都可以通过这个类来实现。

与此并行的是一个对象层次结构,它封装了 TmSchema.hSchemadefs.h 创建的属性表数据。这两个文件是 Platform SDK 的一部分,它们定义了 Windows UI 的哪些部分可以被主题化的数据模型。

这个对象层次结构以 ThemeInfo 类开始。这个类包含一些关于当前主题的简单元数据,但也包含一个 WindowThemes 集合。WindowTheme 类包含特定窗口类的部件信息。WindowTheme 类有一个 ThemePartsThemePartStates 集合。

ThemePart 代表窗口类的一个独立部分。例如,向下按钮是 ScrollBar 窗口类的一部分。

每个 WindowThemeThemePart 可以有 0 到 n 个状态。ThemePartStates 是像 normaldisabledhot 这样的东西。WindowTheme 类有一个 UxTheme 属性,可以用来获取特定于该窗口类的 UxTheme 类的实例。ThemePartThemePartStates 也包含一些实例方法,以便可以直接渲染到图形上下文中。

UxTheme 类可以在没有 ThemeInfo 层次结构的情况下使用,但该层次结构为整个事情提供了一个更面向对象的界面。

使用代码

使用代码非常简单。所有类都在 System.Windows.Forms.Themes 命名空间下。唯一可以公开创建的类是 ThemeInfo。要深入了解窗口类、其部件和状态,请创建一个 ThemeInfo 实例,然后开始查看其 WindowThemes 集合。

你可以通过 WindowTheme 实例获取 UxTheme 的实例,或者通过调用静态方法 UxTheme::OpenThemeUxTheme::GetWindowTheme 来获取。

在尝试使用 UxTheme 的其他方法之前,请确保在代码中查看 UxTheme::IsAppThemed。这将告诉你当前操作系统是否支持主题,以及当前是否启用了主题。而且别忘了,这在你应用程序的生命周期中可能会发生变化,因为用户可以随时关闭主题。

该程序集有一个强名称,所以你可以把它放到 GAC 中,如果你愿意的话。

演示项目包含了一个用 C# 编写并使用托管 API 的 David 的 Theme Explorer 应用程序 的重新实现。它还包括对 Pierre 提供的 TabPage 兼容控件 的轻微重构。它们仅仅作为演示,展示了该程序集如何从自定义控件实现中使用。

关注点

Managed C++ DLL 在入口点方面有一些有趣的限制。由于此 DLL 使用 C-Runtime,因此它是一个 混合模式 DLL。它使用 /NOENTRY 链接器标志进行编译。使用此标志链接的程序集没有显式的入口点,这会阻止任何静态数据的初始化,除了简单的、整型类型。如果能够将 CVisualStylesXp 类用作静态实例将会很好,但由于运行时不会初始化它,所以这是不可能的。

每个 UxTheme 实例都会创建一个指向 CVisualStylesXp 类实例的成员指针。UxThemes 的静态方法会在本地创建一个该类的实例。结果是大量对 LoadLibraryFreeLibrary 的调用。幸运的是,Windows 对动态加载的 DLL 保持引用计数,所以这不会造成显著的性能损失。

尽管如此,这是我的实现中让我感到困扰的一个地方。如果有人知道如何只加载和释放 UxTheme DLL 一次,并避免创建如此多的 C++ 封装类实例的好方法,我非常乐意听到。MSDN 建议实现显式的 InitializeDeinitialize 方法,但我不想强迫这个程序集的用户这样做。

历史

  • 版本 1 - 初始发布
  • 08/26/2003
    • Bug 修复
    • API 的一些重组
© . All rights reserved.