在Windows平台下,可执行文件的图标资源存储在PE格式文件的资源段中,我们可以通过Win32提供的资源操作API直接对这部分资源进行读取和修改,不需要借助额外的第三方工具。这种方式适合需要在程序运行时动态调整自身或目标程序图标的场景,比如多皮肤程序、自定义安装包生成工具等。

核心Win32资源API介绍
实现图标资源的读取和修改,主要依赖以下几个核心API:
- FindResource:用于在指定模块的指定资源类型中查找目标资源,返回资源句柄。
- LoadResource:根据资源句柄加载资源到内存,返回资源数据句柄。
- LockResource:锁定加载到内存的资源,返回指向资源数据的指针。
- BeginUpdateResource:打开可执行文件用于资源更新,返回更新句柄。
- UpdateResource:向目标可执行文件写入新的资源数据。
- EndUpdateResource:结束资源更新操作,提交修改到文件。
读取可执行文件图标资源的实现
读取图标资源的核心思路是先定位到目标可执行文件中的图标资源,再加载并解析资源数据。以下是完整的实现代码:
#include <windows.h>
#include <iostream>
// 读取目标可执行文件的第一个图标资源
bool ReadExeIcon(const wchar_t* exePath) {
// 加载目标可执行文件作为模块,不执行其中的代码
HMODULE hModule = LoadLibraryEx(exePath, NULL, LOAD_LIBRARY_AS_DATAFILE);
if (hModule == NULL) {
std::wcout << L"加载可执行文件失败,错误码:" << GetLastError() << std::endl;
return false;
}
// 查找图标资源,RT_ICON是系统预定义的图标资源类型,这里查找第一个图标
HRSRC hRes = FindResource(hModule, MAKEINTRESOURCE(1), RT_ICON);
if (hRes == NULL) {
std::wcout << L"未找到图标资源,错误码:" << GetLastError() << std::endl;
FreeLibrary(hModule);
return false;
}
// 获取资源大小
DWORD resSize = SizeofResource(hModule, hRes);
if (resSize == 0) {
std::wcout << L"获取资源大小失败,错误码:" << GetLastError() << std::endl;
FreeLibrary(hModule);
return false;
}
// 加载资源到内存
HGLOBAL hResData = LoadResource(hModule, hRes);
if (hResData == NULL) {
std::wcout << L"加载资源失败,错误码:" << GetLastError() << std::endl;
FreeLibrary(hModule);
return false;
}
// 锁定资源获取指针
LPVOID pResData = LockResource(hResData);
if (pResData == NULL) {
std::wcout << L"锁定资源失败,错误码:" << GetLastError() << std::endl;
FreeLibrary(hModule);
return false;
}
std::wcout << L"成功读取图标资源,资源大小:" << resSize << L" 字节" << std::endl;
// 这里可以对pResData指向的图标数据做进一步处理,比如保存为ico文件
// 释放资源
FreeLibrary(hModule);
return true;
}
int main() {
// 替换为目标可执行文件路径
const wchar_t* targetExe = L"C:\Windows\notepad.exe";
ReadExeIcon(targetExe);
return 0;
}
修改可执行文件图标资源的实现
修改图标资源需要使用资源更新相关的API,需要注意的是目标可执行文件不能有只读属性,且最好先备份原文件避免操作失败导致文件损坏。以下是修改图标资源的完整代码:
#include <windows.h>
#include <iostream>
// 修改目标可执行文件的第一个图标资源
bool ModifyExeIcon(const wchar_t* targetExePath, const wchar_t* newIconPath) {
// 加载新的图标文件
HICON hNewIcon = (HICON)LoadImage(NULL, newIconPath, IMAGE_ICON, 0, 0, LR_LOADFROMFILE);
if (hNewIcon == NULL) {
std::wcout << L"加载新图标文件失败,错误码:" << GetLastError() << std::endl;
return false;
}
// 获取图标数据大小
DWORD iconSize = 0;
// 先获取需要的缓冲区大小
GetIconInfo(hNewIcon, NULL);
// 这里简化操作,直接使用系统API获取图标资源数据,实际场景中可以先将图标转换为符合PE资源格式的二进制数据
// 打开目标可执行文件用于资源更新
HANDLE hUpdate = BeginUpdateResource(targetExePath, FALSE);
if (hUpdate == NULL) {
std::wcout << L"打开可执行文件更新失败,错误码:" << GetLastError() << std::endl;
DestroyIcon(hNewIcon);
return false;
}
// 将图标转换为资源数据,这里使用简化的方式,实际需要将图标转换为RT_ICON格式的二进制数据
// 以下示例假设已经获取到正确的图标资源数据,实际开发中可以通过序列化图标结构得到
// 这里仅演示API调用流程,真实场景需要处理图标数据的格式转换
BYTE dummyData[1] = {0}; // 实际替换为真实的图标资源数据
if (!UpdateResource(hUpdate, RT_ICON, MAKEINTRESOURCE(1), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), dummyData, sizeof(dummyData))) {
std::wcout << L"更新资源失败,错误码:" << GetLastError() << std::endl;
EndUpdateResource(hUpdate, TRUE); // 回滚修改
DestroyIcon(hNewIcon);
return false;
}
// 提交资源修改
if (!EndUpdateResource(hUpdate, FALSE)) {
std::wcout << L"提交资源修改失败,错误码:" << GetLastError() << std::endl;
DestroyIcon(hNewIcon);
return false;
}
std::wcout << L"成功修改可执行文件图标资源" << std::endl;
DestroyIcon(hNewIcon);
return true;
}
int main() {
// 替换为目标可执行文件路径和新图标路径
const wchar_t* targetExe = L"C:\test\target.exe";
const wchar_t* newIcon = L"C:\test\new.ico";
ModifyExeIcon(targetExe, newIcon);
return 0;
}
注意事项
- 修改可执行文件资源时,目标文件不能被其他进程占用,否则更新操作会失败。
- 图标资源有多个尺寸和色深版本,如果需要完整替换所有图标变体,需要遍历所有图标资源ID进行更新。
- 操作前建议备份目标可执行文件,避免资源格式错误导致文件无法运行。
- 如果是修改当前正在运行的程序自身的图标,需要先复制到临时文件再操作,因为正在运行的程序文件会被系统锁定。
Win32_API可执行文件图标修改资源操作C_plus_plus修改时间:2026-06-19 07:48:37