Rust で書いてますが、他の言語でも同様に出来るはず。
use base64;
use winapi::um::shellapi::{SHGetFileInfoW, SHGFI_ICON, SHGFI_SMALLICON};
use winapi::um::wingdi::DeleteObject;
use std::mem;
use std::os::windows::ffi::OsStrExt;
use std::path::PathBuf;
use std::ptr;
use winapi::um::wingdi::{
CreateCompatibleBitmap, CreateCompatibleDC, DeleteDC, GetDIBits, SelectObject, BITMAPINFO,
BITMAPINFOHEADER,
};
use winapi::um::winuser::{GetDC, ReleaseDC};
use winapi::shared::windef::{HBITMAP, HICON};
use winapi::um::wingdi::{GetObjectW, BITMAP};
use winapi::um::winuser::{DrawIconEx, GetIconInfo, ICONINFO};
use winapi::um::shellapi::SHFILEINFOW;
use winapi::um::wingdi::PatBlt;
use winapi::um::wingdi::WHITENESS;
use winapi::um::wingdi::{BITMAPFILEHEADER, BI_RGB};
use winapi::um::winuser::DestroyIcon;
pub fn get_file_icon(filepath: &PathBuf) -> Option<String> {
let icon = extract_icon_from_file(&filepath)?;
let bitmap = icon_to_bitmap(icon.data)?;
let bites = bitmap_to_bites(bitmap.data)?;
Some(base64::encode(&bites))
}
struct AutoRelease<T> {
data: T,
release_func: fn(&mut T),
}
impl<T> Drop for AutoRelease<T> {
fn drop(&mut self) {
(self.release_func)(&mut self.data);
}
}
fn extract_icon_from_file(file_name: &PathBuf) -> Option<AutoRelease<HICON>> {
unsafe {
let file_name = std::ffi::OsStr::new(&file_name.to_str()?)
.encode_wide()
.chain(std::iter::once(0))
.collect::<Vec<_>>();
let mut sfi: SHFILEINFOW = mem::zeroed();
let ret = SHGetFileInfoW(
file_name.as_ptr(),
0,
&mut sfi as *mut SHFILEINFOW,
mem::size_of::<SHFILEINFOW>() as u32,
SHGFI_ICON | SHGFI_SMALLICON,
);
if ret == 0 {
return None;
}
Some(AutoRelease {
data: sfi.hIcon,
release_func: |data| {
DestroyIcon(*data);
},
})
}
}
fn icon_to_bitmap(h_icon: HICON) -> Option<AutoRelease<HBITMAP>> {
let icon_info = unsafe {
let mut icon_info: ICONINFO = mem::zeroed();
if GetIconInfo(h_icon, &mut icon_info as *mut ICONINFO) == 0 {
return None;
}
AutoRelease {
data: icon_info,
release_func: |data| {
DeleteObject(data.hbmMask as *mut _);
DeleteObject(data.hbmColor as *mut _);
},
}
};
let bmp = unsafe {
let mut bmp: BITMAP = mem::zeroed();
let ret = GetObjectW(
icon_info.data.hbmColor as *mut _,
mem::size_of::<BITMAP>() as i32,
&mut bmp as *mut BITMAP as *mut _,
);
if ret == 0 {
return None;
}
bmp
};
unsafe {
let hdc_screen = AutoRelease {
data: GetDC(ptr::null_mut()),
release_func: |data| {
ReleaseDC(ptr::null_mut(), *data);
},
};
let hdc_mem = AutoRelease {
data: CreateCompatibleDC(hdc_screen.data),
release_func: |data| {
DeleteDC(*data);
},
};
let h_bitmap = CreateCompatibleBitmap(hdc_screen.data, bmp.bmWidth, bmp.bmHeight);
SelectObject(hdc_mem.data, h_bitmap as *mut _);
PatBlt(hdc_mem.data, 0, 0, bmp.bmWidth, bmp.bmHeight, WHITENESS);
let h_old_obj = SelectObject(hdc_mem.data, h_bitmap as *mut _);
let ret = DrawIconEx(
hdc_mem.data,
0,
0,
h_icon,
bmp.bmWidth,
bmp.bmHeight,
0,
ptr::null_mut(),
3,
);
SelectObject(hdc_mem.data, h_old_obj as *mut _);
if ret == 0 {
return None;
}
Some(AutoRelease {
data: h_bitmap,
release_func: |data| {
DeleteObject(*data as *mut _);
},
})
}
}
fn bitmap_to_bites(h_bitmap: HBITMAP) -> Option<Vec<u8>> {
let bmp = unsafe {
let mut bmp: BITMAP = mem::zeroed();
let ret = GetObjectW(
h_bitmap as *mut _,
mem::size_of::<BITMAP>() as i32,
&mut bmp as *mut BITMAP as *mut _,
);
if ret == 0 {
return None;
}
bmp
};
let mut bmf_header: BITMAPFILEHEADER = unsafe { std::mem::zeroed() };
bmf_header.bfType = 0x4D42; // "BM"
bmf_header.bfSize = (std::mem::size_of::<BITMAPFILEHEADER>()
+ std::mem::size_of::<BITMAPINFOHEADER>()) as u32
+ (bmp.bmWidthBytes * bmp.bmHeight) as u32;
bmf_header.bfOffBits = std::mem::size_of::<BITMAPFILEHEADER>() as u32
+ std::mem::size_of::<BITMAPINFOHEADER>() as u32;
let mut bi: BITMAPINFOHEADER = unsafe { std::mem::zeroed() };
bi.biSize = std::mem::size_of::<BITMAPINFOHEADER>() as u32;
bi.biWidth = bmp.bmWidth;
bi.biHeight = bmp.bmHeight;
bi.biPlanes = 1;
bi.biBitCount = bmp.bmBitsPixel;
bi.biCompression = BI_RGB;
bi.biSizeImage = (bmp.bmWidthBytes * bmp.bmHeight) as u32;
let header_bytes: [u8; mem::size_of::<BITMAPFILEHEADER>()] =
unsafe { mem::transmute(bmf_header) };
let info_bytes: [u8; mem::size_of::<BITMAPINFOHEADER>()] = unsafe { mem::transmute(bi) };
let hdc_screen = unsafe { GetDC(ptr::null_mut()) };
let hdc_mem = unsafe { CreateCompatibleDC(hdc_screen) };
unsafe { SelectObject(hdc_mem, h_bitmap as *mut _) };
let height = bmp.bmHeight.abs();
let mut bmi: BITMAPINFO = unsafe { std::mem::zeroed() };
bmi.bmiHeader = bi;
let mut pixels: Vec<u8> = vec![0; (bmp.bmWidthBytes * height) as usize];
unsafe {
GetDIBits(
hdc_mem,
h_bitmap,
0,
height.try_into().unwrap(),
pixels.as_mut_ptr() as *mut _,
&mut bmi,
winapi::um::wingdi::DIB_RGB_COLORS,
);
ReleaseDC(ptr::null_mut(), hdc_screen);
DeleteDC(hdc_mem);
};
let mut combined_bytes = Vec::new();
combined_bytes.extend_from_slice(&header_bytes);
combined_bytes.extend_from_slice(&info_bytes);
combined_bytes.extend_from_slice(&pixels);
Some(combined_bytes)
}
-
SHGetFileInfoW
に与えるフラグを変えると、大きいアイコンが取れる。
オーバーレイアイコンが欲しい場合は以下の様に。
use winapi::um::shellapi::{SHGFI_OVERLAYINDEX, SHGFI_SYSICONINDEX};
use winapi::um::commctrl::{ImageList_GetIcon, HIMAGELIST, ILD_NORMAL, INDEXTOOVERLAYMASK};
fn extract_icon_from_file(file_name: &PathBuf) -> Option<AutoRelease<HICON>> {
let file_name = std::ffi::OsStr::new(&file_name.to_str()?)
.encode_wide()
.chain(std::iter::once(0))
.collect::<Vec<_>>();
unsafe {
let mut shfi: SHFILEINFOW = std::mem::zeroed();
let handle = SHGetFileInfoW(
file_name.as_ptr(),
0x0000020, // FILE_ATTRIBUTE_ARCHIVE
&mut shfi,
std::mem::size_of::<SHFILEINFOW>() as u32,
SHGFI_SMALLICON | SHGFI_SYSICONINDEX | SHGFI_OVERLAYINDEX | SHGFI_ICON,
);
if handle == 0 {
return None;
}
let handle = handle as HIMAGELIST;
DestroyIcon(shfi.hIcon);
let d_no = shfi.iIcon;
let index = d_no & 0x00FFFFFF;
let sub_index = (d_no & 0x0F000000) >> 24;
let hicon = ImageList_GetIcon(
handle,
index as i32,
INDEXTOOVERLAYMASK(sub_index as u32) | ILD_NORMAL,
);
Some(AutoRelease {
data: hicon,
release_func: |data| {
DestroyIcon(*data);
},
})
}
}
- 事前に
OleInitialize
を呼んでおく必要が在るかも。