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 はサイズが同じだからこういう間違いって多いんだろうな。