매일 컴퓨터를 사용할 때 일반적으로 사용되는 프로그램을 열어야 하는 경우가 많습니다. 이러한 프로그램은 인터페이스에 특정 아이콘을 표시하므로 신속하게 식별하고 찾을 수 있습니다. 그러나 어떤 경우에는 이러한 프로그램 아이콘을 개인 취향이나 테마와 더욱 일치하도록 변경하는 등의 작업을 원할 수도 있습니다.
이 기사에서는 golang과 일부 시스템 라이브러리를 사용하여 프로그램 아이콘을 변경하는 방법에 중점을 둘 것입니다. 데모 환경으로 Windows를 사용하겠습니다.
먼저 수행해야 할 기본 단계를 간략히 설명하겠습니다.
다음으로 이러한 단계를 하나씩 완료하는 방법에 대해 논의하겠습니다.
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 중국어 웹사이트의 기타 관련 기사를 참조하세요!