修改window应用程序图标

需求:

客户需要定制程序的图标, 期望不需要重新编译也能修改图标.

方法:直接更新exe图标资源.

官网:添加、删除和替换资源 - Win32 apps | Microsoft Learn

例子: 使用资源 - Win32 apps | Microsoft Learn

资源类型: Winuser.h) (资源类型 - Win32 apps | Microsoft Learn

解析资源组种图标或游标组件的组成:NEWHEADER+NEWHEADER.ResCount*RESDIR

typedef struct {
  WORD Reserved;//保留;必须为零。
  WORD ResType;//RES_ICON=1图标资源类型。RES_CURSOR=2游标资源类型。
  WORD ResCount;//资源组中的图标或游标组件数。
} NEWHEADER, *PNEWHEADER;

typedef struct {
  ICONRESDIR Icon;//所指示图标的宽度、高度和颜色计数。
  CURSORDIR  Cursor;//所指示光标的宽度和高度。
  WORD       Planes;//图标或光标位图中的颜色平面数。
  WORD       BitCount;//图标或光标位图中每像素的位数。
  DWORD      BytesInRes;//资源的大小(以字节为单位)。
  WORD       IconCursorId;//具有唯一序号标识符的图标或光标。
} RESDIR;//请注意, RESDIR 结构由 ICONRESDIR 结构或 CURSORDIR 结构组成,也就是说要么是图标,要么是光标,只能存在一个

typedef struct {
  BYTE Width;//图标的宽度(以像素为单位)。 可接受的值为 16、32 和 64,128。
  BYTE Height;//图标的高度(以像素为单位)。 可接受的值为 16、32 和 64,128。
  BYTE ColorCount;//图标中的颜色数。 可接受的值为 2、8 和 16。
  BYTE reserved;//保留;必须设置为与图标文件标头中保留字段的值相同的值。
} ICONRESDIR;

typedef struct {
  WORD Width;//光标的宽度(以像素为单位)。 可接受的值为 16、32 和 64。
  WORD Height;//光标的高度(以像素为单位)。 可接受的值为 16、32 和 64。
} CURSORDIR;

RESDIR 结构 - Win32 apps | Microsoft Learn

资源结构 (菜单和其他资源) - Win32 apps | Microsoft Learn

由此我们可以分析出ICO的结构是:NEWHEADER+NEWHEADER.ResCount*ICONRESDIR+图片数据

typedef struct {
  WORD Reserved;//保留;必须为零。
  WORD ResType;//RES_ICON=1图标资源类型。RES_CURSOR=2游标资源类型。
  WORD ResCount;//资源组中的图标或游标组件数。
} NEWHEADER, *PNEWHEADER;

typedef struct {
  BYTE Width;//图标的宽度(以像素为单位)。 可接受的值为 16、32 和 64,128。
  BYTE Height;//图标的高度(以像素为单位)。 可接受的值为 16、32 和 64,128。
  BYTE ColorCount;//图标中的颜色数。 可接受的值为 2、8 和 16。
  BYTE reserved;//保留;必须设置为与图标文件标头中保留字段的值相同的值。
  WORD       Planes;//图标或光标位图中的颜色平面数。
  WORD       BitCount;//图标或光标位图中每像素的位数。
  DWORD      BytesInRes;//资源的大小(以字节为单位)。
  DWORD      IconCursorId;//经过测试,发现这是ico中每副图像数据(image)起点位置的偏移,并且是DWORD而不是WORD
} ICONRESDIR;//16字节
#include <Windows.h>
#include <winbase.h>
//icoFilePath:修改的图标
//exeFilePath:修改的程序exe
bool parseIco(QString icoFilePath, std::map<int, ICONRESDIR> &outDataMap, QString exeFilePath)
{
    char szIco[1024] = {0};
    icoFilePath.replace("/", "\\");
    memcpy(szIco, icoFilePath.toLocal8Bit().data(), icoFilePath.toLocal8Bit().size());
    // open the icon file
    // 计算需要的宽字符缓冲区大小
    int nChars = MultiByteToWideChar(CP_ACP, 0, szIco, -1, NULL, 0);

    // 分配足够的空间给宽字符缓冲区
    wchar_t *szPathW = new wchar_t[nChars];
    if (!szPathW) {
        // 处理内存分配失败
        return false;
    }

    // 进行转换
    MultiByteToWideChar(CP_ACP, 0, szIco, -1, szPathW, nChars);

    // 现在可以使用 szPathW 作为 LPCWSTR 参数
    HANDLE hFile = CreateFileW(szPathW, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
    {
        delete[] szPathW;
        qCritical() << "Failed open Icon File! err = " << GetLastError();
        return false;
    }
    // 使用完毕后,记得释放内存
    delete[] szPathW;

    // get the file size
    DWORD dwFileSize = GetFileSize(hFile, NULL);
    unsigned char *filemem = new unsigned char[dwFileSize];
    memset(filemem, 0, dwFileSize);

    // read file to memory
    DWORD dwBytesRead(0);
    ReadFile(hFile, filemem, dwFileSize, &dwBytesRead, NULL);
    CloseHandle(hFile);
 
    NEWHEADER *iconHeader = (NEWHEADER *)(filemem);
    auto *outheader = filemem + sizeof(NEWHEADER);
    outDataMap.clear();

    HANDLE hUpdateRes = nullptr; // update resource handle
    if (!exeFilePath.isEmpty())
    {
        char szExeFile[1024] = {0};
        memcpy(szExeFile, exeFilePath.toStdString().c_str(), exeFilePath.toStdString().size());
        hUpdateRes = BeginUpdateResource(TEXT(szExeFile), FALSE);
        if (hUpdateRes == NULL)
        {
            qCritical() << "BeginUpdateResource File!";
            return false;
        }
    }

    for (int i = 0; i < iconHeader->ResCount; ++i)
    {
        auto outheader_ = (ICONRESDIR *)(outheader + (sizeof(ICONRESDIR) * i));
        outDataMap[i] = *outheader_;
        if (hUpdateRes)
        {
            BOOL result;
            result = UpdateResource(hUpdateRes,                                   // update resource handle
                                    RT_ICON,                                      // change  resource
                                    MAKEINTRESOURCE(i + 1),                       // id  == 0 是 RT_GROUP_ICON 的id,所有全部+1
                                    MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL),    // neutral language
                                    (LPVOID)(filemem + outheader_->IconCursorId), // ptr to resource info
                                    outheader_->BytesInRes);
            if (result == FALSE)
            {
                qCritical() << "UpdateResource File!";
                break;
            }
        }
    }
    if (hUpdateRes)
    {
        // Write changes to FOOT.EXE and then close it.
        if (!EndUpdateResource(hUpdateRes, FALSE))
        {
            qCritical() << "EndUpdateResource File!";
            return false;
        }
    }
    delete[] filemem;
    return true;
}

//调用
std::map<int, ICONRESDIR> outDataMap;
parseIco(".ico",outDataMap,".exe");

修改后如果图标没有立刻发生变化那么需要清理图标缓存:批处理.bat

rem 关闭Windows外壳程序explorer

taskkill /f /im explorer.exe

rem 清理系统图标缓存数据库

attrib -h -s -r "%userprofile%\AppData\Local\IconCache.db"

del /f "%userprofile%\AppData\Local\IconCache.db"

attrib /s /d -h -s -r "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\*"

del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_32.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_96.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_102.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_256.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_1024.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_idx.db"
del /f "%userprofile%\AppData\Local\Microsoft\Windows\Explorer\thumbcache_sr.db"

rem 清理 系统托盘记忆的图标

echo y|reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v IconStreams
echo y|reg delete "HKEY_CLASSES_ROOT\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify" /v PastIconsStream

rem 重启Windows外壳程序explorer

start explorer

ok, 完美解决.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

You can do more

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值