IronPython への関連付け

Windows 7 を Linux の GNOME のように便利に使いたい、その2。

Windows でスクリプトを使うのに Linux に比べ何が不便なのか。
書くまでも無い、実行パーミッションという概念が無いこと。
すべてが拡張子による関連付けで決まってしまうこと。

特に普通の Python はインストールで *.py に関連付けられるので悲惨。
ファイルをうっかり W クリックすると無意味に cmd.exe コンソールが出る。
ま、インストールする気は無いけど、Windows では何の約にも立たない。

Windows 版 Python はその辺りを考えてか Pythonw という exe が同根されている。
コンソール不要なら拡張子を *.pyw にすればいい、だけどそれだけだ。
何より Windows ではコンソールなんてほとんどの人が使わない。
IronPython にも同様に ipyw(64) という exe が同根されている。

ipyw64

しかしよく考えてみれば私の Y901x や SeeMe も実行パーミッションは付けていない。
同根のシェルスクリプトからメインファイルを Python に引渡しているだけ。
ならば ipyw64.exe を利用して同じようにすればいいんでないの?

ということで考えた。

*.py をテキストエディタに関連付け
*.pyw を ipyw64.exe に関連付け(私の Windows 7 が 64bit なので)

というくだらない方法だがこれで Linux で Python を使っている時と同様に使える。
実行はシェルスクリプトの変わりに ipyw64.exe にやってもらおうという考え。

main_window.py

# -*- coding: UTF-8 -*-

import clr

clr.AddReferenceByPartialName("PresentationCore")
clr.AddReferenceByPartialName("PresentationFramework")
clr.AddReferenceByPartialName("WindowsBase")

from System import *
from System.Windows import *
from System.Windows.Controls import *
from System.Windows.Media.Imaging import *

class PicViewWindow(Window):
    def __init__(self):
        self.Title = "PicView"
        self.Drop += self.on_drop
        self.AllowDrop = True
        self.Width = 300
        self.Height = 300
        self.image = Image()
        self.Content = self.image

    def on_drop(self, sender, e):
        filename = e.Data.GetData(DataFormats.FileDrop)[0].ToString()
        self.image.Source = BitmapImage(Uri(filename))

def main():
    Application().Run(PicViewWindow())

if __name__ == "__main__":
    main()

picview.pyw

import main_window
main_window.main()

という2つのファイルを用意し上記関連付けを行っておく。
picview.pyw を W クリックしてみる。

ipyw_picview

ということができる。
main() という関数を用意するのがミソ。
debug 時は EmEditor 等テキストエディタの機能で以下のように実行できるから。

ぱぇぽぃ2 ? Blog Archive ? EmEditor を Gedit 風にカスタマイズ

これでコードを編集したい時は *.py 実行時は *.pyw を W クリックという環境完成。
アイコンが違うので解りやすいしこのほうがコード分割も行いやすい。
ただ OS デフォルト動作では無いので配布に関してはどうするかだ…

Windows 7 の Explorer で FTP は快適

Windows 7 を Linux の GNOME のように便利に使いたい。

既に EmEditor を導入し Gedit 風にカスタマイズしている。
Tab キーでスニペットが挿入できるようになった v9 は本当にありがたい。

IronPython を導入し Python と PyGtk の代わりに使っている。
普通の Python じゃとにかく何をやるにも別個モジュールが必要になってアホクサイ。

Linux で Python が糞便利な理由は全てディストリビューションのおかげ。
必要になりそうなモジュールを最初からまとめて入れてくれているからである。
IronPython なら .NET Framework が使えるから別の何かが必要になる場合はほぼ無い。
もう少し DLR の初期化が早ければなぁ…

次は Explorer をなるべく Nautilus のように使いたい。
Nautilus も Dolphin も本当に便利だ、なにより FTP 接続が簡単である。
Explorer は…と思っていたけど調べてみたら実は超簡単だった。

ここ (sakura) であれば

ftp://ユーザー名@wwwサーバー番号.sakura.ne.jp/www

をアドレスバーに打ち込むだけ、実は Vista でも同じ。
パスワードを保存も可能だけどセキュリティ面でやらないほうがいいかと。

ftp_login

パスワードを打ち込んで Enter すれば普通に接続完了。
後はそのままローカルのファイルのようにドラッグアンドドロップ可能。
右クリックメニューのプロパティからパーミッションも変更できるようだ。

ftp_setting

最ログインが必要になったら「ファイル」メニューから「ログイン方法」を選択。

ftp_relogin

それだけでは毎回打ち込みが必要で不便なので「お気に入り」に追加する。
「お気に入り」を右クリックして簡単に登録できます。

ftp_explorer

サーバー番号と実ユーザー名は一応塗りつぶしました。
これが何故か Vista ではできない、コピー動作になる…
以後はお気に入りからクリックするだけで簡単 FTP クライアントに。

もう Windows 7 では
「君もしかして FTP ソフトなんか入れているの?ププッw」
になってしまいました。

他 Windows 7 日本語版の Explorer はデフォルトで LHA の解凍が可能になった。
でも作成はできない、つか個人的には tar.gz に対応してほしい…

ついでに。
このスクリーンショット加工に mspaint.exe を使っているんだが意外に使いやすい!
PrtSc で mspaint に Ctrl+V で普通にスクリーンショットの大きさな画像ができる。
実際 Gimp も一部加工程度でしか使わないのでペイントソフトはコレで十分かな。

個人製作オンラインソフトの廃れ具合は加速するばかり…

minipoli 1.6.0 64bit 版追加

遅ればせながら minipoli に 64bit 版を追加しました。
ついでに x64 用 msi 設定プロジェクトのバックアップを兼ねている。

追加機能無し、32bit 版のままでも WOW64 で問題なく動くんだけど…

C:\Program Files (x86)

にどうしてもインストーラで入れたく無かったんだよ!

x64 用 msi インストーラを作ったのでキチンと Program Files に入ります。
SeeMe は勝手に 64bit で動くけどインストーラはどうしよう?

つか x64 Windows 7 を使っているのだから 64bit アプリをなるべく使いたい。
今の状況で 32bit のまま Windows 7 対応なんて言ったら笑われそうだし。

とにかくコレで Acer Aspire 1410 に minipoli を入れる場合の不満は無くなった。
IronPython コード書きはマジにコイツでやっているので自分でコレが必要なのよ。
x64 Windows マシンを早めに買って良かった、64bit アプリ開発が少し楽しい。

ちなみに Aspire 1410 での動画再生は WMP12 と VLC です(ぉい!
てか動画はほとんど Mandriva Linux で見ているし…
リポジトリからデコーダー導入可能な GStreamer のほうが DirectShow より便利だよ。

ついでに。
以前書いたが Mandriva Linux から VirtualBox ose 上に 32bit Vista を入れている。
それに Visual Studio 2008 を入れて C++ 開発はそれでやっているのだが。

Vista_HDD

一応テキスト編集用に秀丸、アーカイブ作成用に ExpLZH だけは入れた状態。
でコレだよ、Opera すら入れていないのにタマランわ。
コイツからは多分 MSDN しか見ないので IE のほうが都合がいい。

P/Invoke CharSet.Unicode

間違いに気づいたまま放置はアレなので書き換えた。
しかし SHGetFileInfo C# なんかでググると笑った、皆 SHFILEINFO の第二引数が IntPtr だ。
32bit でしか動作確認していないんだねぇ、私もそうだったけど。

ついでに P/Invoke で SHGetFileInfoW の UNICODE 関数化したんだけど…

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

SHGetFileInfoW てか UNICODE を P/Invoke で指定するにはこんなに面倒なのか!
CharSet = CharSet.Unicode を指定しなければ Ansi 関数呼び出しになってしまうのかい。
更に Ansi 文字列に変換を全自動で行うなんて今の時代としては逆だと思うんだが。

ぱぇぽぃ2 ? Blog Archive ? UNICODE にならん

を以前自分でやっていなかったら気がつかなかったかもだよ。
前回書いたように IronPython なら関数名直で str をそのまま渡せばいいだけなのに。

てか .NET Framework 2.0 からは XP 以降でしか動かないのにこの仕様…
Microsoft がやっていることは本当によく解らない。

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