C# で SHGetFileInfo を使ってアイコンと種類を取ってくる例
の IronPython 化コード。
CreateBitmapSourceFromHIcon の第一引数は IntPtr であった。
Imaging.CreateBitmapSourceFromHIcon メソッド (System.Windows.Interop)
HICON である ctypes.c_void_p をそのまま渡すと error になる。
例外くらい吐いてくれよ…
printf debug は面倒なんだが…
ということで以下のようなコードを書いてみた。
# -*- coding: UTF-8 -*-
"""
get_file_detailst.py
System.IO.FileInfo and SHGetFileInfoW Windows API use
both the x86 and x64 editions
"""
import clr
clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName("WindowsBase")
from System import *
from System.IO import *
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Documents import *
from System.Windows.Media.Imaging import *
from ctypes import *
MAX_PATH = 260
HICON = c_void_p
# ShellFileInfoFlags
SHGFI_ICON = 0x000000100
SHGFI_DISPLAYNAME = 0x000000200
SHGFI_TYPENAME = 0x000000400
SHGFI_ATTRIBUTES = 0x000000800
SHGFI_ICONLOCATION = 0x000001000
SHGFI_EXETYPE = 0x000002000
SHGFI_SYSICONINDEX = 0x000004000
SHGFI_LINKOVERLAY = 0x000008000
SHGFI_SELECTED = 0x000010000
SHGFI_ATTR_SPECIFIED = 0x000020000
SHGFI_LARGEICON = 0x000000000
SHGFI_SMALLICON = 0x000000001
SHGFI_OPENICON = 0x000000002
SHGFI_SHELLICONSIZE = 0x000000004
SHGFI_PIDL = 0x000000008
SHGFI_USEFILEATTRIBUTES = 0x000000010
# FileAttributesFlags
FILE_ATTRIBUTE_ARCHIVE = 0x00000020,
FILE_ATTRIBUTE_ENCRYPTED = 0x00004000,
FILE_ATTRIBUTE_HIDDEN = 0x00000002,
FILE_ATTRIBUTE_NORMAL = 0x00000080,
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x00002000,
FILE_ATTRIBUTE_OFFLINE = 0x00001000,
FILE_ATTRIBUTE_READONLY = 0x00000001,
FILE_ATTRIBUTE_SYSTEM = 0x00000004,
FILE_ATTRIBUTE_TEMPORARY = 0x00000100
class SHFILEINFOW(Structure):
"""
typedef struct
"""
_fields_ = [("hIcon", HICON),
("iIcon", c_int),
("dwAttributes", c_uint),
("szDisplayName", c_wchar * MAX_PATH),
("szTypeName", c_wchar * 80)]
class GetFileInfoWindow(Window):
"""
Get FileInfo as Droped File
"""
def __init__(self):
"""
Window.__init__(self) is No
Constructor has been
"""
self.Title = "Get FileInfo"
self.Drop += self.on_drop
self.AllowDrop = True
self.SizeToContent = SizeToContent.WidthAndHeight
self.ResizeMode = ResizeMode.NoResize
# Controls
panel = StackPanel()
self.Content = panel
b = TextBlock()
b.Inlines.Add(Bold(Run("Get FileInfo as Droped File")))
panel.Children.Add(b)
self.image = Image()
self.image.Height = 32
self.image.Width = 32
panel.Children.Add(self.image)
# Create TextBlock List
self.texts = []
for i in range(4):
t = TextBlock()
panel.Children.Add(t)
self.texts.append(t)
def on_drop(self, sender, e):
"""
Get Dropped File Details
"""
filename = e.Data.GetData(DataFormats.FileDrop)[0].ToString()
shinfo = SHFILEINFOW()
r = windll.shell32.SHGetFileInfoW(
filename,
FILE_ATTRIBUTE_NORMAL,
byref(shinfo),
sizeof(shinfo),
SHGFI_ICON |
SHGFI_LARGEICON |
SHGFI_TYPENAME |
SHGFI_DISPLAYNAME
)
if r == 0:
self.texts[0].Text = "error"
else:
# Get System.IO.FileInfo
ioinfo = IO.FileInfo(filename)
# Icon
bms = Interop.Imaging.CreateBitmapSourceFromHIcon(
IntPtr(shinfo.hIcon),
Int32Rect(0, 0, 32, 32),
BitmapSizeOptions.FromEmptyOptions()
)
self.image.Source = bms
# File Name
self.texts[0].Text = shinfo.szDisplayName
# Updated
self.texts[1].Text = ioinfo.LastWriteTime.ToString("yyyy/MM/dd HH:mm")
# Type Name
self.texts[2].Text = shinfo.szTypeName
# File Size
self.texts[3].Text = Math.Ceiling(ioinfo.Length / 1024.0).ToString("#,##0 KB")
if __name__ == "__main__":
Application().Run(GetFileInfoWindow())
IntPtr(shinfo.hIcon)
と cast すれば Icon Image も普通に取得できるようである。
HICON, HWND のような Handle は Pointer と同じと考えていいのね。
str(e.Data.GetData(DataFormats.FileDrop)[0])
だと日本語ファイル名が上手くいかない、str と ToString は少し処理が違うみたい。
とにかくこれでアイコンや詳細を取得できるどころか x64 対応コードが簡単に書ける。
C# の P/Invoke なんてもうばかばかしい、これからは IronPython の時代だ。
しかし上記リンク先コードは色々間違えていると今頃気が付いた。
SMALLICON 指定だし ANSI 関数だし SHFILEINFO 第二引数は int にしないといけないし…
32bit Windows は int と Pointer はサイズが同じだからこういう間違いって多いんだろうな。