How to convert HICON to png?

hello everyone.

I want to extract the icon in the application and convert it to png format.

At present, I can get HICON through the SHGetFileInfoW method of winapi

And get ICONINFO from HICON through GetIconInfo, but I don't know how to continue to convert to png format.

Please give me some advice, thank you!

Do you read your HICON from an .ico file? I don't know Windows, sorry. But if that is the case, you can use the image crate for converting a .ico file to .png, as it supports both formats.

Hello, I didn't extract the HICON from .ico file, but extracted the HICON from exe or dll through SHGetFileInfoW

anybody konws how to do it?

Probably using DrawIcon with a drawing context that lets you get the image data.

You can use GetIconInfo() to get the HBITMAP for the icon, and potentially it's mask (iirc only if it's paletted)

You can then use GetDIBits() to get the raw bits and metadata of the HBITMAP(s).

With that raw data, you can probably most easily use the image crate's flat module with a bit of work to get an image you can then easily save as PNG as in the module example.

Here's a complete example of using GetIconInfo() and GetDIBits() to extract the image data as an RgbaImage that can be saved as a PNG file:

/*
[dependencies]
image = "0.24.5"

[dependencies.windows]
version = "0.46.0"
features = [
    "Win32_Foundation",
    "Win32_Graphics_Gdi",
    "Win32_UI_WindowsAndMessaging",
]
*/

use image::RgbaImage;
use std::{
    mem::{self, MaybeUninit},
    ptr::addr_of_mut,
};
use windows::Win32::{
    Foundation::HWND,
    Graphics::Gdi::{
        DeleteObject, GetDC, GetDIBits, GetObjectW, ReleaseDC, BITMAP, BITMAPINFOHEADER, BI_RGB,
        DIB_RGB_COLORS, HDC,
    },
    UI::WindowsAndMessaging::{GetIconInfo, HICON},
};

unsafe fn icon_to_image(icon: HICON) -> RgbaImage {
    let bitmap_size_i32 = i32::try_from(mem::size_of::<BITMAP>()).unwrap();
    let biheader_size_u32 = u32::try_from(mem::size_of::<BITMAPINFOHEADER>()).unwrap();

    let mut info = MaybeUninit::uninit();
    GetIconInfo(icon, info.as_mut_ptr()).unwrap();
    let info = info.assume_init_ref();
    DeleteObject(info.hbmMask).unwrap();

    let mut bitmap: MaybeUninit<BITMAP> = MaybeUninit::uninit();
    let result = GetObjectW(
        info.hbmColor,
        bitmap_size_i32,
        Some(bitmap.as_mut_ptr().cast()),
    );
    assert!(result == bitmap_size_i32);
    let bitmap = bitmap.assume_init_ref();

    let width_u32 = u32::try_from(bitmap.bmWidth).unwrap();
    let height_u32 = u32::try_from(bitmap.bmHeight).unwrap();
    let width_usize = usize::try_from(bitmap.bmWidth).unwrap();
    let height_usize = usize::try_from(bitmap.bmHeight).unwrap();
    let buf_size = width_usize
        .checked_mul(height_usize)
        .and_then(|size| size.checked_mul(4))
        .unwrap();
    let mut buf: Vec<u8> = Vec::with_capacity(buf_size);

    let dc = GetDC(HWND(0));
    assert!(dc != HDC(0));

    let mut bitmap_info = BITMAPINFOHEADER {
        biSize: biheader_size_u32,
        biWidth: bitmap.bmWidth,
        biHeight: -bitmap.bmHeight,
        biPlanes: 1,
        biBitCount: 32,
        biCompression: BI_RGB,
        biSizeImage: 0,
        biXPelsPerMeter: 0,
        biYPelsPerMeter: 0,
        biClrUsed: 0,
        biClrImportant: 0,
    };
    let result = GetDIBits(
        dc,
        info.hbmColor,
        0,
        height_u32,
        Some(buf.as_mut_ptr().cast()),
        addr_of_mut!(bitmap_info).cast(),
        DIB_RGB_COLORS,
    );
    assert!(result == bitmap.bmHeight);
    buf.set_len(buf.capacity());

    let result = ReleaseDC(HWND(0), dc);
    assert!(result == 1);
    DeleteObject(info.hbmColor).unwrap();

    for chunk in buf.chunks_exact_mut(4) {
        let [b, _, r, _] = chunk else { unreachable!() };
        mem::swap(b, r);
    }

    RgbaImage::from_vec(width_u32, height_u32, buf).unwrap()
}

// testing:

use windows::{
    core::PCWSTR,
    Win32::{
        Foundation::HINSTANCE,
        UI::WindowsAndMessaging::{DestroyIcon, LoadImageW, IMAGE_ICON, LR_DEFAULTCOLOR, OIC_NOTE},
    },
};

fn main() {
    unsafe {
        let icon = LoadImageW(
            HINSTANCE(0),
            PCWSTR::from_raw(OIC_NOTE as *const u16),
            IMAGE_ICON,
            0,
            0,
            LR_DEFAULTCOLOR,
        )
        .unwrap();
        let icon = HICON(icon.0);
        let image = icon_to_image(icon);
        DestroyIcon(icon).unwrap();
        image.save("example.png").unwrap();
    }
}

(I do assume here that the hbmColor bitmap of an HICON is always a DDB compatible with the screen; I do not see any way for it to be a DIB, or to be compatible with some other DC.)