天天看點

在 Windows 應用程式中使用 Windows XP 的外觀風格和 PrintWindow

在 Windows 應用程式中使用 Windows XP 的外觀風格和 PrintWindow

在 Windows 應用程式中使用 Windows XP 的外觀風格和 PrintWindow

單擊此處下載下傳示例 - TaskSwitch.exe。

<!--END SAMPLE-->

Paul Hellyar

Microsoft Corporation

2001年10月25日

從 MSDN Downloads(英文)下載下傳本文的示例應用程式。

注意: 此示例僅适用于運作 Windows XP 的計算機。打開示例并打開兩個或多個應用程式視窗之後,請使用 AltTab 功能鍵來運作 TaskSwitcher 應用程式。
摘要:

本文介紹了一種增強的 AltTab 應用程式 TaskSwitcher,并以此為架構示範了在 Windows 應用程式中使用 Windows XP 新的外觀風格和 PrintWindow 的方法。

目錄

  • 簡介
  • TaskSwitcher 應用程式
  • 截取鍵盤輸入
  • 枚舉頂層應用程式視窗
  • 顯示頂層應用程式視窗
  • 使用 Comctl32.dll 版本 6
  • 總結

簡介

Microsoft® Windows® XP 引入了一種新的外觀風格,它使用友善,并且使用者界面也更加豐富。例如,圓角視窗、更具質感的工作列以及将滑鼠懸停在 UI 元素上時,可實作 UI 元素的熱跟蹤。

在 Windows 應用程式中使用 Windows XP 的外觀風格和 PrintWindow
圖 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 将把標明的圖示所代表的應用程式置于前台。

在 Windows 應用程式中使用 Windows XP 的外觀風格和 PrintWindow
圖 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;
        }
    }
}
      
在 Windows 應用程式中使用 Windows XP 的外觀風格和 PrintWindow
圖 3:TaskSwitcher AltTab 容器視窗

使用 Comctl32.dll 版本 6

Taskswitcher 利用了 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,開發人員可以制作指定視窗的快照并将其插入裝置環境。

<!--- end main ---><!--- end --->

繼續閱讀