0

0

golang程序图标修改

WBOY

WBOY

发布时间:2023-05-10 20:37:06

|

511人浏览过

|

来源于php中文网

原创

在我们日常使用电脑时,我们常常需要打开一些常用的程序。这些程序会在我们的界面上展示出一个特定的图标,以便我们快速地识别和找到它们。但是在某些情况下,我们可能会希望更改这些程序图标,例如让它们更符合自己的个人喜好或主题。

在本篇文章中,我们将着重介绍如何使用golang和一些系统库来更改程序的图标。我们将使用Windows作为我们的演示环境。

首先,让我们概述一下我们需要进行的基本步骤:

  1. 打开程序的资源文件(.exe或.dll文件)并找到它的图标资源。
  2. 将新的图标资源添加到程序的资源文件中。
  3. 更改程序的.manifest文件,以便它可以访问新的图标资源。

接下来,我们将逐一讨论如何完成这些步骤。

第一步:打开资源文件并找到图标资源

立即学习go语言免费学习笔记(深入)”;

在golang中,我们可以使用系统库“syscall”中的函数来打开和读取文件。为此,我们需要定义一些必要的变量:

package main

import (
    "os"
    "syscall"
    "unsafe"
)

var (
    kernel32DLL                             = syscall.MustLoadDLL("kernel32.dll")
    BeginUpdateResourceProc     = kernel32DLL.MustFindProc("BeginUpdateResourceW")
    UpdateResourceProc              = kernel32DLL.MustFindProc("UpdateResourceW")
    EndUpdateResourceProc       = kernel32DLL.MustFindProc("EndUpdateResourceW")
)

我们这里使用了Windows API中的几个函数,分别是“BeginUpdateResourceW”、“UpdateResourceW”和“EndUpdateResourceW”。这些函数可以帮助我们操作程序资源文件中的资源。

接下来,我们需要打开要更改的程序的资源文件(可以是.exe或.dll文件),并找到其图标资源。这里我们使用了一个名为“findIconIndex”的函数来遍历程序资源文件,找到其图标资源所在的索引号。

func findIconIndex(exePath string) (int, error) {
    exeFile, err := os.OpenFile(exePath, os.O_RDWR, 0666)
    defer exeFile.Close()
    if err != nil {
        return 0, err
    }
    exeStat, err := exeFile.Stat()
    if err != nil {
        return 0, err
    }
    exeSize := exeStat.Size()

    // DOS header
    dosHeader := new(image.DosHeader)
    err = binary.Read(exeFile, binary.LittleEndian, dosHeader)
    if err != nil {
        return 0, err
    }
    exeFile.Seek(int64(dosHeader.Lfanew), 0)

    // File header and optional header
    fileHeader := new(image.FileHeader)
    err = binary.Read(exeFile, binary.LittleEndian, fileHeader)
    if err != nil {
        return 0, err
    }
    extHeader := make([]byte, fileHeader.SizeOfOptionalHeader-2)
    exeFile.Read(extHeader)

    // Section headers
    sections := make([]image.SectionHeader, fileHeader.NumberOfSections)
    err = binary.Read(exeFile, binary.LittleEndian, sections)
    if err != nil {
        return 0, err
    }

    // Find icon resource
    for _, section := range sections {
        if section.Name == ".rsrc" {
            exeFile.Seek(int64(section.Offset), 0)
            resourceHeader := new(resourceHeader)
            err = binary.Read(exeFile, binary.LittleEndian, resourceHeader)
            if err != nil {
                return 0, err
            }
            stack := []resourceDirectoryEntry{resourceDirectoryEntry{uint32(resourceHeader.RootID), int64(resourceHeader.OffsetToDirectory)}}
            for len(stack) > 0 {
                currentEntry := stack[len(stack)-1]
                stack = stack[:len(stack)-1]
                exeFile.Seek(currentEntry.offset, 0)
                directoryHeader := new(resourceDirectoryHeader)
                err = binary.Read(exeFile, binary.LittleEndian, directoryHeader)
                if err != nil {
                    return 0, err
                }
                entries := make([]resourceDirectoryEntry, directoryHeader.NumNamedEntries+directoryHeader.NumIDEntries)
                for i := range entries {
                    err = binary.Read(exeFile, binary.LittleEndian, &entries[i])
                    if err != nil {
                        return 0, err
                    }
                    if entries[i].nameIsString {
                        nameBytes := make([]byte, entries[i].nameOffset&0x7FFFFFFF)
                        exeFile.Read(nameBytes)
                        entries[i].name = syscall.UTF16ToString(nameBytes)
                    }
                }
                for _, entry := range entries {
                    if entry.ID&^0xFFFF == rtIcon {
                        return int(entry.ID & 0xFFFF), nil
                    } else if entry.name == "ICON" {
                        stack = append(stack, resourceDirectoryEntry{entry.ID, int64(entry.offset)})
                    } else if entry.name == "#0" && entry.ID&^0xFFFF == rtGroupIcon {
                        groupIconDirHeader := new(resourceGroupIconDirectoryHeader)
                        exeFile.Seek(int64(entry.offset), 0)
                        err = binary.Read(exeFile, binary.LittleEndian, groupIconDirHeader)
                        if err != nil {
                            return 0, err
                        }
                        var largestIcon resourceGroupIconDirectoryEntry
                        for i := 0; i < int(groupIconDirHeader.Count); i++ {
                            groupIconDirEntry := new(resourceGroupIconDirectoryEntry)
                            err = binary.Read(exeFile, binary.LittleEndian, groupIconDirEntry)
                            if err != nil {
                                return 0, err
                            }
                            if groupIconDirEntry.Width > largestIcon.Width || groupIconDirEntry.Height > largestIcon.Height {
                                largestIcon = *groupIconDirEntry
                            }
                        }
                        return int(largestIcon.ID), nil
                    } else if entry.name == "ICONGROUP" {
                        stack = append(stack, resourceDirectoryEntry{entry.ID, int64(entry.offset)})
                    } else if entry.name == "MAINICON" {
                        stack = append(stack, resourceDirectoryEntry{entry.ID, int64(entry.offset)})
                    } else {
                        stack = append(stack, resourceDirectoryEntry{entry.ID, int64(entry.offset)})
                    }
                }
            }
            return 0, fmt.Errorf("Icon not found")
        }
    }
    return 0, fmt.Errorf("Resource not found")
}

此函数遍历程序资源文件中的每个节(.rsrc)并找到图标资源的索引。通常情况下,索引取决于图标资源的大小和格式。我们可以自行决定要更改的图标资源在资源文件中的索引。

聚好用AI
聚好用AI

可免费AI绘图、AI音乐、AI视频创作,聚集全球顶级AI,一站式创意平台

下载

第二步:将新的图标资源添加到资源文件中

要将新的图标资源添加到程序资源文件中,我们需要先将其保存为ICO文件格式。我们可以使用golang中的image库来创建ICO文件。

package main

import (
    "image"
    "image/draw"
    "image/png"
    "os"
)

func writeIcoFile(icon image.Image, filename string) error {
    file, err := os.Create(filename)
    if err != nil {
        return err
    }
    defer file.Close()

    // Create icon file header
    iconSize := icon.Bounds().Size()
    fileHeader := new(resourceIconFileHeader)
    fileHeader.Reserved = 0
    fileHeader.Type = 1
    fileHeader.Count = 1

    // Create icon directory entry
    dirEntry := new(resourceIconDirectoryEntry)
    dirEntry.Width = uint8(iconSize.X)
    dirEntry.Height = uint8(iconSize.Y)
    dirEntry.Colors = 0
    dirEntry.Reserved = 0
    dirEntry.Plane = 1
    dirEntry.BitCount = 32
    dirEntry.SizeInBytes = uint32(40 + 4*iconSize.X*iconSize.Y)
    dirEntry.Offset = 22

    // Create bitmap info header and color mask for bitmap graphics
    colorMask := [12]byte{0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0xFF, 0x00, 0x00}
    infoHeader := new(bitmapInfoHeader)
    infoHeader.Size = 40
    infoHeader.Width = int32(iconSize.X)
    infoHeader.Height = int32(2 * iconSize.Y)
    infoHeader.Planes = 1
    infoHeader.BitCount = 32
    infoHeader.Compression = 0
    infoHeader.SizeImage = uint32(4 * iconSize.X * iconSize.Y)
    infoHeader.XPelsPerMeter = 0
    infoHeader.YPelsPerMeter = 0
    infoHeader.ClrUsed = 0
    infoHeader.ClrImportant = 0

    // Write icon file header, directory entry, bitmap info header and color mask
    binary.Write(file, binary.LittleEndian, fileHeader)
    binary.Write(file, binary.LittleEndian, dirEntry)
    binary.Write(file, binary.LittleEndian, infoHeader)
    binary.Write(file, binary.LittleEndian, colorMask)

    // Write bitmap graphics
    rgba := image.NewRGBA(image.Rect(0, 0, iconSize.X, 2*iconSize.Y))
    draw.Draw(rgba, rgba.Bounds(), image.Black, image.ZP, draw.Src)
    draw.Draw(rgba, image.Rect(0, 0, iconSize.X, iconSize.Y), icon, image.ZP, draw.Over)
    draw.Draw(rgba, image.Rect(0, iconSize.Y, iconSize.X, 2*iconSize.Y), image.Transparent, image.ZP, draw.Src)
    err = png.Encode(file, rgba)
    if err != nil {
        return err
    }

    return nil
}

这个函数创建了一个ICO文件头,并将图标资源附加到其中。ICO文件头包含有关ICO文件中的图标资源的必要信息。

接下来,我们将它们写入资源文件中。我们需要使用Windows API中的“BeginUpdateResource”、“UpdateResource”和“EndUpdateResource”函数来执行此操作。

func updateIcon(exePath, icoPath string, iconIndex int) error {
    exeFile, err := os.OpenFile(exePath, os.O_RDWR, 0666)
    defer exeFile.Close()
    if err != nil {
        return err
    }

    icoFile, err := os.Open(icoPath)
    defer icoFile.Close()
    if err != nil {
        return err
    }

    // Read ICO file and prepare icon directory entry
    icoData, err := ioutil.ReadAll(icoFile)
    if err != nil {
        return err
    }
    dirEntry := new(resourceIconDirectoryEntry)
    dirEntry.Width = 0
    dirEntry.Height = 0
    dirEntry.Colors = 0
    dirEntry.Reserved = 0
    dirEntry.Plane = 1
    dirEntry.BitCount = 0
    dirEntry.SizeInBytes = uint32(len(icoData))
    dirEntry.Offset = 22

    // Find update handle
    exeHandle, err := syscall.CreateFile(syscall.StringToUTF16Ptr(exePath), syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
    if err != nil {
        return err
    }
    defer syscall.CloseHandle(exeHandle)
    updateHandle, _, err := BeginUpdateResourceProc.Call(uintptr(exeHandle), 0)
    defer syscall.CloseHandle(syscall.Handle(updateHandle))
    if updateHandle == 0 {
        return fmt.Errorf("BeginUpdateResourceW failed")
    }

    // Write resource to update handle
    success, _, err := UpdateResourceProc.Call(uintptr(updateHandle), uintptr(rtIcon), uintptr(iconIndex), 0, uintptr(unsafe.Pointer(&icoData[0])), uintptr(len(icoData)))
    if success == 0 {
        return fmt.Errorf("UpdateResourceW failed")
    }

    // Write updated icon directory entry
    success, _, err = UpdateResourceProc.Call(uintptr(updateHandle), uintptr(rtGroupIcon), uintptr(MAKEINTRESOURCE(iconIndex)), 0, uintptr(unsafe.Pointer(dirEntry)), uintptr(unsafe.Sizeof(*dirEntry)))
    if success == 0 {
        return fmt.Errorf("UpdateResourceW failed")
    }

    // Commit update handle
    success, _, err = EndUpdateResourceProc.Call(updateHandle, 0)
    if success == 0 {
        return fmt.Errorf("EndUpdateResourceW failed")
    }

    return nil
}

第三步:更改程序的.manifest文件

我们需要更改程序的.manifest文件,以便它可以访问新的图标资源。为此,我们需要在.manifest文件中添加以下内容:

<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity type="win32" name="MyApplication" version="1.0.0.0" processorArchitecture="x86" />
  <icon type="group">MyIconResourceIndex</icon>
</assembly>

这将为程序指定一个图标资源索引号,以便它可以使用新的图标资源。我们可以使用golang中的os库来更改.manifest文件。

func updateManifest(manifestPath string, iconIndex int) error {
    manifestData, err := ioutil.ReadFile(manifestPath)
    if err != nil {
        return err
    }

    updatedManifest := strings.Replace(string(manifestData), "</assembly>", "  <icon type="group">"+strconv.Itoa(iconIndex)+"</icon>
</assembly>", 1)

    err = ioutil.WriteFile(manifestPath, []byte(updatedManifest), 0666)
    if err != nil {
        return err
    }

    return nil
}

现在,我们已经知道了如何使用golang和系统库来更改程序的图标。将这些步骤组合在一起,我们构建了一个完整的功能。下面是一个示例代码:

package main

import (
    "encoding/binary"
    "encoding/hex"
    "fmt"
    "image"
    "image/png"
    "io/ioutil"
    "os"
    "strconv"
    "strings"
    "syscall"
    "unsafe"
)

const (
    rtIcon        = 14
    rtGroupIcon   = rtIcon + 11
    LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020
)

// Resource header
type resourceHeader struct {
    RootID        uint16
    RootType      uint16
    RootName      [16]uint16
    RootDataSize  uint32
    RootDataVer   uint32
    RootDate      uint32
    RootRMLow     uint16
    RootRMHigh    uint16
    RootLangID    uint16
    RootDataVerOS uint32
}

// Resource directory header
type resourceDirectoryHeader struct {
    Characteristics uint32
    TimeDateStamp   uint32
    VersionMajor    uint16
    VersionMinor    uint16
    NumNamedEntries uint16
    NumIDEntries    uint16
}

// Resource directory entry
type resourceDirectoryEntry struct {
    nameIsString bool
    ID           uint32
    offset       uint32
    nameOffset   uint32
    name         string
}

// Resource icon file header
type resourceIconFileHeader struct {
    Reserved uint16
    Type     uint16
    Count    uint16
}

// Resource icon directory entry
type resourceIconDirectoryEntry struct {
    Width       uint8
    Height      uint8
    Colors      uint8
    Reserved    uint8
    Plane       uint16
    BitCount    uint16
    SizeInBytes uint32
    Offset      uint32
}

// Resource group icon directory header
type resourceGroupIconDirectoryHeader struct {
    Width    uint16
    Height   uint16
    ColorCount uint16
    Reserved uint16
    Planes   uint16
    BitCount uint16
    Count    uint32
}

// Resource group icon directory entry
type resourceGroupIconDirectoryEntry struct {
    Width      uint8
    Height     uint8
    ColorCount uint8
    Reserved   uint8
    Planes     uint16
    BitCount   uint16
    BytesInRes uint32
    ID         uint16
}

// Bitmap header
type bitmapInfoHeader struct {
    Size          uint32
    Width         int32
    Height        int32
    Planes        uint16
    BitCount      uint16
    Compression   uint32
    SizeImage     uint32
    XPelsPerMeter int32
    YPelsPerMeter int32
    ClrUsed       uint32
    ClrImportant  uint32
}

var (
    kernel32DLL                = syscall.MustLoadDLL("kernel32.dll")
    user32DLL                  = syscall.MustLoadDLL("user32.dll")
    autoDetectEncodingProc     = kernel32DLL.MustFindProc("AutoDetectEncoding")
    BeginUpdateResourceProc    = kernel32DLL.MustFindProc("BeginUpdateResourceW")
    LoadImageProc              = user32DLL.MustFindProc("LoadImageW")
    ResourceNotFound         = fmt.Errorf("Resource not found")
    NoIconFound             = fmt.Errorf("Icon not found")
)

func main() {
    exePath := "path/to/program.exe"
    icoPath := "path/to/newicon.png"
    manifestPath := "path/to/program.exe.manifest"
    iconIndex, err := findIconIndex(exePath)

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

WorkBuddy
WorkBuddy

腾讯云推出的AI原生桌面智能体工作台

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
TypeScript类型系统进阶与大型前端项目实践
TypeScript类型系统进阶与大型前端项目实践

本专题围绕 TypeScript 在大型前端项目中的应用展开,深入讲解类型系统设计与工程化开发方法。内容包括泛型与高级类型、类型推断机制、声明文件编写、模块化结构设计以及代码规范管理。通过真实项目案例分析,帮助开发者构建类型安全、结构清晰、易维护的前端工程体系,提高团队协作效率与代码质量。

49

2026.03.13

Python异步编程与Asyncio高并发应用实践
Python异步编程与Asyncio高并发应用实践

本专题围绕 Python 异步编程模型展开,深入讲解 Asyncio 框架的核心原理与应用实践。内容包括事件循环机制、协程任务调度、异步 IO 处理以及并发任务管理策略。通过构建高并发网络请求与异步数据处理案例,帮助开发者掌握 Python 在高并发场景中的高效开发方法,并提升系统资源利用率与整体运行性能。

89

2026.03.12

C# ASP.NET Core微服务架构与API网关实践
C# ASP.NET Core微服务架构与API网关实践

本专题围绕 C# 在现代后端架构中的微服务实践展开,系统讲解基于 ASP.NET Core 构建可扩展服务体系的核心方法。内容涵盖服务拆分策略、RESTful API 设计、服务间通信、API 网关统一入口管理以及服务治理机制。通过真实项目案例,帮助开发者掌握构建高可用微服务系统的关键技术,提高系统的可扩展性与维护效率。

276

2026.03.11

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

59

2026.03.10

Kotlin Android模块化架构与组件化开发实践
Kotlin Android模块化架构与组件化开发实践

本专题围绕 Kotlin 在 Android 应用开发中的架构实践展开,重点讲解模块化设计与组件化开发的实现思路。内容包括项目模块拆分策略、公共组件封装、依赖管理优化、路由通信机制以及大型项目的工程化管理方法。通过真实项目案例分析,帮助开发者构建结构清晰、易扩展且维护成本低的 Android 应用架构体系,提升团队协作效率与项目迭代速度。

99

2026.03.09

JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

105

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

230

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

619

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

173

2026.03.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号