Programming」カテゴリーアーカイブ

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 なら問題無いんだが。

Linux Opera Starting Check in ps command

せっかく Y901x を Mandriva KDE に対応させたのだから SeeMe もやりたい。
問題は Python の wnck モジュールだ、KDE 版にはデフォルトで入っていない。

seeme102_kde

動画プレイヤーならともかくコイツはモジュールを入れてくれにしたくない。

基本的に私は得に何もインストールさせないで使えるものを作るのがポリシー。
Windows で .NET は IronPython しかやらなくなったのはそのポリシーから。
だって 7 や Vista なら .NET 自体は最初からあるし SDK もコンパイラも必要無い。

ということで wnck を使わずに Opera が起動しているかどうか判別する方法は…

まてよ、Linux は Windows とは違ってコマンドライン OS に GUI が被さっているだけだ。
Window を探して名前を調べなくてもコマンドからチェックできるんじゃないの?
探せば他も見つかるだろうけど単純に ps コマンドの出力から調べられるかも。

#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
    opera_init_check.py
    Find in "ps ax" command output
"""

import commands

r = commands.getoutput("ps ax")
if "/usr/lib/opera/opera" in r:
    print "starting Opera"
else:
    print "not starting Opera"

こんな手抜きバリバリなコードでアッサリ判別できてしまった。
Linux は多分この位置にしか Opera 本体が入らないだろうからイケそうだ。
Windows 的な考え方がまだ抜けていないみたいだな私は。

この判別方法に書き換えて VirtualBox 上の KDE 版で動かしてみよう。

seeme103_kde

問題なく起動できるようになった、Opera の起動判別もバッチリ。

ということでバグ修正を含めて SeeMe for Linux 1.0.3 公開。
ただダウンロード数が強烈に寂しいんだがコイツ…

Python property Decorator

INotifyPropertyChanged and databinding in IronPython WPF

Wonderful!
私が書いた下記を「理想的じゃネェ」と断言しているだけある。

ぱぇぽぃ2 ? Blog Archive ? WPF ListView DataBinding for IronPython

OnPropertyChanged を先に呼ぶことになるのが気になるけど…
問題なく動くから多分 OnPropertyChanged は Queue に入るのだろう。
組み込み以外で Decorator の実用的な使い方を初めて見た。

PyGtk 製 Y901x にて

@dbus.service.method("org.y901x.Interface1")
def set_uri(self, uri):
	###

と使っているが何故これで D-Bus 経由で別インスタンスから関数が呼べるのか実は知らない…
自身で関数を呼んでいるのではないし、もっと調べると面白い使い方ができそうだ。

とにかく良いものを見つけた、こんな Blog をやっているといいことあるよ。
てかこの Blog へのリンク元を見ると海外からばかり、co.uk とかもあるし。
日本の Python 屋って非実用的コードばかり書いている人が多いですからね。