分享
 
 
 

在 Windows 应用程序中使用 Windows XP 的外观风格和 PrintWindow

王朝vc·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

Paul Hellyar

Microsoft Corporation

2001年10月25日

注意:此示例仅适用于运行 Windows XP 的计算机。打开示例并打开两个或多个应用程序窗口之后,请使用 AltTab 功能键来运行 TaskSwitcher 应用程序。摘要:本文介绍了一种增强的 AltTab 应用程序 TaskSwitcher,并以此为框架演示了在 Windows 应用程序中使用 Windows XP 新的外观风格和 PrintWindow 的方法。

目录简介 TaskSwitcher 应用程序 截取键盘输入 枚举顶层应用程序窗口 显示顶层应用程序窗口 使用 Comctl32.dll 版本 6 总结 简介Microsoft® Windows® XP 引入了一种新的外观风格,它使用方便,并且用户界面也更加丰富。例如,圆角窗口、更具质感的任务栏以及将鼠标悬停在 UI 元素上时,可实现 UI 元素的热跟踪。

图 1:新外观风格中的 Calculator(计算器)和 Display Properties(显示属性)对话框

Windows XP 还引入了新的打印 API:PrintWindow(英文)。该 API 允许调用者制作窗口的快照并将其插入设备环境。

有关外观风格以及将其应用于应用程序的介绍,请参阅 MSDN Library 中的技术文章“使用 Windows XP 的外观风格”。该文章提供了相关的概述和介绍信息,而本文的主要目的是提供一个使用外观风格 API 和 PrintWindow API 的实例。本文还为使用某些以前的 Win32 API 提供了一个刷新程序。

本文将特别阐述 TaskSwitcher 应用程序,它与目前 Windows 中已有的 AltTab 机制具有相同的功能。除了显示图标列表外,该应用程序还将显示将要切换到的应用程序的缩略图预览。显示应用程序图标和预览的容器窗口将通过外观风格 API 显示出来,使应用程序的外观符合最终用户当前选择的外观风格。

TaskSwitcher 应用程序TaskSwitcher 是为代替 Windows XP 的现有 AltTab 应用程序切换机制而设计的。AltTab 是内置的 Windows 超级用户功能,它使最终用户能够在顶层应用程序窗口之间进行快速切换。当按下热键组合 Alt+Tab 时,Windows 会生成最终用户正在使用的已打开窗口的列表。已打开窗口的列表将以一组图标的形式显示,其中一个图标带有矩形的选择边框。当最终用户继续按住 Alt 键并按下 Tab 键时,矩形选择框将移至下一个图标。释放 Alt 键后,Windows 将把选定的图标所代表的应用程序置于前台。

图 2:Windows XP AltTab 容器窗口

此功能在逻辑上可以分成三个部分:首先,应用程序必须侦听组合键 Alt+Tab;接收到该组合键时,应用程序需要枚举桌面上的顶层应用程序窗口;最后,应用程序需要在某种 UI 容器中显示这些窗口,使用户可以选择要切换到的应用程序的图标。

截取键盘输入使用 Win32 API,您可以通过几种方法之一创建侦听特定击键的应用程序。最简单的方法是使用 API RegisterHotKey。该 API 包含一个 hwnd、一个 ID、一个虚拟键和一个组合键。如果此调用成功,则无论何时按下虚拟键和组合键,hwnd 的 WndProc 都会收到一个 WM_HOTKEY 消息,该消息的 wParam 等于 ID。无论侦听应用程序窗口是否处于活动状态,都是如此。无论何时按下 AltTab,下面的调用都会使 hwndApp 收到一条 WM_HOTKEY 消息:

RegisterHotKey(hwndApp, IDH_ALTTAB, MOD_ALT, VK_TAB)

在 Windows XP 之前,无法将 AltTab 注册为热键。在 Windows XP 中,您不仅可以成功地将 AltTab 注册为热键,而且 Windows XP 还使您可以自己处理该事件,而不用启动其自身内置的 AltTab 热键处理程序。

// 创建一个侦听热键的虚拟窗口

HWND hwndApp = CreateWindow(WC_APP, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE,

NULL, THIS_EXE, NULL);

if (hwnd)

{

// 注册 Alt+Tab

RegisterHotKey(hwndApp, IDH_NEXT, MOD_ALT, VK_TAB);

RegisterHotKey(hwndApp, IDH_PREV, MOD_ALT|MOD_SHIFT, VK_TAB);

MSG msg;

while (GetMessage(&msg, NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

LRESULT WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

switch (uMsg)

{

case WM_HOTKEY:

{

switch (wParam)

{

// 如果未显示容器窗口,则枚举

// 顶层窗口,提取图标和文本,

// 并将其显示在容器窗口中

case IDH_NEXT:

{

// 在窗口层次结构中选择

// 下一个顶层窗口的图标

break;

}

case IDH_PREV:

{

// 在窗口层次结构中选择

// 上一个顶层窗口的图标

}

}

}

}

}

第二种实现键盘侦听的更高级的方法是同时使用 API SetWindowsHookEx和 WH_KEYBOARD_LL。该方法在当前桌面的全局范围内创建一个低级别的键盘挂钩层。在调用 SetWindowsHookEx 时指定的 LowLevelKeyboardProc 回调函数将接收所有的键盘输入。处理完键盘输入后,LowLevelKeyboardProc 应调用 CallNextHookEx 以使下一个挂钩链(很可能是目标应用程序)能够接收输入。由于 LowLevelKeyboardProc 接收了所有的键盘事件,因此可以很容易地将其用作一个状态机,用于侦听同时按下的 Alt 和 Tab 组合键。如果该应用程序实现它自己的 AltTab 机制,则此时将执行窗口枚举算法,并从 LowLevelKeyboardHook 中返回而不把最后的 AltTab 键事件转给其他应用程序。

hhook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hinst, 0);

LRESULT LowLevelKeyboardProc(INT nCode, WPARAM wParam, LPARAM lParam)

{

static BOOL fShiftPressed = FALSE;

BOOL fHandled = FALSE;

if (nCode == HC_ACTION)

{

KBDLLHOOKSTRUCT *pkbdllhook = (KBDLLHOOKSTRUCT *)lParam;

switch (wParam)

{

case WM_SYSKEYDOWN:

switch (pkbdllhook->vkCode)

{

case VK_LSHIFT:

case VK_RSHIFT:

{

// 用户按下 Shift 键

fShiftPressed = TRUE;

break;

}

case VK_TAB:

{

if (pkbdllhook->flags & LLKHF_ALTDOWN)

{

// 用户按下 Alt+Tab,执行 AltTab 热键处理程序

fHandled = TRUE;

}

break;

}

case VK_ESCAPE:

{

if (pkbdllhook->flags & LLKHF_ALTDOWN)

{

// 用户按下 Esc 键,关闭 AltTab 容器窗口

// 并且不切换到选定窗口

fHandled = TRUE;

}

break;

}

}

break;

case WM_KEYUP:

case WM_SYSKEYUP:

switch (pkbdllhook->vkCode)

{

case VK_LMENU:

case VK_RMENU:

{

// 用户释放 Alt 键,关闭 AltTab 容器窗口

// 并切换到选定窗口

break;

}

case VK_LSHIFT:

case VK_RSHIFT:

{

// 用户释放 Shift 键

fShiftPressed = FALSE;

break;

}

}

break;

}

}

return (fHandled ? TRUE : CallNextHookEx(hhook, nCode, wParam, lParam));

}

枚举顶层应用程序窗口使用 Win32 API EnumWindows可以直接枚举顶层应用程序窗口。这个 API 接受 EnumFunc 回调函数作为参数。对于桌面上的每个顶层窗口,系统将用顶层窗口的窗口句柄作为参数,来调用 EnumFunc 函数。并不是所有的顶层窗口都应显示在 AltTab 列表中。需要查询窗口的一些属性,并且必须满足几个条件:窗口是应用程序窗口吗?窗口能被激活吗?窗口可视吗?窗口是 ToolWindow 吗?

接收到 AltTab 事件之后,TaskSwitcher 便开始使用 EnumWindows 枚举桌面上的顶层窗口。系统为每个顶层窗口调用回调函数。满足条件的窗口将被添加到窗口列表中,并显示在 AltTab 列表中。

显示顶层应用程序窗口在 AltTab 列表的 UI 显示中,TaskSwitcher 使用了很多 Windows XP 的新编程功能。它使用新的 API DrawShadowText 来显示选定的应用程序的文本,使用新的 API PrintWindow 来生成窗口预览。最后,而且也许是应用程序开发人员最感兴趣的,TaskSwitcher 使用了 Windows XP 的新外观风格。

收集窗口信息生成要在 AltTab 列表中显示的窗口列表后,会检索列表中每个窗口的各种属性并将其显示在预览容器中。通过向该窗口发送一个 WM_GETICON窗口消息,在列表中显示每个窗口的图标。当用户按 Tab 键在列表中移动时,列表中选定的应用程序图标的图标和文本将显示在预览容器的顶部。通过使用 API GetWindowText来检索每个窗口的标题文本。有趣的是,它使用了新的 API comctl32 v6 API DrawShadowText来显示应用程序文本。该 API 是 Windows XP 的新增功能,采用了 API DrawText 的所有相同参数,还有两个表示文本颜色和阴影颜色的 COLORREF 参数,以及阴影的 x 偏移量和 y 偏移量。

绘制窗口预览TaskSwitcher 还可以显示选定窗口的缩略图预览。(除非最小化所预览的窗口,因为此时将只显示该窗口的标题栏。)绘制缩略图预览时,TaskSwitcher 采用了一些高级的 Win32 绘图技术,例如双重缓冲和半色调缩放等。然而,获取窗口预览的核心技术是新的 Windows XP user32 API PrintWindow。PrintWindow 带有一个窗口句柄、一个 hdc 和一个保留标志。该 API 使用窗口重定向,将窗口的快照绘制到 hdc 中。

// 制作窗口 hwnd 的快照,该窗口存储在内存设备环境 hdcMem 中

HDC hdc = GetWindowDC(hwnd);

if (hdc)

{

HDC hdcMem = CreateCompatibleDC(hdc);

if (hdcMem)

{

RECT rc;

GetWindowRect(hwnd, &rc);

HBITMAP hbitmap = CreateCompatibleBitmap(hdc, RECTWIDTH(rc), RECTHEIGHT(rc));

if (hbitmap)

{

SelectObject(hdcMem, hbitmap);

PrintWindow(hwnd, hdcMem, 0);

DeleteObject(hbitmap);

}

DeleteObject(hdcMem);

}

ReleaseDC(hwnd, hdc);

}

使用外观风格 API 来显示容器所有这些都绘制在容器窗口上。容器窗口的背景体现了 Windows XP 的新外观风格。也就是说,它和 Windows XP 的其余部分具有相同的外观,包括圆角窗口以及与标题栏类似的更具质感的图案背景。显示容器背景时,TaskSwitcher 使用了 uxtheme.h 中的很多新的主题 API,例如 OpenThemeData、CloseThemeData、GetThemeBackgroundRegion和 DrawThemeBackground。在本例中,我们把用于开始面板顶部的外观风格用作容器窗口的背景。

#include <uxtheme.h>

#include <tmschema.h>

// AltTab 列表容器窗口的对话框程序

INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM, LPARAM lParam)

{

static HTHEME htheme = NULL;

switch (uMsg)

{

case WM_INITDIALOG:

{

htheme = OpenThemeData(hwnd, L"StartPanel");

if (htheme)

{

// 获取要用于绘制容器窗口的

// 背景区域部分并将其应用于

// 对话框。

HRGN hrgn = NULL;

GetWindowRect(hwnd, &rc);

OffsetRect(&rc, -rc.left, -rc.top);

if (SUCCEEDED(GetThemeBackgroundRegion(htheme, NULL,

SPP_USERPANE, 0, &rc, &hrgn)))

{

SetWindowRgn(hwnd, hrgn, FALSE);

}

}

break;

}

case WM_PAINT:

{

PAINTSTRUCT ps;

HDC hdc = BeginPaint(hwnd, &ps);

if (hdc)

{

if (htheme)

{

// 外观风格处于活动状态,使用外观

// 风格 API 进行绘制。

RECT rc;

GetWindowRect(hwnd, &rc);

OffsetRect(&rc, -rc.left, -rc.top);

DrawThemeBackground(htheme, hdc, SPP_USERPANE, 0, &rc, NULL);

}

else

{

// 外观风格不处于活动状态,按传统

// 窗口样式进行绘制。

}

}

EndPaint(hwnd, &ps);

break;

}

case WM_THEMECHANGED:

{

// 外观风格已更改,关闭现有的 htheme 并尝试

// 打开一个新的 htheme。

if (htheme)

{

CloseThemeData(htheme);

}

htheme = OpenThemeData(hwnd, L"StartPanel");

break;

}

}

}

图 3:TaskSwitcher AltTab 容器窗口

使用 Comctl32.dll 版本 6Taskswitcher 利用了 comctl32.dll 版本 6 中的一些新功能。例如,图标列表使用 ListView 控件制作;容器的背景使用了匹配的背景水印,从而达到与窗口其余部分的自然融合。此外,API DrawShadowText 也是在 comctl32 v6 中找到的。

Comctl32 版本 6 是一个并行 DLL,即 comctl32.dll 版本 5 和版本 6 是同时安装在系统上的。默认情况下,当应用程序与 comctl32.lib 静态链接时,将使用版本 5。为了使应用程序能够使用版本 6,必须提供一个如下的应用程序声明文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">

<assemblyIdentity

version="1.0.0.0"

processorArchitecture="X86"

name="Microsoft.Shell.TaskSwitch "

type="win32"

/>

<description>TaskSwitcher:AltTab 替代程序。</description>

<dependency>

<dependentAssembly>

<assemblyIdentity

type="win32"

name="Microsoft.Windows.Common-Controls"

version="6.0.0.0"

processorArchitecture="X86"

publicKeyToken="6595b64144ccf1df"

language="*"

/>

</dependentAssembly>

</dependency>

</assembly>

然后通过在 .rc 文件中指定以下行,将该声明文件编入应用程序的资源部分。

CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "TaskSwitch.exe.manifest"

总结Windows XP 提供了一个全新的用户界面,包括新的外观风格以及能够直观捕获窗口内容的能力。使用本文介绍的技术,开发人员可以利用外观风格 API 为其应用程序设计一个可以与 Windows XP 其余部分的外观相匹配的独特外观。使用 PrintWindow,开发人员可以制作指定窗口的快照并将其插入设备环境。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有