本節講述圖標、鼠標指針位圖、字符串資源表、自定義資源的添加和應用。
本節必須掌握的知識點:
??? ????圖標
??? ????第56練:ICON圖標資源
??? ????鼠標指針位圖
??? ????字符串資源表
??? ????自定義資源
??? ????第57練:字符串資源表和自定義資源
9.1.1 圖標
在 Windows 窗口編程中,圖標(Icon)是用于表示應用程序、窗口或文件的小圖像。Windows 提供了幾種不同尺寸和顏色深度的圖標,用于在窗口標題欄、任務欄、文件資源管理器等地方顯示。
■以下是一些與圖標相關的常見任務和概念:
●創建圖標文件:
1.圖標文件通常使用 .ico 擴展名,可以包含多個圖標資源。
2.可以使用圖標編輯器(如 Visual Studio、Visual Studio Code、GIMP 等)創建或編輯圖標文件。
●加載和顯示圖標:
1.使用 LoadIcon 函數從 .ico 文件或資源中加載圖標。
2.使用 DrawIcon 函數將圖標繪制到設備上下文(DC)中的指定位置。
●獲得圖標的大小:
cxIcon = GetSystemMetrics (SM_CXICON) ;
cyIcon = GetSystemMetrics (SM_CYICON) ;
●設置窗口圖標:
1.使用 WM_SETICON 消息或 SetClassLongPtr 函數設置窗口的大圖標和小圖標。
2.大圖標通常在窗口的標題欄和任務欄中顯示,小圖標顯示在窗口的任務欄按鈕中。
●資源文件中的圖標:
1.可以將圖標資源添加到應用程序的資源文件(.rc)中。
2.在應用程序啟動時,可以使用 LoadIcon 或 LoadIconEx 函數加載資源文件中的圖標。
■在應用程序中使用圖標
Windows程序可以在定義一個帶有WNDCLASS結構并使用RegisterClass注冊的窗口類中指定圖標。當圖標文件同時包含標準大小和小號圖像時。在需要顯示圖標圖像時,Windows會在圖標文件中選擇大小最合適的圖像。
RegisterClass有一個增強版本,名為RegisterClassEx,它使用一個名為WNDCLASSEX 的結構。WNDCLASSEX結構有兩個額外的字段:cbSize和blconSm。cbSize字段表示 WNDCLASSEX結構的大小,而hlconSm應該被設為小圖標的句柄。因此,在WNDCLASSEX結構中你需要設定與兩個圖標文件相關聯的兩個圖標句柄—— 一個是標準圖標而另一個是小圖標。
因為Windows可以從單個圖標文件中提取了正確尺寸的圖標圖像。RegisterClassEx似乎沒有必要。如果hlconSm字段引用的是一個包含多個圖像的圖標文件,那么只有第一個會被使用。這可以是一個標準尺寸的圖標,只不過之后會被縮小。RegisterClassEx似乎是為使用多個圖標圖像設計的,這些圖像每個只包含一個圖標尺寸。因為可以在同一個文件中包含多個圖標尺寸,所以使用 WNDCLASS 和 RegisterClass也是可以的。
如果想在程序運行時動態改變程序的圖標,可以通過調用SetClassLong函數來實現。 比如,如果有另一個和標識符IDI_ALTICON 相關聯的圖標文件,便可以使用下面的語句 切換到那個圖標:
SetClassLong (hwnd, GCL_HICON,
?????? LoadIcon (hInstance, MAKEINTRESOURCE (IDI_ALTICON))) ;
如果不想保存程序圖標的句柄,而是使用Drawlcon函數來在某處顯示它,那么你可以 調用GetClassLong函數來獲得句柄。比如:
DrawIcon (hdc, x, y, GetClassLong (hwnd, GCL_HICON)) ;
在Windows文檔的某些地方,LoadIcon被描述為“已過時”,而Loadlmage被推薦使 用。LoadImage當然更靈活,但它目前還無法代替Loadlcon的簡潔性。你會注意到在 ICONDEMO中Loadlcon對同一圖標被調用了兩次。這不是個問題,不會因此有更多內存被使用。Loadlcon是少有的這樣幾個函數之一:它獲得一個句柄,但不要求該句柄被銷毀。 實際上確實存在一個DestroyIcon函數,但它是和Createlcon、Createlconlndirect以及 CreateIconFromResource配套使用的。這些函數允許程序用算法動態生成圖標。
■下面是一個示例代碼片段,展示了如何加載和設置窗口圖標:
HICON hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1)); //加載圖標資源
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); //設置大圖標
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); //設置小圖標
在上述示例中,hInstance 是應用程序的實例句柄,IDI_ICON1 是圖標資源的 ID。可以使用資源編輯器將圖標資源添加到應用程序的資源文件中,并分配一個唯一的 ID 給它。
■VS添加資源步驟:
1.選中資源文件,鼠標右鍵>添加>資源
圖9-1 添加資源
??2.點擊添加后,自動生成ICONDEMO.rc和resource.h文件。選擇Icon,點擊新建或導入現有的Icon圖標資源。
?圖9-2 新建或導入圖標資源
?3.資源視圖窗口,選中已添加的資源ID_ICON1,鼠標右鍵選擇屬性或者ALT+ENTER鍵打開圖標編輯器修改ID。
?圖9-3 定義圖標資源屬性
4.修改ico文件名和ID并保存,或者按下ALT+ENTER鍵修改資源屬性。
?圖9-4 修改圖標文件名和ID
9.1.2 第56練:ICON圖標資源
/*------------------------------------------------------------------------
?056 WIN32 API 每日一練
???? 第56個例子ICONDEMO.C:ICON圖標資源
???? LoadIcon 函數
???? MAKEINTRESOURCE宏
???? DrawIcon函數
??? ?按下ALT+ENTER鍵修改資源屬性
?(c) www.bcdaren.com 編程達人
-----------------------------------------------------------------------*/
#include <windows.h>
#include "resource.h" //編譯器添加資源時自動創建的頭文件
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
TCHAR szAppName[] = TEXT("IconDemo");//程序名
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
?PSTR szCmdLine, int iCmdShow)
{
???? //TCHAR szAppName[] = TEXT("IconDemo");
???? HWND hwnd;
???? MSG msg;
???? WNDCLASSEX wndclass;
??? ?wndclass.cbSize = sizeof(WNDCLASSEX);? //新增
??? ?wndclass.hIconSm = NULL;?????????????? //新增
???? wndclass.style = CS_HREDRAW | CS_VREDRAW;
???? wndclass.lpfnWndProc = WndProc;
???? wndclass.cbClsExtra = 0;
???? wndclass.cbWndExtra = 0;
???? wndclass.hInstance = hInstance;
???? //szAppName為字符串ID,對應資源中的位圖ID需要修改為字符串ID"IconDemo"
??? // wndclass.hIcon = LoadIcon(hInstance, szAppName);
??? ?wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));//數字ID
???? wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
???? wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
???? wndclass.lpszMenuName = NULL;
???? wndclass.lpszClassName = szAppName;
???? if (!RegisterClassEx(&wndclass))?? //RegisterClassEx
???? {
????????? MessageBox(NULL, TEXT("This program requires Windows NT!"),
?????????????? szAppName,
?????????????? MB_ICONERROR);
????????? return 0;
???? }
???? hwnd = CreateWindow(szAppName, TEXT("Icon Demo"),
????????? WS_OVERLAPPEDWINDOW,
????????? CW_USEDEFAULT, CW_USEDEFAULT,
????????? CW_USEDEFAULT, CW_USEDEFAULT,
????????? NULL, NULL, hInstance, NULL);
???? ShowWindow(hwnd, iCmdShow);
???? UpdateWindow(hwnd);
???? while (GetMessage(&msg, NULL, 0, 0))
???? {
????????? TranslateMessage(&msg);
????????? DispatchMessage(&msg);
???? }
???? return msg.wParam;
}
LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM
lParam)
{
???? static HICON hIcon;
???? static int cxIcon,cyIcon,cxClient,cyClient;
???? HDC hdc;
???? HINSTANCE hInstance;
???? PAINTSTRUCT ps;
???? int x,y;
???? switch (message)
???? {
???? case WM_CREATE:
????????? hInstance = ((LPCREATESTRUCT)lParam)->hInstance;
????????? //hIcon = LoadIcon(hInstance,szAppName);//字符串ID
??????? ? hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));//數字ID
??????? ?
????????? cxIcon = GetSystemMetrics(SM_CXICON);//獲取圖標尺寸
????????? cyIcon = GetSystemMetrics(SM_CYICON);
????????? return 0;
???? case WM_SIZE:
????????? cxClient = LOWORD(lParam);//圖標位置
????????? cyClient = HIWORD(lParam);
????????? return 0;
???? case WM_PAINT:
????????? hdc = BeginPaint(hwnd,&ps);
????????? //繪制圖標
????????? for (y = 0;y < cyClient;y += cyIcon)
????????? {
?????????????? for (x = 0;x < cxClient;x += cxIcon)
?????????????? {
??????????????????? DrawIcon(hdc,x,y,hIcon);//顯示圖標
?????????????? }
????????? }
????????? EndPaint(hwnd,&ps);
????????? return 0;
???? case WM_DESTROY:
????????? PostQuitMessage(0);
????????? return 0;
???? }
???? return DefWindowProc(hwnd,message,wParam,lParam);
}
/******************************************************************************
LoadIcon 函數:從與應用程序實例關聯的可執行(.exe)文件中加載指定的圖標資源。
HICON LoadIconA(
? HINSTANCE hInstance,//模塊實例的句柄,其可執行文件包含要加載的圖標。加載標準圖標時,此參數必須為NULL。
? LPCSTR??? lpIconName//要加載的圖標資源的名稱。或者,此參數可以在低位字中包含資源標識符,在高位字中包含零。
????????????????????? //使用MAKEINTRESOURCE宏來創建此值。
);
返回值
類型:HICON
如果函數成功,則返回值是新加載的圖標的句柄。
*******************************************************************************
MAKEINTRESOURCEA宏:將整數值轉換為與資源管理功能兼容的資源類型。使用此宏代替包含資源名稱的字符串。
void MAKEINTRESOURCEA(
?? i //要轉換的整數值。
);
*******************************************************************************
DrawIcon函數:將圖標或光標繪制到指定的設備上下文中。若要指定其他繪圖選項,請使用DrawIconEx函數。
BOOL DrawIcon(
? HDC?? hDC,?? //設備上下文的句柄,將在其中繪制圖標或光標。
? int?? X,???? //圖標左上角的邏輯x坐標。
? int?? Y,???? //圖標左上角的邏輯y坐標。
? HICON hIcon? //要繪制的圖標的句柄。
);
*/
●Resource文件:
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 056_ICONDEMO.rc 使用
//
#define IDI_ICON1?????????????????????? 101
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE??????? 102
#define _APS_NEXT_COMMAND_VALUE???????? 40001
#define _APS_NEXT_CONTROL_VALUE???????? 1001
#define _APS_NEXT_SYMED_VALUE?????????? 101
#endif
#endif
●056_ICONDEMO.rc:
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/
#undef APSTUDIO_READONLY_SYMBOLS
/
// 中文(簡體,中國) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_CHS)
LANGUAGE LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED
#ifdef APSTUDIO_INVOKED
/
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
??? "resource.h\0"
END
2 TEXTINCLUDE
BEGIN
??? "#include ""winres.h""\r\n"
??? "\0"
END
3 TEXTINCLUDE
BEGIN
??? "\r\n"
??? "\0"
END
#endif??? // APSTUDIO_INVOKED
/
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1?????????????? ICON??????????????????? "icon1.ico"
#endif??? // 中文(簡體,中國) resources
/
#ifndef APSTUDIO_INVOKED
/
//
// Generated from the TEXTINCLUDE 3 resource.
//
/
#endif??? // not APSTUDIO_INVOKED
?????? 運行結果:
圖9-5 圖標資源
?
總結
實例ICONDEMO.C添加了一個圖標資源,并且在窗口客戶區內填充圖標位圖。圖標位圖文件icon1.ico位于當前編譯目錄。
添加圖標資源的方法在上一小節中已經講述,在解決方案的“資源文件”目錄下點擊鼠標右鍵,選擇添加>資源>ICON,導入icon1.ico位圖文件。然后VS自動創建re’source資源頭文件和056_ICONDEMO.rc資源腳本文件。re’source資源頭文件中包含圖標資源的數字ID 101,056_ICONDEMO.rc資源腳本文件的資源類型為ICON,將圖標資源ID綁定圖標位圖文件:
IDI_ICON1?????????????? ICON??????????????????? "icon1.ico"
實例的WinMain主程序中,定義了一個擴展窗口類WNDCLASSEX,對比WNDCLASS結構新增了“cbSize”結構大小字段和“hIconSm”小圖標字段。在hIcon字段添加圖標:
wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));//數字ID
【注意】由于添加圖標資源時使用的是數字ID,所以這里需要使用MAKEINTRESOURCE宏將IDI_ICON1轉換為字符串ID。
請讀者測試:如果我們將資源中的圖標位圖ID修改為字符串ID"IconDemo",則不需要轉換:
wndclass.hIcon = LoadIcon(hInstance, szAppName);
窗口過程:
處理WM_CREATE消息:調用LoadIcon函數加載圖標資源,獲取圖標資源句柄。然后調用GetSystemMetrics函數獲取圖標資源尺寸。
處理WM_SIZE消息:獲取窗口客戶區寬和高。
處理WM_PAINT消息:調用DrawIcon函數在窗口客戶區內填充圖標。