我一直盼望着 Windows 新版本的发布。令人感兴趣的事情莫过于浏览 MSDNclass="superscript">® 和 SDK 文档,查找一些可以利用和依赖的最新创新,然后让朋友和同事以及您的老板(如果幸运的话)大开眼界。Windows Vista™ 在这方面包含许多诱人的内容。自从听说该版本将三维/组合层集成到桌面以来,我就特别希望获得该版本。多年来我已编写了数不清的三维应用程序,但我发现令人烦恼的一件事是,虽然能够在三维应用程序中提供非常酷且令人赏心悦目的用户界面,但在非三维应用程序中却不能。有了 Windows Vista 和桌面窗口管理器 (DWM),这种情况就发生了改变(请参见图 1)。
组合桌面的好处 所有这些新的子系统可让您独立呈现窗口,并在将它们呈现到桌面之前对其执行组合步骤。在 Windows Vista 和随之发布的一些更新应用程序中可以很好地利用这一点。关于这一点,我要向您展示的两个用法是 Aero™ 玻璃效果和缩略图。玻璃效果仅在运行 Aero Glass 方案并打开组合功能时才可用。Aero Basic 不提供此效果。 由于每个窗口都在自己的视频内存区中创建,因此需要由 DWM 将该窗口的最后组合呈现到桌面。这意味着,DWM 可以访问桌面上的图像,并可以将其与您的窗口呈现混合在一起,创建由二者组合的呈现。在与现有桌面图像混合以创建霜冻玻璃效果的任何窗口区域中,这一点非常明显。由于每个窗口都呈现到中间屏外表面,这意味着 DWM 是涉及更新玻璃效果的唯一程序。在移动使用玻璃效果的窗口时,不需要使任何基本窗口无效。DWM 会处理半透明可视图像到新坐标的更新。可以指示 DWM 为要呈现玻璃效果的窗口添加某些客户端区域,这样可让您创建供自己使用的玻璃区域。 此屏外组合使得桌面响应更快。由于每个窗口现在都从桌面中单独呈现,因此就消除了您在更新较慢的应用程序(如 Web 浏览器)中经常遇到的问题。在 Windows 的以前版本中,图 2 显示的内容并不陌生。在另一个应用程序上方来回移动窗口时会出现这种撕裂现象,这是因为下面的窗口更新的速度太慢。进行桌面组合之后,就不会再看到这种现象。
一些常见的组合函数 如果您希望在程序中使用桌面组合功能,则需要查询和设置各种 DWM 参数。例如,如果某些应用程序切换为全屏显示,然后 DWM 关闭组合,并以不透明的桌面背景颜色呈现,那么您的应用程序应识别这种情况并禁用特定于组合的功能。下面是将您的程序与 DWM 集成的一些基本函数: DwmEnableComposition 启用或禁用 DWM 组合。DWM 将在当前进程中或直到重设前保持此设置。更改设置会导致发出 WM_DWMCOMPOSITIONCHANGED 通知。多数应用程序不需要调用此函数,但您可能需要监视 Windows 的最终消息。 DwmIsCompositionEnabled 获取桌面启用 DWM 组合的状态。 DwmSetWindowAttribute 为窗口设置指定的 DWM 属性的值,控制如何处理 DWM 过渡,是否允许非客户端呈现,以及 Flip 3D 将如何处理窗口。例如,如果对某个窗口关闭非客户端呈现,则稍后扩展框架或使窗口后面的内容变模糊的要求将会失败。 DwmGetWindowAttribute 为指定窗口检索指定 DWMWINDOWATTRIBUTE 的当前值。 DwmGetColorizationColor 检索用于 DWM 玻璃组合的当前颜色。此值基于当前颜色方案。更改此设置将导致发出 WM_WMCOLORIZATIONCOLORCHANGED 通知。 DwmDefWindowProc 在使用 WM_NCHITTEST 通知进行调用时以及由于在扩展了客户端框架而需要处理 WM_NCCALCSIZE 及类似消息时,使 DWM 命中测试位于非客户端区域。 在您的程序中呈现玻璃效果相当简单。DWM 为此提供了两个函数: DwmExtendFrameIntoClientArea 一个简单的函数,将非客户端框架的边缘扩展到窗口内。 DwmEnableBlurBehindWindow 一种更为复杂的函数,对玻璃效果的呈现方式提供更多的控制。 由于所有组合窗口都通过 DWM 呈现到一个屏外窗口,然后进行组合后再呈现到桌面上,因此获取这些图像并提供应用程序的实时缩略图表示并不困难。DWM 为您提供了四个函数来控制缩略图的呈现方式: DwmQueryThumbnailSourceSize 返回 DWM 缩略图的原始大小。 DwmRegisterThumbnail 创建目标窗口和源窗口之间的缩略图关系。 DwmUnregisterThumbnail 删除由 DwmRegisterThumbnail 创建的 DWM 缩略图关系。 DwmUpdateThumbnailProperties 更新给定缩略图的属性。 DWM 提供了五个函数用于微调 DWM 的呈现方式,但这些函数超出了本文的讨论范围。 为 Aero Glass 做好准备 若要对 DWM 界面编程,您需要运行能够显示 Aero Glass 的 Windows Vista 版本。虽然最方便的方法是使用 C++ 代码调用这些新函数,但如果可以的话,我喜欢用 C# 编写用户界面代码。本文的所有代码均使用 C# 编写,但这意味着您必须经受严峻的考验。若要使用本文讨论的函数,您要么需要使用 C++ 和正确的库中的链接,要么必须用 C# 为函数和结构编写 P/Invoke 包装。在本文的下载部分中包含一个库,其中提供了 DWM 所需函数和结构的包装,因此您可以从 C# 程序调用它。基本上,这只是从 dwmapi.dll 下载界面的一组说明。为使用本文中使用的用于玻璃效果和缩略图的 DWM 函数,您需要创建 DWM 函数和数据结构的 C# 声明。我为本文创建的声明类似于图 5。
Figure 5 DWM 的 C# 声明
internal class DwmApi { [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmEnableBlurBehindWindow( IntPtr hWnd, DWM_BLURBEHIND pBlurBehind); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmExtendFrameIntoClientArea( IntPtr hWnd, MARGINS pMargins); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern bool DwmIsCompositionEnabled(); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmEnableComposition(bool bEnable); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmGetColorizationColor( out int pcrColorization, [MarshalAs(UnmanagedType.Bool)]out bool pfOpaqueBlend); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern IntPtr DwmRegisterThumbnail( IntPtr dest, IntPtr source); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmUnregisterThumbnail(IntPtr hThumbnail); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmUpdateThumbnailProperties( IntPtr hThumbnail, DWM_THUMBNAIL_PROPERTIES props); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern void DwmQueryThumbnailSourceSize( IntPtr hThumbnail, out Size size); [StructLayout(LayoutKind.Sequential)] public class DWM_THUMBNAIL_PROPERTIES { public uint dwFlags; public RECT rcDestination; public RECT rcSource; public byte opacity; [MarshalAs(UnmanagedType.Bool)] public bool fVisible; [MarshalAs(UnmanagedType.Bool)] public bool fSourceClientAreaOnly; public const uint DWM_TNP_RECTDESTINATION = 0x00000001; public const uint DWM_TNP_RECTSOURCE = 0x00000002; public const uint DWM_TNP_OPACITY = 0x00000004; public const uint DWM_TNP_VISIBLE = 0x00000008; public const uint DWM_TNP_SOURCECLIENTAREAONLY = 0x00000010; } [StructLayout(LayoutKind.Sequential)] public class MARGINS { public int cxLeftWidth, cxRightWidth, cyTopHeight, cyBottomHeight; public MARGINS(int left, int top, int right, int bottom) { cxLeftWidth = left; cyTopHeight = top; cxRightWidth = right; cyBottomHeight = bottom; } } [StructLayout(LayoutKind.Sequential)] public class DWM_BLURBEHIND { public uint dwFlags; [MarshalAs(UnmanagedType.Bool)] public bool fEnable; public IntPtr hRegionBlur; [MarshalAs(UnmanagedType.Bool)] public bool fTransitionOnMaximized; public const uint DWM_BB_ENABLE = 0x00000001; public const uint DWM_BB_BLURREGION = 0x00000002; public const uint DWM_BB_TRANSITIONONMAXIMIZED = 0x00000004; } [StructLayout(LayoutKind.Sequential)] public struct RECT { public int left, top, right, bottom; public RECT(int left, int top, int right, int bottom) { this.left = left; this.top = top; this.right = right; this.bottom = bottom; } } }如果您使用的是 C#,则需要在您的代码中创建与此类似的声明。然后,如果您运行的是 Windows Vista,则可以进行 DWM 调用。当然,不应仅假定您的应用程序运行于 Windows Vista 上。为保险起见,您需要确认 Environment.OSVersion.Version.Major 至少为 6.0。或者,您可以捕获由于尝试通过 P/Invoke 调用不存在的函数而导致的异常。 如果希望使用玻璃效果,则您使用的计算机需要满足三个要求。首先,需要运行 Windows Vista 的 Premium、Business 或 Ultimate 版本。其次,硬件应能够运行 Aero 界面(详细信息请参见microsoft.com/windowsvista/getready/capable.mspx)。最后,需要在 Windows Vista 中选择 Windows Aero 颜色方案。需要谨慎使用此操作,因为用户过度使用该效果会对计算机的 GPU 造成负担。
对 DWM 编程 通过调用 DwmIsCompositionEnabled 可以检查在您的程序中是否启用了 Aero 方案。但是请注意,不但用户可以随时更改当前 Aero 方案,而且其他应用程序也能够以编程方式启用或禁用该方案。因此检查一次此函数的结果可能不够可靠。 DwmEnableComposition 函数可让程序打开或关闭 Aero 方案。例如,如果您编写的应用程序可能遇到兼容性问题,则在应用程序运行时可能需要禁用组合(如果您编写的是全屏 DirectX 专用应用程序,将会自动禁用组合)。此设置仅在设置它的进程期间保持,当进程结束时,组合标志将被重设为其初始值。通常情况下,除非由于应用程序兼容性原因,否则应用程序不应使用此设置,而应让系统或用户作出决定。 当桌面组合的状态更改时,将广播 WM_DWMCOMPOSITIONCHANGED 消息。您无法通过参数了解是否启用或禁用了组合,因此,如果感兴趣,由您自己决定调用 DwmIsCompositionEnabled。执行检查的代码非常简单,难点是当禁用组合时,如何决定窗口的外观。
// Check to see if composition is Enabled if (DwmIsCompositionEnabled()) { // enable glass rendering } else { // fallback rendering }最后,即使启用了 Aero 方案,用户可能已经更改了玻璃颜色并使组合效果变得不透明。我编写了一个小应用程序,使用该程序可以创建完全是玻璃效果的窗口,然后我在控制面板中更改了玻璃属性(请参见图 8)。第一个图像显示窗口颜色和透明度的默认设置。然后我关闭了透明度,留下一个不透明窗口。我更改为红色窗口颜色和默认的透明度,您仍可从中辨认出某些底层窗口图像。在一个足够大的不透明窗口中,您将观察到放置在玻璃呈现中的高亮区域,它就是窗口上可见的斜纹“极光”效果。
protected override void WndProc(ref Message msg) { switch (msg.Msg) { case WM_DWMCOLORIZATIONCOLORCHANGED: // The color format of currColor is 0xAARRGGBB. uint currColor = (uint)msg.WParam.ToInt32(); bool opacityblend = (msg.LParam.ToInt32() != 0); ... break; } }在对非客户端区域更改 DWM 呈现时,将发送 WM_DWMNCRENDERINGCHANGED 消息。如果启用了 DWM 的非客户端呈现,wParam 将为 true。当最大化或非最大化 DWM 组合窗口时收到 WM_DWMWINDOWMAXIMIZEDCHANGE 消息,您还将得到通知。如果已最大化窗口,wParam 将为 true。 前面我们介绍了两个函数,您可以使用这两个函数为您的程序提供玻璃效果。第一个是 DwmExtendFrameIntoClientArea。使用 Aero 方案的窗口在标题栏区域和窗口边缘的边界处具有玻璃效果(实质上是窗口的所有非客户端区域)。此函数可让您扩展呈现到客户端区域的非客户端区域的每个边,并使用玻璃效果呈现它。换句话说,您可以将玻璃窗口框架的顶边、左边、右边和底边无缝扩展到您的窗口中。 第二个函数是 DwmEnableBlurBehindWindow,它可让您呈现边缘为任意形状的玻璃效果,并指定更多参数来更好地控制呈现效果,但是,我认为多数使用玻璃效果的用户只是将玻璃效果从边缘扩展到客户端区域。无论使用哪个函数,都需要密切跟踪组合状态,以查看是否应在呈现时启用玻璃效果。这意味着要跟踪四个 WM_DWM* 消息,或者调用 DwmIsCompositionEnabled,以便了解是关闭玻璃效果进行呈现,还是在打开时呈现。 首先来看一下较简单的调用。此函数专用于无框架窗口(如任务栏、侧栏、Tablet 笔输入窗口和“开始”菜单);未定义有框架窗口上的行为。 DwmExtendFrameIntoClientArea 函数采用窗口句柄和 MARGINS 结构。窗口句柄是框架从边缘扩展到客户端区域的窗口。您需要设置包含像素数的 MARGINS 结构,以便将框架扩展到客户端区域。MARGINS 结构的 C# 实现如图 5 所示。 刚开始您可能会感到迷惑,因为没有其他 Win32 函数与此作用类似,但是基本上您可以从其他函数独立地控制每个边。选择您要扩展的边,并指定要呈现的效果在客户端区域的深入程度(请参见图 9)。如果需要扩展多个边,它们可以重叠。如果希望该效果动态跟踪窗口大小,则每次在窗口大小更改时都需要调用 DwmExtendFrameIntoClientArea 函数。一种特殊情况是将一个或多个边距设置为 -1,这样会将玻璃效果扩展到整个窗口。若要重设边距,只需将所有边距值设置为 0,并再次调用 DwmExtendFrameIntoClientArea。
在玻璃区域上绘制 在窗口中使用玻璃效果作为背景需要一些技巧。如果呈现本身不透明的任何内容(如 GDI 函数),则会将您的项目呈现在玻璃区域上,但有时会出现异常效果。如果您希望实际将呈现混合到玻璃界面中,则需要利用能够使用 Alpha 颜色管道的功能,如 GDI+、Windows Presentation Foundation 或 Windows XP Theme API。 一个特殊问题是使用位模式 0x00000000 以黑色呈现 GDI 项目,在使用 Alpha 管道时也会碰巧出现完全透明的黑色。这意味着如果您使用黑色 GDI 画笔或笔进行绘制,将会得到透明的颜色,而不是黑色。当您尝试使用默认文本颜色控制位于玻璃区域中的文本标签时,这种问题表现得就特别明显。因为默认文本颜色通常为黑色,DWM 会认为它是透明的,因此文本将错误地写入玻璃区域。图 10 显示了一个这样的示例。第一行使用 GDI+ 编写,第二行是一个使用默认颜色的文本标签控件。可以看出,其中的内容几乎无法辨认,因为它实际上是错误呈现的文件,文本显示为灰色,而不是黑色。
static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(true); Application.Run(new GlassForm()); }您可以在 Miguel A. Lacouture 在 2006 年 3 月份的《MSDN 杂志》上发表的文章“Build World-Ready Apps Using Complex Scripts In Windows Forms Controls”中找到有关此内容和使用 TextRenderer 类的详细信息。 在开始呈现窗口之前应启用玻璃效果。组合引擎将查看您窗口的 Alpha 值,并将模糊效果应用到透明区域。这在使用某些 GDI 函数时可能会出现问题,因为这些函数不保留 Alpha 值。您可以在需要时使用 GDI+,但应谨慎使用,因为 GDI+ 调用在软件中呈现,而不是在硬件上呈现,因此窗口刷新频率较高时使用 GDI+ 调用可能导致耗费大量的系统资源。 可以通过相同方法在 DirectX 应用程序中获得玻璃效果。您需要做的只是控制呈现目标的 Alpha 值和使用两个启用玻璃效果的 DWM 函数之一。无论在何处指示 DWM 使用玻璃效果,它都将使用呈现目标的 Alpha 值。无论在其他任何地方,呈现目标均应为不透明的,否则将得到未定义的行为。
缩略图 缩略图是打开的应用程序上由 DWM 呈现的实时显示窗口。缩略图由 Flip 和 Alt+Tab 任务切换器使用。实质上,您可以请求应用程序窗口的缩略图,并让其在应用程序中呈现。缩略图 API 将为您提供应用程序窗口的实时表示。 缩略图易于使用,因为 Windows 已为您做好了大部分艰苦工作。困难之处在于获取应用程序的 HWND。在获取所需的 HWND 后,您只需注册一个缩略图,并将该 HWND 与要呈现缩略图的 HWND 和该窗口中的位置相关联。这时操作系统将负责以后的更新。每当源窗口更改时,更改将反映到目标窗口中。 若要使用缩略图,必须首先使用 DwmRegisterThumbnail 函数注册缩略图。您提供了两个窗口句柄:源 HWND(即需要缩略图视图的窗口)和目标 HWND(需要呈现缩略图的窗口)。在使用缩略图结束之后,您需要通过调用 DwmUnregisterThumbnail 让 DWM 知道关系已结束。在创建了缩略图之后,DwmRegisterThumbnail 函数将返回一个缩略图句柄,所有其他缩略图函数将把该句柄作为参数。在注册缩略图之后,您需要调用 DwmUpdateThumbnailProperties 才能更新缩略图。图 11 显示了呈现另一窗口实时缩略图的窗体示例代码。
public partial class Thumbnail : Form { private IntPtr m_hThumbnail; public Thumbnail() { InitializeComponent(); } public void CreateAndShow(IntPtr sourceWindow) { m_hThumbnail = DwmApi.DwmRegisterThumbnail( Handle, sourceWindow); DwmApi.DWM_THUMBNAIL_PROPERTIES m_ThumbnailProperties = new DwmApi.DWM_THUMBNAIL_PROPERTIES(); m_ThumbnailProperties.dwFlags = DwmApi.DWM_THUMBNAIL_PROPERTIES.DWM_TNP_VISIBLE + DwmApi.DWM_THUMBNAIL_PROPERTIES.DWM_TNP_OPACITY + DwmApi.DWM_THUMBNAIL_PROPERTIES.DWM_TNP_RECTDESTINATION + DwmApi.DWM_THUMBNAIL_PROPERTIES. DWM_TNP_SOURCECLIENTAREAONLY; m_ThumbnailProperties.opacity = 255; m_ThumbnailProperties.fVisible = true; m_ThumbnailProperties.rcSource = m_ThumbnailProperties.rcDestination = new DwmApi.RECT(0, 0, ClientRectangle.Right, ClientRectangle.Bottom); m_ThumbnailProperties.fSourceClientAreaOnly = false; DwmApi.DwmUpdateThumbnailProperties( m_hThumbnail, m_ThumbnailProperties); Show(); } protected override void Dispose(bool disposing) { if (disposing && (components != null)) components.Dispose(); base.Dispose(disposing); if (m_hThumbnail != IntPtr.Zero) { if (DwmApi.DwmIsCompositionEnabled()) DwmApi.DwmUnregisterThumbnail(m_hThumbnail); m_hThumbnail = IntPtr.Zero; } } }除注册和取消注册缩略图的两个函数外,还有其他两个函数与缩略图一起使用。DwmQueryThumbnailSourceSize 返回指定缩略图的原始大小。DwmUpdateThumbnailProperties 可让您更新给定 DWM 缩略图的属性。它采用 DWM_THUMBNAIL_PROPERTIES 结构,其 C# 实现如图 5 所示。 在不想使用整个源窗口时,使用 DWM_THUMBNAIL_PROPERTIES 结构可以指定属性的数量,如目标窗口中的目标矩形(rcDestination 成员)和要使用的源窗口的矩形区域(rcSource 成员)。 如果不想让缩略图完全不透明,还可以使用不透明度成员指定缩略图的不透明度,其中 0 为透明,255 为不透明。如果希望缩略图不可视,可以将 fVisible 标志设置为 false。如果仅希望使用缩略图窗口的客户端区域,而不是整个源窗口(其中包括非客户端区域,如框架和标题栏),则只需将 fSourceClientAreaOnly 布尔值设置为 true。您可以通过 dwFlags 参数告诉界面要设置哪些参数。当设置某个参数时,您需要在 dwFlags 参数中打开相应的位。 最后,在诸如目标窗口大小方面没有限制,通常使用缩略图界面扩大和缩小源窗口。在保持长宽比方面存在限制。始终保持源窗口的长宽比。如果更改了源窗口的大小,缩略图也将更改大小,以保持其本身处于指定的边界内。 在本文随附的源代码中提供了一个按钮,使用该按钮将创建主应用程序窗口的微型实时缩略图,如图 12 所示。 您会很容易发现,缩略图的呈现是实时的。如果更改主应用程序窗口,则会看到缩略图更新。您会发现,稍微发挥一下自己的聪明才智,就可以相当容易地使用缩略图以及 FindWindow 和 GetWindow Win32 函数创建自己的任务切换器。
小生在这只是起到搬运工的作用
原文地址:http://msdn.microsoft.com/zh-cn/magazine/cc163435.aspx#S4
/****************************邪恶的分割线*******************************/
在这小生献上实现的一段代码
一下代码实现了玻璃效果只需将代码拷入然后构造函数里调用方法dwmInitialize();即可.
需添加命名空间using System.Runtime.InteropServices;
/// <summary> /// /// </summary> /// <param name="e"></param> protected override void OnPaintBackground(PaintEventArgs e) { base.OnPaintBackground(e); e.Graphics.Clear(Color.FromArgb(100, 10, 10, 10)); } /// <summary> /// dwm初始化 /// </summary> private void dwmInitialize() { #region[dwmInitialize] int flag = 0; MARGINS mg = new MARGINS(); mg.m_Buttom = -1;// 100; mg.m_Left = -1;// 100; mg.m_Right = -1;//100; mg.m_Top = -1;// 100; //判断Vista系统 if (System.Environment.OSVersion.Version.Major >= 6) { DwmIsCompositionEnabled(ref flag); //检测Aero是否为打开 if (flag > 0) { DwmExtendFrameIntoClientArea(this.Handle, ref mg); } else { MessageBox.Show("Desktop Composition is Disabled!"); } } else { MessageBox.Show("Please run this on Windows Vista."); } #endregion } /// <summary> /// /// </summary> public struct MARGINS { public int m_Left; public int m_Right; public int m_Top; public int m_Buttom; }; [DllImport("dwmapi.dll")] private static extern void DwmIsCompositionEnabled(ref int enabledptr); [DllImport("dwmapi.dll", PreserveSig = false)] public static extern bool DwmIsCompositionEnabled(); [DllImport("dwmapi.dll")] private static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margin);