はじめに
Windows APIを使ってサムネイル画像を作成するプログラムを記述しました。
DMD v2.095.0でコンパイルしました。
ソースコード
import core.sys.windows.windows;
import std.path;
import std.utf;
pragma(lib, "Gdi32.lib");
pragma(lib, "Ole32.lib");
pragma(lib, "Rpcrt4.lib");
pragma(lib, "Shell32.lib");
pragma(lib, "User32.lib");
extern (C) {
interface IShellItem : IUnknown {
}
interface IShellItemImageFactory : IUnknown {
HBITMAP GetImage(SIZE nativeSize, int options, HBITMAP *hBitmap);
}
interface IBindCtx : IUnknown {
}
}
extern (Windows) HRESULT SHCreateItemFromParsingName(PCWSTR pszPath, IBindCtx *pbc, REFIID riid, void **ppv);
HBITMAP getThumbnail(string fileName, int width, int height, int options)
{
CoInitialize(null);
scope(exit) CoUninitialize();
GUID shellItem2Guid;
UuidFromString(cast(wchar *)"7E9FB0D3-919F-4307-AB2E-9B1860310C93"w.ptr, &shellItem2Guid);
IShellItem *nativeShellItem;
int retCode = SHCreateItemFromParsingName(fileName.absolutePath.toUTF16z, cast(IBindCtx *)null, &shellItem2Guid, cast(void **)&nativeShellItem);
HBITMAP hBitmap;
if ( retCode == 0 ){
SIZE nativeSize;
nativeSize.cx = width;
nativeSize.cy = height;
GUID IID_IShellItemImageFactory;
UuidFromString(cast(wchar *)"BCC18B79-BA16-442F-80C4-8A59C30C463B"w.ptr, &IID_IShellItemImageFactory);
IShellItemImageFactory *psiif;
(cast(IShellItemImageFactory)nativeShellItem).QueryInterface(&IID_IShellItemImageFactory, cast(void**)&psiif);
(cast(IShellItemImageFactory)psiif).GetImage(nativeSize, 0, &hBitmap); // SIIGBF_RESIZETOFIT = 0;
}
return ( hBitmap );
}
void saveBmpFile(HBITMAP hBmp, string filename)
{
HDC hdc = GetDC(null);
scope(exit) ReleaseDC(null, hdc);
HDC hdcMem = CreateCompatibleDC(hdc);
scope(exit) DeleteDC(hdcMem);
BITMAP bmp;
GetObject(hBmp, bmp.sizeof, &bmp);
SelectObject(hdcMem, hBmp);
uint bpp;
switch ( bmp.bmBitsPixel ){
case 2: case 4: case 8:
bpp = 2 ^^ bmp.bmBitsPixel;
break;
default:
bpp = 0;
}
uint imageSize = cast(uint)(bmp.bmWidthBytes * bmp.bmHeight
+ BITMAPFILEHEADER.sizeof + BITMAPINFOHEADER.sizeof + RGBQUAD.sizeof * bpp);
BITMAPFILEHEADER bfh;
bfh.bfType = 'B' | 'M' << 8; // "BM" : LittleEndian
bfh.bfSize = imageSize;
bfh.bfReserved1 = 0;
bfh.bfReserved2 = 0;
bfh.bfOffBits = cast(uint)(BITMAPFILEHEADER.sizeof + BITMAPINFOHEADER.sizeof + RGBQUAD.sizeof * bpp);
union BI {
BITMAPINFO bi;
byte[BITMAPINFOHEADER.sizeof + RGBQUAD.sizeof * 256] dummy;
alias bi this;
}
BI bi;
bi.bmiHeader.biSize = BITMAPINFOHEADER.sizeof;
bi.bmiHeader.biWidth = bmp.bmWidth;
bi.bmiHeader.biHeight = bmp.bmHeight;
bi.bmiHeader.biPlanes = 1;
bi.bmiHeader.biBitCount = bmp.bmBitsPixel;
bi.bmiHeader.biCompression = BI_RGB;
if ( bpp != 0 ){
GetDIBColorTable(hdcMem, 0, bpp, bi.bmiColors.ptr);
}
byte[] bits = new byte[bmp.bmWidthBytes * bmp.bmHeight];
GetDIBits(hdcMem, hBmp, 0, bmp.bmHeight, bits.ptr, &bi.bi, DIB_RGB_COLORS);
HANDLE hFile = CreateFile(filename.toUTF16z, GENERIC_WRITE, 0, null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);
scope(exit) CloseHandle(hFile);
DWORD writeSize;
WriteFile(hFile, &bfh, bfh.sizeof, &writeSize, null);
WriteFile(hFile, &bi, cast(uint)(BITMAPINFOHEADER.sizeof + RGBQUAD.sizeof * bpp), &writeSize, null);
WriteFile(hFile, bits.ptr, bmp.bmWidthBytes * bmp.bmHeight, &writeSize, null);
}
void main(string[] args)
{
int THUMB_SIZE = 256;
HBITMAP thumbnail = getThumbnail(args[1], THUMB_SIZE, THUMB_SIZE, 0x00);
saveBmpFile(thumbnail, args[2]);
}
ソースコード補足説明
getThumbnail
関数は、ファイル名よりサムネイル画像のハンドル(HBITMAP
)を返します。
関数内部でIShellItemImageFactory::GetImage
メソッドを呼び出しています。
このメソッドは、エクスプローラーで表示されるファイルアイコンと同じ画像のハンドルを取得できます。
つまり、画像ファイルだけでなく、EXE
ファイルのアイコン画像なども取得可能です。
saveBmpFile
関数は、ビットマップのハンドルから、ビットマップファイルを作成します。
GetObject
を使って、BITMAP構造体の情報を取得します。
BITMAPFILEHEADERやBITMAPINFO構造体に必要な情報をセットして、ファイルを作成します。
コンパイル、実行結果
D:\Dev> dmd -m64 tnail.d
D:\Dev> tnail dlang.jpg dlang.bmp
元ファイル(dlang.jpg): https://dlang.org の画面イメージです
サムネイル(dlang.bmp)出力例
※bmp形式のファイルを直接アップロードできなかったので、jpeg形式に保存しなおしたものになります。
参考情報
IShellItemImageFactory
SHCreateItemFromParsingName
UuidFromString