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:
- Open the program’s resource file (.exe or .dll file) and find its icon resource.
- Add new icon resources to the program's resource file.
- 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!

Golangisidealforbuildingscalablesystemsduetoitsefficiencyandconcurrency,whilePythonexcelsinquickscriptinganddataanalysisduetoitssimplicityandvastecosystem.Golang'sdesignencouragesclean,readablecodeanditsgoroutinesenableefficientconcurrentoperations,t

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.

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

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


Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

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

Hot Article

Hot Tools

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
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

PhpStorm Mac version
The latest (2018.2.1) professional PHP integrated development tool

WebStorm Mac version
Useful JavaScript development tools