>백엔드 개발 >Golang >golang 프로그램 아이콘 수정

golang 프로그램 아이콘 수정

WBOY
WBOY원래의
2023-05-10 20:37:06604검색

매일 컴퓨터를 사용할 때 일반적으로 사용되는 프로그램을 열어야 하는 경우가 많습니다. 이러한 프로그램은 인터페이스에 특정 아이콘을 표시하므로 신속하게 식별하고 찾을 수 있습니다. 그러나 어떤 경우에는 이러한 프로그램 아이콘을 개인 취향이나 테마와 더욱 일치하도록 변경하는 등의 작업을 원할 수도 있습니다.

이 기사에서는 golang과 일부 시스템 라이브러리를 사용하여 프로그램 아이콘을 변경하는 방법에 중점을 둘 것입니다. 데모 환경으로 Windows를 사용하겠습니다.

먼저 수행해야 할 기본 단계를 간략히 설명하겠습니다.

  1. 프로그램의 리소스 파일(.exe 또는 .dll 파일)을 열고 해당 아이콘 리소스를 찾습니다.
  2. 프로그램의 리소스 파일에 새 아이콘 리소스를 추가합니다.
  3. 새 아이콘 리소스에 액세스할 수 있도록 프로그램의 .manifest 파일을 변경하세요.

다음으로 이러한 단계를 하나씩 완료하는 방법에 대해 논의하겠습니다.

1단계: 리소스 파일을 열고 아이콘 리소스를 찾습니다.

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)을 반복하여 아이콘 리소스의 인덱스를 찾습니다. 일반적으로 인덱싱은 아이콘 리소스의 크기와 형식에 따라 달라집니다. 우리는 우리의 재량에 따라 리소스 파일에서 변경될 아이콘 리소스의 인덱스를 결정할 수 있습니다.

2단계: 리소스 파일에 새 아이콘 리소스 추가

프로그램 리소스 파일에 새 아이콘 리소스를 추가하려면 먼저 ICO 파일 형식으로 저장해야 합니다. golang의 이미지 라이브러리를 사용하여 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
}

3단계: 프로그램의 .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)

위 내용은 golang 프로그램 아이콘 수정의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:golang 환경 빌드 git다음 기사:golang 환경 빌드 git