Get File Details and Icon Image for IronPython ctypes

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())

get_fileinfo_x64

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

IronPython SHGetFileInfoW is ctypes

Re: SHGetFileInfo :: ASPN Mail Archive :: python-list

の IronPython 化が解った。
HICON は ctypes.c_voidp or ctype.c_void_pにすれば x86,x64 両方に対応できる。
追記: c_voidp は後方互換性で残っているだけのようで…
x64 のみでいいなら ctypes.int64 でもいいみたいだけど…
違いは何なのだろう?もう少しソースコードを見てみよう。

ついでに UNICODE Function 化、x64 で ANSI なんて悲しすぎる。
つか IronPython は内部が UNICODE や…
ANSI 関数に UNICODE でファイル名を渡してたから上手くいかなかったのね。

#-*- coding:utf-8 -*-

from ctypes import *

MAX_PATH = 260
HICON = c_void_p

# SHFILEINFOW
class SHFILEINFOW(Structure):
    _fields_ = [("hIcon", HICON),
                ("iIcon", c_int),
                ("dwAttributes", c_uint),
                ("szDisplayName", c_wchar * MAX_PATH),
                ("szTypeName", c_wchar * 80)]

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

shfileinfo = SHFILEINFOW()

import sys

# SHGetFileInfoW
flags = SHGFI_DISPLAYNAME | SHGFI_TYPENAME | SHGFI_ATTRIBUTES
print windll.shell32.SHGetFileInfoW(sys.executable,
                                   0,
                                   byref(shfileinfo),
                                   sizeof(shfileinfo),
                                   flags)

# print no repr
print hex(shfileinfo.dwAttributes)
print shfileinfo.szDisplayName
print shfileinfo.szTypeName

python_list

よし動いた。
後下記をコレで書いてみようと思ったけど何故か Image が取得できない。

C# で SHGetFileInfo を使ってアイコンと種類を取ってくる例

c_voidp で HICON は取得できないのかな?
もう少し調べる。

IronPython が RC3 になっていた

IronPython のサイトを久々に覗いてみた
なんか RC3 になっていた、まだ正式版にするつもりは無いのだろうか?

IronPython

とりあえずダウンロード、おぉ ctypes モジュールが付属になったのか。
てゆーかいつのまにかメチャメチャ付属モジュールが増えている!

ctypes_and_etc

ちなみに zip 版にはコレらは入っていないようである。
早速動作テストだ。

ぱぇぽぃ2 ? Blog Archive ? ctypes で WindowsAPI

も当然のように IronPython のみインストールでそのまま可能になった。
普通の Python はもう本当に必要無いね、そうしたかったんだろうけど。

Re: SHGetFileInfo :: ASPN Mail Archive :: python-list

でもコレはできない…
64bit で動かないから 32bit の Vista でも試したけど駄目だった。

32bit_test

バグっぽいけどもう少し弄くってみます。

ついでに Mandriva Linux でも少し試してみた。
3つもダウンロードした私は余程人柱になりたいのだろうか。

ipy26test

clr を import できない…
これじゃ何もできん、2.0.3 なら問題無いんだが。

てんしん

sum とは言うまでもなく「合計」の意味である。
dim sum の意味が「点心」って、、、、、天津ではなくて?

tensin

Dim sum – Wikipedia, the free encyclopedia

プログラマーのみが笑えるネタおしまい。
こんなエントリさえもググなんとかさんは拾ってしまうかの実験を兼ねて。
いや、Mandriva ネタのアクセス解析が予想どうりだったので、というのは内緒だよ。

お知らせ – Mandriva Linux 製品取扱終了のお知らせ – SIDfm

需要ネェ…

Mandriva GNOME 版で Qt3 アプリのメニュー

Mandriva 2010.0 GNOME 版で Opera を使うとメニューがおかしい。
メニューがボタンのようになっていてクリックしないとサブメニューが出ない。
Opera は Qt3 製だ、これは qtconfig を使って設定を変更するしかなさそうだ。
しかしソフトウエア管理からは Qt4 用しか見つからない。

conf01

qtconfig_qt4

/usr/lib/qt4/bin/qtconfig

に入る、インストールして端末から qtconfig を実行するとコレが起動する。
ちなみに設定ファイルは以下

~/.config/Trollteck.conf

実は Opera を入れると Qt3 用 qtconfig は入るようだ。

/usr/lib/qt3/bin/qtconfig

を実行すれば Qt3 用 qtconfig が起動できる、設定は以下のようだ。

~/.qt/qtrc

qtconfig_qt3

デフォルトでは GUI Style が Motif になっている。
コレを Windows に変更、てか KDE 版は Windows になっている。
これでメニューがボタンみたいで使いにくい不満は解決できる。

mandriva_opera_menu

でも Opera は大半が独自描写なのでメニューくらいしかコレで変更できない…

日本語入力ができなくなる場合があるのは Ubuntu で使っていた時と同じか。
一度右クリックメニューを出してしまえば入力できるようになるんだが何故だろう。
コレで XIM Input Style を変更しても同じ結果、KDE 版でさえ同じ結果なのよね。