從 Windows 95 到 Windows 2000/XP,系統內建圖示不僅數量增加,視覺風格也大幅現代化。開發者常需在自訂對話框、工具列、狀態列中使用這些標準圖示(如資訊、警告、錯誤、詢問),卻面臨「資源 ID 與實際外觀不符」、「新舊版本語意映射混淆」、「IDI_WINLOGO 行為詭異」等問題。本文系統梳理系統圖示的獲取、使用技巧與版本差異,並提供可直接套用的程式碼模式。
呼叫 LoadIcon(NULL, MAKEINTRESOURCE(nID)),傳入預定義資源 ID。NULL 表示載入系統資源。範例:
HICON hIconQuestion = ::LoadIcon(NULL, IDI_QUESTION);
| 符號常數 | 傳統外觀 (Win95/98) | 現代外觀 (Win2000/XP+) | 語意用途 |
|---|---|---|---|
| IDI_APPLICATION | 預設應用程式圖示 | 通用視窗圖示 | 應用程式預設圖示 |
| IDI_HAND | 手掌(停止) | 紅底白叉(錯誤) | 嚴重錯誤/停止 |
| IDI_QUESTION | 問號氣泡 | 藍底白問號 | 詢問/幫助 |
| IDI_EXCLAMATION | 驚嘆號 | 黃底黑驚嘆號(警告) | 警告/注意 |
| IDI_ASTERISK | 星號 | 藍底白 i(資訊) | 資訊/提示 |
| IDI_WINLOGO | Windows 飛躍旗幟 | 同 IDI_APPLICATION | Windows 品牌(見下文說明) |
| IDI_WARNING | — | 同 IDI_EXCLAMATION | 警告(別名) |
| IDI_ERROR | — | 同 IDI_HAND | 錯誤(別名) |
| IDI_INFORMATION | — | 同 IDI_ASTERISK | 資訊(別名) |
winuser.h 中的映射#if (WINVER >= 0x0400)
#define IDI_WARNING IDI_EXCLAMATION
#define IDI_ERROR IDI_HAND
#define IDI_INFORMATION IDI_ASTERISK
#endif
Microsoft 為了語意清晰,在 WINVER >= 0x0400 (Windows 95/NT 4.0) 起引入新符號,並將舊符號映射至新外觀。開發者應優先使用新符號(IDI_WARNING、IDI_ERROR、IDI_INFORMATION),程式碼可讀性更強,且能正確傳達設計意圖。
IDI_WINLOGO 的特殊行為雖然 IDI_WINLOGO 在 XP 時代仍存在,但載入後顯示的是通用視窗圖示(同 IDI_APPLICATION),而非經典的四色飛躍旗幟。MSDN 註明此為「設計行為」。若需品牌標誌,建議自行攜帶品牌圖示資源,勿依賴此系統 ID。
CStatic)// 資源編輯器加入 Static 控制項,屬性設為 Icon 模式,ID 為 IDC_STATIC_ICON
// 程式碼中:
HICON hIcon = ::LoadIcon(NULL, IDI_INFORMATION);
((CStatic*)GetDlgItem(IDC_STATIC_ICON))->SetIcon(hIcon);
CStatic 的 CStaticLink)範例程式的「關於」對話框即採用此法:問號圖示 + URL 連結,點擊可啟動瀏覽器。
OnPaint 繪製(適用於自繪視窗)void CMainFrame::OnPaint() {
CPaintDC dc(this);
CRect rect(10, 10, 42, 42);
for (int i = 0; SysIcons[i].nResID; ++i) {
HICON hIcon = ::LoadIcon(NULL, SysIcons[i].nResID);
if (hIcon) {
dc.DrawIcon(rect.left, rect.top, hIcon);
dc.TextOut(rect.right + 5, rect.top, SysIcons[i].name);
rect.OffsetRect(0, 36);
}
}
}
其中 SysIcons 為結構陣列,包含資源 ID 與顯示名稱,條件編譯區隔新舊版本 ID。
const struct {
LPCTSTR nResID;
LPCTSTR name;
} SysIcons[] = {
{ MAKEINTRESOURCE(IDI_APPLICATION), _T("IDI_APPLICATION") },
{ MAKEINTRESOURCE(IDI_HAND), _T("IDI_HAND") },
{ MAKEINTRESOURCE(IDI_QUESTION), _T("IDI_QUESTION") },
{ MAKEINTRESOURCE(IDI_EXCLAMATION), _T("IDI_EXCLAMATION") },
{ MAKEINTRESOURCE(IDI_ASTERISK), _T("IDI_ASTERISK") },
#if (WINVER >= 0x0400)
{ MAKEINTRESOURCE(IDI_WINLOGO), _T("IDI_WINLOGO") },
{ MAKEINTRESOURCE(IDI_WARNING), _T("IDI_WARNING") },
{ MAKEINTRESOURCE(IDI_ERROR), _T("IDI_ERROR") },
{ MAKEINTRESOURCE(IDI_INFORMATION), _T("IDI_INFORMATION") },
#endif
{ NULL, NULL }
};
IDI_INFORMATION/IDI_WARNING/IDI_ERROR/IDI_QUESTION 而非舊符號。#if (WINVER >= 0x0400) 守護新符號,確保舊平台相容。IDI_WINLOGO:不作為品牌圖示來源,改用自訂資源。LoadIcon 載入的系統圖示無需呼叫 DestroyIcon 釋放,系統自動管理。LoadImage 搭配 LR_DEFAULTSIZE | LR_SHARED 載入可縮放圖示,或改用向量圖示資源。善用系統圖示能讓應用程式與作業系統視覺語言保持一致,降低自繪圖示的維護成本。理解符號映射演變歷史,避開 IDI_WINLOGO 等陷阱,即可在各版本 Windows 上穩定呈現專業的視覺體驗。本文提供的列舉結構與顯示模式,可直接整合至現有 MFC/SDK 專案中。