0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ファイルアイコンをBMPとして取得するコード

Posted at

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 を呼んでおく必要が在るかも。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?