自定义资源(Custom Resources)是在 Windows 程序中使用的一种资源类型,用于存储应用程序特定的数据、图像、音频、二进制文件等。通过自定义资源,开发者可以将应用程序所需的各种资源文件集中管理和存储,便于在程序中访问和使用。
■以下是一些关于自定义资源的要点:
●自定义资源的类型:
1.自定义资源可以是任何应用程序特定的数据或文件,如图像、音频、XML 配置文件、文本文件等。
2.自定义资源可以使用自定义的资源类型标识符进行标识,例如 "MY_CUSTOM_RESOURCE"。
●创建和编辑自定义资源:
1.自定义资源通常包含在应用程序的资源文件(.rc)中。
2.使用资源编辑器(如 Visual Studio 的资源视图)可以创建和编辑自定义资源。
3.在资源文件中,可以为每个资源指定唯一的资源标识符和对应的资源文件路径。
●引用和使用自定义资源:
1.在代码中引用和使用自定义资源时,可以使用资源标识符来加载或访问对应的资源。
2.使用相应的 API 函数(如 LoadResource、FindResource、LockResource 等)来加载和操作自定义资源。
3.根据自定义资源的类型,可以使用不同的 API 函数(如图像资源可以使用 GDI 函数、音频资源可以使用 DirectSound 函数)来处理自定义资源。
■下面是一个示例代码片段,展示了如何加载和使用自定义资源:
#include <Windows.h>
int main()
{
HINSTANCE hInstance = GetModuleHandle(NULL); // 获取当前实例句柄
HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(IDR_MY_RESOURCE), "MY_CUSTOM_RESOURCE"); // 查找自定义资源
HGLOBAL hResData = LoadResource(hInstance, hResource); // 加载自定义资源
LPVOID pData = LockResource(hResData); // 锁定自定义资源
// 使用自定义资源...
return 0;
}
在上述示例中,IDR_MY_RESOURCE 是自定义资源的标识符,在资源文件中定义了对应的资源文件路径。使用 FindResource 函数查找自定义资源,然后使用 LoadResource 函数加载自定义资源,最后使用 LockResource 函数锁定自定义资源以获取指向资源数据的指针。随后,可以根据资源的类型和需求,使用指针 pData 进行后续的处理和操作。
需要注意的是,自定义资源的具体使用方式取决于资源类型和应用程序的需求。开发者可以根据实际需求选择合适的资源类型和对应的 API 函数来处理自定义资源。
自定义资源为应用程序提供了一种灵活的资源管理方式,可以将应用程序所需的各种数据和文件统一管理,并方便地在程序中访问和使用。通过使用自定义资源,可以使应用程序更加模块化、可维护和可扩展。
■在VS中添加自定义资源
1、添加资源
2、点击“自定义”,填写资源类型“TEXT"
3、导入资源,自动生成text1.bin文件,资源ID:IDR_TEXT1
4、VS中直接在自定义编译器中输入资源文件名并修改ID报错:无法加载外部资源——可能的原因是资源未导入。
5、在资源视图框中添加资源。
6、导入外部资源文件。
7、重新填写资源文件名和ID,注意ID名称需要填写带引号的字符串"ANNABELLEE"。
8、保存设置之后就可以了
还有一种更简单的方法,直接修改资源文件POEPOEM.rc。TEXT下方画线处添加资源即可。
9.1.6 第57练:字符串资源表和自定义资源
/*------------------------------------------------------------------------
057 WIN32 API 每日一练
第57个例子POEPOEM.C:字符串资源表和自定义资源
LoadString 函数
LoadResource 函数
FindResource 函数
LockResource 函数
AnsiNext 宏定义
(c) www.bcdaren.com 编程达人
-----------------------------------------------------------------------*/
#include <windows.h>
#include "resource.h"
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HINSTANCE hInst ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{ //资源文件名,标题和错误信息
TCHAR szAppName[16], szCaption[64], szErrMsg[64];
HWND hwnd;
MSG msg;
WNDCLASS wndclass;
//加载字符串资源
LoadString(hInstance, IDS_APPNAME, szAppName,
sizeof(szAppName) / sizeof(TCHAR));
LoadString(hInstance, IDS_CAPTION, szCaption,
sizeof(szCaption) / sizeof(TCHAR));
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
//添加图标
wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
//wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
//自定义鼠标指针
wndclass.hCursor = LoadCursor(hInstance,MAKEINTRESOURCE(IDC_CURSOR1));
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szAppName;
//hInst = hInstance;//原代码中没有,且不会报错
if (!RegisterClass(&wndclass))
{
//LoadStringA(hInstance, IDS_APPNAME, (char *)szAppName,
// sizeof(szAppName));
LoadStringA(hInstance, IDS_ERRMSG, (char *)szErrMsg,
sizeof(szErrMsg));
MessageBoxA(NULL, (char *)szErrMsg,
(char *)szAppName,
MB_ICONERROR);
return 0;
}
hwnd = CreateWindow(szAppName, szCaption,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
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 char * pText;
static LPCSTR pText;
static HGLOBAL hResource;//资源句柄-指向内存块的句柄
static HWND hScroll;
static int iPosition, cxChar, cyChar, cyClient,
iNumLines, xScroll;
HDC hdc;
PAINTSTRUCT ps;
RECT rect;
TEXTMETRIC tm;
switch (message)
{
case WM_CREATE:
hdc = GetDC(hwnd);
GetTextMetrics(hdc, &tm);
cxChar = tm.tmAveCharWidth;
cyChar = tm.tmHeight + tm.tmExternalLeading;
ReleaseDC(hwnd, hdc);
//垂直滚动条的宽度,以像素为单位
xScroll = GetSystemMetrics(SM_CXVSCROLL);
//生成滚动条
hScroll = CreateWindow(TEXT("scrollbar"), NULL,
WS_CHILD | WS_VISIBLE | SBS_VERT,
0, 0, 0, 0,
hwnd, (HMENU)1, hInst, NULL);
//获取文本句柄
hResource = LoadResource(hInst,
FindResource(hInst, TEXT("AnnabelLee"),//确定具有指定类型和名称
的资源在指定模块中的位置
TEXT("TEXT")));
//检索指向内存中指定资源的指针
pText = (LPCSTR)LockResource(hResource);
iNumLines = 0;//读取文本行数
//获取文本行数
while (*pText != '\\' && *pText != '\0')//遇\或\0结束
{
if (*pText == '\n')
iNumLines++;
//返回ansi字符串中的下一个位置(指针),AnsiNext为16位Windows上
//的一个宏定义,Win32使用CharNext代替(AnsiNext只能处理ansi字符
//串;而CharNext可以ansi,也可以unicode字符串)
//pText = AnsiNext(pText);
pText = (LPCSTR)CharNext((LPCWSTR)pText);
}
//*pText = '\0';//此处原代码中说明以空字符结尾---实际文本不是以空字符结尾
//设置滚动条
SetScrollRange(hScroll, SB_CTL, 0, iNumLines, FALSE);
SetScrollPos(hScroll, SB_CTL, 0, FALSE);
return 0;
case WM_SIZE:
MoveWindow(hScroll, LOWORD(lParam) - xScroll, 0,
xScroll, cyClient = HIWORD(lParam), TRUE);
SetFocus(hwnd);//设置焦点
return 0;
case WM_SETFOCUS:
SetFocus(hScroll);//捕捉滚动条滑块焦点
return 0;
case WM_VSCROLL:
switch (LOWORD(wParam)) //须加LOWORD,因为通知码在低位字
{
case SB_TOP:
iPosition = 0;
break;
case SB_BOTTOM:
iPosition = iNumLines;
break;
case SB_LINEUP:
iPosition -= 1;
break;
case SB_LINEDOWN:
iPosition += 1;
break;
case SB_PAGEUP:
iPosition -= cyClient / cyChar;
break;
case SB_PAGEDOWN:
iPosition += cyClient / cyChar;
break;
case SB_THUMBTRACK:
iPosition = HIWORD(wParam);
break;
}
iPosition = max(0, min(iPosition, iNumLines));
if (iPosition != GetScrollPos(hScroll, SB_CTL))
{
SetScrollPos(hScroll, SB_CTL, iPosition, TRUE);
InvalidateRect(hwnd, NULL, TRUE);
}
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
pText = (char *)LockResource(hResource);
GetClientRect(hwnd, &rect);
rect.left += cxChar;//从第2列开始显示
rect.top += cyChar * (1 - iPosition);
//包括字体外部行首高度。通常,外部行首不包括在一行文本的高度中。
DrawTextA(hdc, pText, -1, &rect, DT_EXTERNALLEADING);
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
FreeResource(hResource);
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hwnd, message, wParam, lParam);
}
/******************************************************************************
LoadString 函数:加载字符串资源,然后将字符串复制到带有终止空字符的缓冲区中,
或者返回指向字符串资源本身的只读指针。
*******************************************************************************
LoadResource 函数:检索可用于获取指向内存中指定资源的第一个字节的指针的句柄
HGLOBAL LoadResource(
HMODULE hModule,//其可执行文件包含资源的模块的句柄。如果hModule为NULL,则系统将从用于创建当前进程的模块中加载资源
HRSRC hResInfo//要加载的资源的句柄
);
*******************************************************************************
FindResource 函数:确定具有指定类型和名称的资源在指定模块中的位置
HRSRC FindResourceA(
HMODULE hModule,//包含资源的模块的句柄
LPCSTR lpName,//资源的名称
LPCSTR lpType//资源的类型
);
*******************************************************************************
LockResource 函数:检索指向内存中指定资源的指针
LPVOID LockResource(
HGLOBAL hResData//要访问的资源的句柄
);
*******************************************************************************
AnsiNext 宏定义:
16位Windows上的一个api函数(准确地说是一个宏定义),只能处理ansi字符串;
后来Win32出现的时候,微软为了兼容,封装就是通过CharNext来实现的。
CharNext可以ansi,也可以unicode字符串。
该函数的就是返回ansi字符串中的下一个位置(指针)。
*/
Resource.h头文件
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ 生成的包含文件。
// 供 057_POEPOEM.rc 使用
//
#define IDS_APPNAME 1
#define IDS_Caption 2
#define IDS_CAPTION 3
#define IDS_ERRMSG 4
#define IDI_ICON1 1
#define IDC_CURSOR1 2
#define IDR_TEXT1 107
#define IDR_TEXT2 108
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 126
#define _APS_NEXT_COMMAND_VALUE 40002
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
057_POEPOEM.rc资源脚本文件包含4种类型资源:
1.图标资源:
// Icon
//
IDI_ICON1 ICON "D:\\code\\windows5.0\\A daily
practice\\057_POEPOEM\\057_POEPOEM\\icon1.ico"
2.鼠标指针位图资源:
// Cursor
//
IDC_CURSOR1 CURSOR "cursor1.cur"
3.字符串资源表:
// String Table
//
STRINGTABLE
BEGIN
IDS_APPNAME "PoePoem"
IDS_CAPTION """Annabel Lee"" by Edgar Allan Poe"
IDS_ERRMSG "This program requires Windows NT!"
END
4.自定义资源:
// TEXT
//
ANNABELLEE TEXT "POEPOEM.TXT"
运行结果:
图9-9 字符串资源表和自定义资源
总结
在POEPOEM.RC资源脚本中,用户定义资源被指定为TEXT类型,文本名字为 "ANNABELLEE" :ANNABELLEE TEXT POEPOEM.TXT
在WndProc中处理WM_CREATE消息时,该资源的句柄通过FindResource和LoadResource 来获得。LockResource将该资源锁定,一个小的例程将文件尾部的反斜杠(\)替换为0。这是为了方便在后面的WM_PAINT消息中使用DrawText函数。
【注意】我们使用了子窗口滚动条控件而不是窗口滚动条。该子窗口滚动条控件有一个自动键盘接口,所以在POEPOEM中不需要处理WM_KEYDOWN消息。
POEPOEM还使用了三个字符串,它们的ID在RESOURCE.H头文件中定义。在程序的开始,IDS_APPNAME和IDS_CAPTION字符串被LoadString加载到内存:
LoadString (hlnstance, IDS_APPNAME, szAppName,
sizeof (szAppName) /sizeof (TCHAR));
LoadString (hlnstance, IDS_CAPTION, szCaption,
sizeof (szCaption) /sizeof (TCHAR));
【注意】这两个调用在RegisterClass之前。程序用LoadStringA加载IDS_APPNAME和 IDS_ERRMSG字符串,并用MessageBoxA来显示自定义消息框:
if (!RegisterClass (&wndclass))
{
LoadStringA (hInstance, IDS_APPNAME, (char *) szAppName,
sizeof (szAppName)) ;
LoadStringA (hInstance, IDS_ERRMSG, (char *) szErrMsg,
sizeof (szErrMsg)) ;
MessageBoxA (NULL, (char *) szErrMsg,
(char *) szAppName, MB_ICONERROR) ;
return 0 ;
}
【注意】从TCHAR字符串变量到char指针的强制转换。
AnsiNext为16位Windows上的一个宏定义,Win32使用CharNext代替(AnsiNext只能处理ansi字符串;而CharNext可以ansi字符串,也可以unicode字符串)。
pText = AnsiNext(pText);
替换为:
pText = (LPCSTR)CharNext((LPCWSTR)pText);
由POEPOEM使用的所有字符串都被定义为资源,所以程序很容易就能够被翻译人员转换为非英语版本。