ホームページ  >  記事  >  バックエンド開発  >  Golangプログラムアイコンの変更

Golangプログラムアイコンの変更

WBOY
WBOYオリジナル
2023-05-10 20:37:06566ブラウズ

私たちが毎日コンピューターを使用するとき、よく使用されるプログラムを開く必要があることがよくあります。これらのプログラムはインターフェイス上に特定のアイコンを表示するため、すぐに識別して見つけることができます。ただし、場合によっては、個人の好みやテーマとの一貫性を高めるなど、これらのプログラム アイコンを変更したい場合があります。

この記事では、golang といくつかのシステム ライブラリを使用してプログラムのアイコンを変更する方法に焦点を当てます。デモ環境として Windows を使用します。

まず、実行する必要がある基本的な手順の概要を説明します。

  1. プログラムのリソース ファイル (.exe または .dll ファイル) を開いて、そのアイコン リソースを見つけます。
  2. 新しいアイコン リソースをプログラムのリソース ファイルに追加します。
  3. プログラムの .manifest ファイルを変更して、新しいアイコン リソースにアクセスできるようにします。

次に、これらの手順を 1 つずつ完了する方法について説明します。

ステップ 1: リソース ファイルを開いてアイコン resource を見つけます

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 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。