I'm making some code to extract the 64x64px variant of the icon in an exe file.
The following code does work, but it outputs a grayscale 16x16px image.
I'm still a bit new to rust so please excuse the bad code.
I'm using winapi
version 0.3.9 with the features: "winuser", "shellapi".
use std::ffi::CString;
use std::ptr;
use iced::widget::image::Handle;
use winapi::shared::minwindef::DWORD;
use winapi::um::shellapi::ExtractIconA;
use winapi::shared::windef::HICON;
use winapi::um::wingdi::{CreateDIBSection, DeleteObject, GetDIBits, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, DIB_RGB_COLORS, RGBQUAD};
use winapi::um::winuser::{DestroyIcon, GetIconInfo};
use winapi::um::wingdi::{CreateCompatibleDC, SelectObject};
pub fn exe_icon_to_vec_u8(path: &str) -> Result<Handle, String> {
let c_path = CString::new(path).map_err(|_| "Invalid path".to_string())?;
let hicon: HICON = unsafe { ExtractIconA(ptr::null_mut(), c_path.as_ptr(), 0) };
if hicon.is_null() {
return Err("Failed to extract icon".into());
}
let mut icon_info = unsafe { std::mem::zeroed() };
if unsafe { GetIconInfo(hicon, &mut icon_info) } == 0 {
unsafe { DestroyIcon(hicon) };
return Err("Failed to get icon info".into());
}
let mut bmp_info = BITMAPINFO {
bmiHeader: BITMAPINFOHEADER {
biSize: std::mem::size_of::<BITMAPINFOHEADER>() as DWORD,
biWidth: 64, biHeight: -64, biPlanes: 1, biBitCount: 32,
biCompression: BI_RGB, biSizeImage: 0, biXPelsPerMeter: 0,
biYPelsPerMeter: 0, biClrUsed: 0, biClrImportant: 0,
}, bmiColors: [RGBQUAD { rgbBlue: 0, rgbGreen: 0, rgbRed: 0, rgbReserved: 0 }; 1],
};
let mut pixels: Vec<u8> = vec![0; 64 * 64 * 4];
let hdc = unsafe { CreateCompatibleDC(ptr::null_mut()) };
if hdc.is_null() {
unsafe {
DestroyIcon(hicon);
}
return Err("Failed to create compatible DC".into());
}
let hbitmap = unsafe {
CreateDIBSection(
hdc,
&mut bmp_info,
DIB_RGB_COLORS,
&mut pixels.as_mut_ptr() as *mut _ as *mut _,
ptr::null_mut(),
0,
)
};
if hbitmap.is_null() {
unsafe {
DeleteObject(icon_info.hbmColor as _);
DeleteObject(icon_info.hbmMask as _);
DestroyIcon(hicon);
}
return Err("Failed to create DIB section".into());
}
unsafe {
SelectObject(hdc, hbitmap as _);
GetDIBits(
hdc,
icon_info.hbmColor,
0,
64,
pixels.as_mut_ptr() as *mut _,
&mut bmp_info,
DIB_RGB_COLORS,
);
DeleteObject(hbitmap as _);
DeleteObject(icon_info.hbmColor as _);
DeleteObject(icon_info.hbmMask as _);
DestroyIcon(hicon);
}
Ok(Handle::from_pixels(64, 64, pixels))
}
I tried to find some cargo packages that do this but with no success.
Here is the answer for anyone in the future:
use winapi::um::wingdi::{ CreateCompatibleDC, DeleteDC, DeleteObject, GetDIBits, BITMAPINFO, BITMAPINFOHEADER, BI_RGB, RGBQUAD };
use winapi::um::shellapi::{ SHGetFileInfoW, SHFILEINFOW, SHGFI_ICON, SHGFI_LARGEICON };
use winapi::um::winbase::{ GlobalAlloc, GlobalLock, GHND, GlobalUnlock, GlobalFree };
use winapi::um::winuser::{ GetIconInfo, ICONINFO, DestroyIcon };
use winapi::shared::minwindef::DWORD;
use iced::widget::image::Handle;
use widestring::U16CString;
use std::{mem, ptr, slice};
pub fn extract_icon_as_handle(path: &str) -> Result<Handle, Box<dyn std::error::Error>> {
unsafe {
let mut shfi = SHFILEINFOW {
hIcon: ptr::null_mut(), iIcon: 0,
dwAttributes: 0,
szDisplayName: [0; 260],
szTypeName: [0; 80],
};
SHGetFileInfoW(
U16CString::from_str(path)?.as_ptr(),
0,
&mut shfi,
mem::size_of::<SHFILEINFOW>() as DWORD,
SHGFI_ICON | SHGFI_LARGEICON,
);
if shfi.hIcon.is_null() {
return Err("No icon found.".into());
}
let mut icon_info = ICONINFO {
fIcon: 0,
xHotspot: 0,
yHotspot: 0,
hbmMask: ptr::null_mut(),
hbmColor: ptr::null_mut(),
};
GetIconInfo(shfi.hIcon, &mut icon_info);
let hdc = CreateCompatibleDC(ptr::null_mut());
let bmp_info_header = BITMAPINFOHEADER {
biSize: mem::size_of::<BITMAPINFOHEADER>() as DWORD,
biWidth: 32,
biHeight: -32, // Negative to indicate a top-down DIB
biPlanes: 1,
biBitCount: 32,
biCompression: BI_RGB as DWORD,
biSizeImage: 0,
biXPelsPerMeter: 0,
biYPelsPerMeter: 0,
biClrUsed: 0,
biClrImportant: 0,
};
let mut bitmap_info = BITMAPINFO {
bmiHeader: bmp_info_header,
bmiColors: [RGBQUAD { rgbBlue: 0, rgbGreen: 0, rgbRed: 0, rgbReserved: 0 }; 1],
};
let bitmap_memory = GlobalAlloc(GHND, (32 * 32 * 4) as usize);
let bitmap_bits = GlobalLock(bitmap_memory) as *mut u8;
GetDIBits(
hdc,
icon_info.hbmColor,
0,
32,
bitmap_bits as *mut _,
&mut bitmap_info,
0,
);
GlobalUnlock(bitmap_memory);
DeleteDC(hdc);
DeleteObject(icon_info.hbmColor as _);
DestroyIcon(shfi.hIcon);
let bitmap_slice = slice::from_raw_parts(bitmap_bits, (32 * 32 * 4) as usize).to_vec();
let mut rgba_slice = vec![0u8; bitmap_slice.len()];
for i in 0..(32 * 32) {
let b = bitmap_slice[i * 4 + 0];
let g = bitmap_slice[i * 4 + 1];
let r = bitmap_slice[i * 4 + 2];
let a = bitmap_slice[i * 4 + 3];
rgba_slice[i * 4 + 0] = r;
rgba_slice[i * 4 + 1] = g;
rgba_slice[i * 4 + 2] = b;
rgba_slice[i * 4 + 3] = a;
}
GlobalFree(bitmap_memory);
let handle = Handle::from_pixels(32, 32, rgba_slice);
return Ok(handle);
}
}