C# で SHGetFileInfo を使ってアイコンと種類を取ってくる例
の IronPython 化コード。
CreateBitmapSourceFromHIcon の第一引数は IntPtr であった。
Imaging.CreateBitmapSourceFromHIcon メソッド (System.Windows.Interop)
HICON である ctypes.c_void_p をそのまま渡すと error になる。
例外くらい吐いてくれよ…
printf debug は面倒なんだが…
ということで以下のようなコードを書いてみた。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | # -*- 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()) |
1 | IntPtr(shinfo.hIcon) |
と cast すれば Icon Image も普通に取得できるようである。
HICON, HWND のような Handle は Pointer と同じと考えていいのね。
1 | str (e.Data.GetData(DataFormats.FileDrop)[ 0 ]) |
だと日本語ファイル名が上手くいかない、str と ToString は少し処理が違うみたい。
とにかくこれでアイコンや詳細を取得できるどころか x64 対応コードが簡単に書ける。
C# の P/Invoke なんてもうばかばかしい、これからは IronPython の時代だ。
しかし上記リンク先コードは色々間違えていると今頃気が付いた。
SMALLICON 指定だし ANSI 関数だし SHFILEINFO 第二引数は int にしないといけないし…
32bit Windows は int と Pointer はサイズが同じだからこういう間違いって多いんだろうな。