search
HomeBackend DevelopmentGolangGolang program icon modification

When we use computers daily, we often need to open some commonly used programs. These programs will display a specific icon on our interface so that we can quickly identify and find them. But in some cases, we may want to change these program icons, such as making them more consistent with our personal preferences or themes.

In this article, we will focus on how to use golang and some system libraries to change the icon of the program. We will use Windows as our demonstration environment.

First, let’s outline the basic steps we need to take:

  1. Open the program’s resource file (.exe or .dll file) and find its icon resource.
  2. Add new icon resources to the program's resource file.
  3. Change the program's .manifest file so that it can access the new icon resource.

Next, we will discuss how to complete these steps one by one.

Step one: Open the resource file and find the icon resource

In golang, we can use the function in the system library "syscall" to open and read the file. To do this, we need to define some necessary variables:

package main

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

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

We use several functions in the Windows API here, namely "BeginUpdateResourceW", "UpdateResourceW" and "EndUpdateResourceW". These functions can help us operate the resources in the program resource file.

Next, we need to open the resource file of the program we want to change (can be an .exe or .dll file) and find its icon resource. Here we use a function called "findIconIndex" to traverse the program resource file and find the index number where its icon resource is located.

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")
}

This function iterates through each section (.rsrc) in the program resource file and finds the index of the icon resource. Typically, indexing depends on the size and format of the icon resource. We can decide at our own discretion the index of the icon resource to be changed in the resource file.

Step 2: Add the new icon resource to the resource file

To add the new icon resource to the program resource file, we need to save it in ICO file format first. We can use the image library in golang to create ICO files.

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
}

This function creates an ICO file header and attaches the icon resource to it. The ICO file header contains necessary information about the icon resources in the ICO file.

Next, we write them into resource files. We need to use the "BeginUpdateResource", "UpdateResource" and "EndUpdateResource" functions in the Windows API to do this.

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
}

Step 3: Change the program’s .manifest file

We need to change the program’s .manifest file so that it can access the new icon resource. To do this, we need to add the following to the .manifest file:

<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>

This will assign an icon resource index number to the program so that it can use the new icon resource. We can use the os library in golang to change the .manifest file.

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
}

Now, we already know how to use golang and system libraries to change the icon of the program. Putting these steps together, we build a complete feature. Here is a sample code:

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)

The above is the detailed content of Golang program icon modification. For more information, please follow other related articles on the PHP Chinese website!

Statement
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Golang vs. Python: The Pros and ConsGolang vs. Python: The Pros and ConsApr 21, 2025 am 12:17 AM

Golangisidealforbuildingscalablesystemsduetoitsefficiencyandconcurrency,whilePythonexcelsinquickscriptinganddataanalysisduetoitssimplicityandvastecosystem.Golang'sdesignencouragesclean,readablecodeanditsgoroutinesenableefficientconcurrentoperations,t

Golang and C  : Concurrency vs. Raw SpeedGolang and C : Concurrency vs. Raw SpeedApr 21, 2025 am 12:16 AM

Golang is better than C in concurrency, while C is better than Golang in raw speed. 1) Golang achieves efficient concurrency through goroutine and channel, which is suitable for handling a large number of concurrent tasks. 2)C Through compiler optimization and standard library, it provides high performance close to hardware, suitable for applications that require extreme optimization.

Why Use Golang? Benefits and Advantages ExplainedWhy Use Golang? Benefits and Advantages ExplainedApr 21, 2025 am 12:15 AM

Reasons for choosing Golang include: 1) high concurrency performance, 2) static type system, 3) garbage collection mechanism, 4) rich standard libraries and ecosystems, which make it an ideal choice for developing efficient and reliable software.

Golang vs. C  : Performance and Speed ComparisonGolang vs. C : Performance and Speed ComparisonApr 21, 2025 am 12:13 AM

Golang is suitable for rapid development and concurrent scenarios, and C is suitable for scenarios where extreme performance and low-level control are required. 1) Golang improves performance through garbage collection and concurrency mechanisms, and is suitable for high-concurrency Web service development. 2) C achieves the ultimate performance through manual memory management and compiler optimization, and is suitable for embedded system development.

Is Golang Faster Than C  ? Exploring the LimitsIs Golang Faster Than C ? Exploring the LimitsApr 20, 2025 am 12:19 AM

Golang performs better in compilation time and concurrent processing, while C has more advantages in running speed and memory management. 1.Golang has fast compilation speed and is suitable for rapid development. 2.C runs fast and is suitable for performance-critical applications. 3. Golang is simple and efficient in concurrent processing, suitable for concurrent programming. 4.C Manual memory management provides higher performance, but increases development complexity.

Golang: From Web Services to System ProgrammingGolang: From Web Services to System ProgrammingApr 20, 2025 am 12:18 AM

Golang's application in web services and system programming is mainly reflected in its simplicity, efficiency and concurrency. 1) In web services, Golang supports the creation of high-performance web applications and APIs through powerful HTTP libraries and concurrent processing capabilities. 2) In system programming, Golang uses features close to hardware and compatibility with C language to be suitable for operating system development and embedded systems.

Golang vs. C  : Benchmarks and Real-World PerformanceGolang vs. C : Benchmarks and Real-World PerformanceApr 20, 2025 am 12:18 AM

Golang and C have their own advantages and disadvantages in performance comparison: 1. Golang is suitable for high concurrency and rapid development, but garbage collection may affect performance; 2.C provides higher performance and hardware control, but has high development complexity. When making a choice, you need to consider project requirements and team skills in a comprehensive way.

Golang vs. Python: A Comparative AnalysisGolang vs. Python: A Comparative AnalysisApr 20, 2025 am 12:17 AM

Golang is suitable for high-performance and concurrent programming scenarios, while Python is suitable for rapid development and data processing. 1.Golang emphasizes simplicity and efficiency, and is suitable for back-end services and microservices. 2. Python is known for its concise syntax and rich libraries, suitable for data science and machine learning.

See all articles

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

MantisBT

MantisBT

Mantis is an easy-to-deploy web-based defect tracking tool designed to aid in product defect tracking. It requires PHP, MySQL and a web server. Check out our demo and hosting services.

Dreamweaver Mac version

Dreamweaver Mac version

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

PhpStorm Mac version

PhpStorm Mac version

The latest (2018.2.1) professional PHP integrated development tool

WebStorm Mac version

WebStorm Mac version

Useful JavaScript development tools