How can I save the HICON as file in Go

huangapple go评论75阅读模式
英文:

How can I save the HICON as file in Go

问题

你可以使用SaveIcon函数将HICON保存为位图文件。以下是一个示例代码:

package main

import (
	"log"
	"syscall"
	"unsafe"
)

func MakeIntResource(id uintptr) *uint16 {
	return (*uint16)(unsafe.Pointer(id))
}

const IDI_QUESTION = 32514

func main() {
	user32Dll := syscall.NewLazyDLL("User32.dll")
	procLoadIconW := user32Dll.NewProc("LoadIconW")
	procSaveIcon := user32Dll.NewProc("SaveIcon")

	hIcon, _, _ := syscall.SyscallN(procLoadIconW.Addr(),
		0, uintptr(unsafe.Pointer(MakeIntResource(IDI_QUESTION))),
	)

	filePath := "icon.bmp"
	ret, _, _ := syscall.Syscall(procSaveIcon.Addr(),
		2, uintptr(hIcon), uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(filePath))), 0,
	)
	if ret != 0 {
		log.Println("Icon saved successfully.")
	} else {
		log.Println("Failed to save icon.")
	}
}

这段代码将HICON保存为名为"icon.bmp"的位图文件。你可以根据需要修改文件路径和文件名。

英文:

I can get the HICON with ExtractIconW

for example:

package main

import (
	"log"
	"syscall"
	"unsafe"
)

func MakeIntResource(id uintptr) *uint16 {
	return (*uint16)(unsafe.Pointer(id))
}

const IDI_QUESTION = 32514

func main() {
	user32Dll := syscall.NewLazyDLL("User32.dll")
	procLoadIconW := user32Dll.NewProc("LoadIconW")

	hIcon, _, _ := syscall.SyscallN(procLoadIconW.Addr(),
		0, uintptr(unsafe.Pointer(MakeIntResource(IDI_QUESTION))),
	)
	log.Println(hIcon)
}

But I don't know what I should do next to save HICON as a file (bitmap format is enough).

答案1

得分: 4

一个真正的.ico文件通常包含多个不同大小的图像,而HICON只是一个单独的图像。

如果目标是复制原始图标,那么你必须使用LoadLibraryEx作为数据文件,然后使用资源函数首先找到RT_GROUP_ICON,一旦找到了,你就知道了RT_ICON的id,然后你可以提取子图像并编写你的.ico文件。

如果你仍然认为你想保存一个HICON,请调用GetIconInfo来获取图像。

没有低级别的Windows函数可以写入.ico文件。你可以尝试使用GDI+或WIC,或者自己编写。该文件格式与资源格式非常接近(存储文件偏移量而不是资源id)。

有关Windows图标格式的指南可以从这里开始...

英文:

A real .ico file usually contains multiple images of different sizes while a HICON is just a single image.

If the goal is to copy the original icon then you must LoadLibraryEx as a datafile and then use the resource functions to first find the RT_GROUP_ICON and once you have that you know the RT_ICON ids and you can extract the sub-images and write your .ico.

If you still think you want to save a HICON, call GetIconInfo to get the image.

There are no low-level Windows functions to write .ico files. You can try GDI+ or WIC or write them yourself. The file format is very close to the resource format (stores a file offset instead of a resource id).

A guide to Windows icon formats starts here...

答案2

得分: 1

以下是用Go语言编写的示例代码,它将资源保存为位图文件:

// 步骤:
// 获取HICON
// 从HICON获取ICONINFO
// 从ICONINFO获取位图
// 将位图保存到文件
// 注意:为了使示例看起来简单,我忽略了所有可能的错误处理。
func Example_saveFileIconAsBitmap() {
	user32dll := w32.NewUser32DLL()
	kernel32dll := w32.NewKernel32DLL()
	gdi32dll := w32.NewGdi32DLL()

	var hIcon w32.HICON
	{
		// 因为我已经知道我想要的图标ID(myResourceID),所以可以直接加载它。
		// 如果你想在搜索RT_GROUP_ICON后找到合适的图标ID,可以参考这个示例:
		// https://github.com/CarsonSlovoka/go-pkg/blob/34e5d2c1fc97bf149bf626acaaf8773fe1509d64/v2/w32/kernel32_func_test.go#L331-L353

		hmExe := kernel32dll.LoadLibrary("./testdata/exe/writeWithFont.exe") // writeWithFont.exe在这里:https://github.com/CarsonSlovoka/go-pkg/tree/983a2c1/v2/w32/testdata/exe
		myResourceID := uintptr(1)                                           // 你可以使用resourceHacker.exe来帮助你找到ID。
		hRes, _ := kernel32dll.FindResource(hmExe,
			w32.MakeIntResource(myResourceID),
			w32.MakeIntResource(w32.RT_ICON),
		)
		hMem, _ := kernel32dll.LoadResource(hmExe, hRes)
		lpResource := kernel32dll.LockResource(hMem)

		hIcon = user32dll.CreateIconFromResourceEx(lpResource,
			kernel32dll.MustSizeofResource(hmExe, hRes), true, 0x00030000,
			32, 32, // 尺寸 X, Y
			w32.LR_DEFAULTCOLOR,
		)
	}

	var iInfo w32.ICONINFO
	{
		if !user32dll.GetIconInfo(hIcon, &iInfo) {
			return
		}
		// 当你不再使用HBITMAP时记得释放它。
		defer func() {
			_ = gdi32dll.DeleteObject(w32.HGDIOBJ(iInfo.HbmColor))
			_ = gdi32dll.DeleteObject(w32.HGDIOBJ(iInfo.HbmMask))
		}()
	}

	bmp := w32.Bitmap{}
	{
		// 通过ICONINFO创建BITMAP
		gdi32dll.GetObject(w32.HANDLE(iInfo.HbmColor), int32(unsafe.Sizeof(bmp)), uintptr(unsafe.Pointer(&bmp)))
	}

	// 将位图保存到文件。
	var (
		bitmapFileHeader w32.BitmapFileHeader // https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header
		bitmapInfoHeader w32.BitmapInfoHeader // https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_(bitmap_information_header)
	)
	{
		bitmapInfoHeader = w32.BitmapInfoHeader{
			Size:        uint32(unsafe.Sizeof(bitmapInfoHeader)), // 40
			Width:       bmp.Width,
			Height:      bmp.Height,
			Planes:      1,
			BitCount:    32,
			Compression: w32.BI_RGB,
		}
		bmpSize := ((bmp.Width*int32(bitmapInfoHeader.BitCount) + 31) / 32) * 4 /* uint32 */ * bmp.Height // 参考维基百科:https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage

		sizeofDIB := 14 + uint32(unsafe.Sizeof(bitmapInfoHeader)) + uint32(bmpSize)
		bitmapFileHeader = w32.BitmapFileHeader{
			Type:       0x4D42, // BM。// B: 42, M: 4D // 所有整数值都以小端格式存储
			Size:       sizeofDIB, // HEADER + INFO + DATA
			OffsetBits: 14 + uint32(unsafe.Sizeof(bitmapInfoHeader)),
		}

		hdc := user32dll.GetDC(0)

		var lpBitmap w32.LPVOID
		hDIB, _ := kernel32dll.GlobalAlloc(w32.GHND, w32.SIZE_T(bmpSize))
		lpBitmap, _ = kernel32dll.GlobalLock(hDIB)
		defer func() {
			kernel32dll.GlobalUnlock(hDIB)
			kernel32dll.GlobalFree(hDIB)
		}()
		gdi32dll.GetDIBits(
			hdc, iInfo.HbmColor,
			0,
			w32.UINT(bmp.Height),
			lpBitmap, // [out]
			&w32.BitmapInfo{Header: bitmapInfoHeader},
			w32.DIB_RGB_COLORS,
		)
		outputBmpPath := "testdata/temp001.bmp"
		// 写入:FileHeader、DIPHeader、位图数据
		{
			f, _ := os.Create(outputBmpPath)
			defer func() {
				_ = os.Remove(outputBmpPath) // 删除测试数据。如果你想看到结果,删除这行代码以查看最终数据。
			}()

			// FileHeader
			_ = binary.Write(f, binary.LittleEndian, bitmapFileHeader)

			// DIP Header
			_ = binary.Write(f, binary.LittleEndian, bitmapInfoHeader)

			// 位图数据
			bmpDatas := make([]byte, sizeofDIB)
			var offset uint32 = 0
			for offset = 0; offset < sizeofDIB; offset += 1 {
				curByteAddr := unsafe.Pointer(uintptr(lpBitmap) + uintptr(offset))
				bmpDatas[offset] = *(*byte)(curByteAddr)
			}
			_ = binary.Write(f, binary.LittleEndian, bmpDatas)

			_ = f.Close()
		}
	}
	// 输出:
}

以上代码是第三方库的一部分,但没有使用其他第三方资源。

由于WinAPI有太多的常量、变量和类型,并且DLL的声明也非常繁琐,因此它不再作为单个文件编写。
如果你有兴趣,可以下载这个库。它的打包非常简单。如果你不喜欢使用其他第三方库,可以捕获相关变量。

英文:

The following code is an example which writing by go, and it saves one of the resources to a bitmap file.

// Step:
// get HICON
// get ICONINFO from HICON
// get Bitmap from ICONINFO
// save Bitmap to a File
// Note: To make the example look simple, I ignore all possible errors handling.
func Example_saveFileIconAsBitmap() {
	user32dll := w32.NewUser32DLL()
	kernel32dll := w32.NewKernel32DLL()
	gdi32dll := w32.NewGdi32DLL()

	var hIcon w32.HICON
	{
		// Because I already know the iconID(myResourceID) I want, I can load it directly.
		// If you want to find the appropriate iconID after searching through RT_GROUP_ICON you can refer to this example:
		// https://github.com/CarsonSlovoka/go-pkg/blob/34e5d2c1fc97bf149bf626acaaf8773fe1509d64/v2/w32/kernel32_func_test.go#L331-L353

		hmExe := kernel32dll.LoadLibrary(&quot;./testdata/exe/writeWithFont.exe&quot;) // writeWithFont.exe is in here: https://github.com/CarsonSlovoka/go-pkg/tree/983a2c1/v2/w32/testdata/exe
		myResourceID := uintptr(1)                                           // You can use resourceHacker.exe to help you find the ID.
		hRes, _ := kernel32dll.FindResource(hmExe,
			w32.MakeIntResource(myResourceID),
			w32.MakeIntResource(w32.RT_ICON),
		)
		hMem, _ := kernel32dll.LoadResource(hmExe, hRes)
		lpResource := kernel32dll.LockResource(hMem)

		hIcon = user32dll.CreateIconFromResourceEx(lpResource,
			kernel32dll.MustSizeofResource(hmExe, hRes), true, 0x00030000,
			32, 32, // size X, Y
			w32.LR_DEFAULTCOLOR,
		)
	}

	var iInfo w32.ICONINFO
	{
		if !user32dll.GetIconInfo(hIcon, &amp;iInfo) {
			return
		}
		// Remember to release when you are not using the HBITMAP.
		defer func() {
			_ = gdi32dll.DeleteObject(w32.HGDIOBJ(iInfo.HbmColor))
			_ = gdi32dll.DeleteObject(w32.HGDIOBJ(iInfo.HbmMask))
		}()
	}

	bmp := w32.Bitmap{}
	{
		// Create BITMAP by ICONINFO
		gdi32dll.GetObject(w32.HANDLE(iInfo.HbmColor), int32(unsafe.Sizeof(bmp)), uintptr(unsafe.Pointer(&amp;bmp)))
	}

	// Save Bitmap to a file.
	var (
		bitmapFileHeader w32.BitmapFileHeader // https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header
		bitmapInfoHeader w32.BitmapInfoHeader // https://en.wikipedia.org/wiki/BMP_file_format#DIB_header_(bitmap_information_header)
	)
	{
		bitmapInfoHeader = w32.BitmapInfoHeader{
			Size:  uint32(unsafe.Sizeof(bitmapInfoHeader)), // 40
			Width: bmp.Width, Height: bmp.Height,
			Planes:      1,
			BitCount:    32,
			Compression: w32.BI_RGB,
		}
		bmpSize := ((bmp.Width*int32(bitmapInfoHeader.BitCount) + 31) / 32) * 4 /* uint32 */ * bmp.Height // see the wiki: https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage

		sizeofDIB := 14 + uint32(unsafe.Sizeof(bitmapInfoHeader)) + uint32(bmpSize)
		bitmapFileHeader = w32.BitmapFileHeader{
			Type:       0x4D42,    // BM. // B: 42, M: 4D  //  All of the integer values are stored in little-endian format
			Size:       sizeofDIB, // HEADER + INFO + DATA
			OffsetBits: 14 + uint32(unsafe.Sizeof(bitmapInfoHeader)),
		}

		hdc := user32dll.GetDC(0)

		var lpBitmap w32.LPVOID
		hDIB, _ := kernel32dll.GlobalAlloc(w32.GHND, w32.SIZE_T(bmpSize))
		lpBitmap, _ = kernel32dll.GlobalLock(hDIB)
		defer func() {
			kernel32dll.GlobalUnlock(hDIB)
			kernel32dll.GlobalFree(hDIB)
		}()
		gdi32dll.GetDIBits(
			hdc, iInfo.HbmColor,
			0,
			w32.UINT(bmp.Height),
			lpBitmap, // [out]
			&amp;w32.BitmapInfo{Header: bitmapInfoHeader},
			w32.DIB_RGB_COLORS,
		)
		outputBmpPath := &quot;testdata/temp001.bmp&quot;
		// Write: FileHeader, DIPHeader, bitmapData
		{
			f, _ := os.Create(outputBmpPath)
			defer func() {
				_ = os.Remove(outputBmpPath) // Remove test data. If you want to see the result, delete this line to see the final data.
			}()

			// FileHeader
			_ = binary.Write(f, binary.LittleEndian, bitmapFileHeader)

			// DIP Header
			_ = binary.Write(f, binary.LittleEndian, bitmapInfoHeader)

			// bitmapData
			bmpDatas := make([]byte, sizeofDIB)
			var offset uint32 = 0
			for offset = 0; offset &lt; sizeofDIB; offset += 1 {
				curByteAddr := unsafe.Pointer(uintptr(lpBitmap) + uintptr(offset))
				bmpDatas[offset] = *(*byte)(curByteAddr)
			}
			_ = binary.Write(f, binary.LittleEndian, bmpDatas)

			_ = f.Close()
		}
	}
	// Output:
}

Above code is part of a third-party library, but no other third-party resources are used.

It is no longer written as a single file because winapi has too many constants, variables, and types, and the declaration of the DLL is also very tedious.
If you are interested, you can download this library back. Its packaging is very simple. If you do not like to use other third parties to capture the relevant variables can be done.


The following pictures will help you understand the meaning of the parameters of MAKEINTRESOURCE more easily.

How can I save the HICON as file in Go

huangapple
  • 本文由 发表于 2022年10月20日 19:16:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/74138968.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定