チケット #47 (new blog)

登録: 3 年

最終更新: 2 年

pythonからctypesで任意のDLLの関数を使う。

報告者: mtamaki 担当者: mtamaki
優先度: major マイルストーン:
コンポーネント: blog バージョン:
キーワード: 関係者:

説明 (最終更新者: mtamaki) (diff)

ctypesを使って躓いたところメモ&チュートリアル。

まずHelloWorldがてらMessageBox。

from ctypes import windll
windll.user32.MessageBoxW( 0, u"テスト", 0, 0 )

こんだけ。超楽。

次に、自前のDLLを使う。 たとえばc:\test\test.dllで

extern "C"
{
        const char* test()
        {
                return "test";
        }
}

という関数がエクスポートされていて、これを使うとき、

from ctypes import windll, c_char_p
print c_char_p( windll.LoadLibrary(r"c:\test\test.dll").test() ).value

とする。 LoadLibraryで読み込んで、test関数を呼ぶ。 ctypesはデフォルトでは整数を返してしまうので、文字列型にキャストして、それのvalueを見ることで戻り値の文字列を取得できる。ちなみにargtypesを使うもっとエレガントなやり方もある。 たくさん関数を呼ぶときはデコレータとかうまく使うときれいにかけそうな予感がする。

んで、今回のメイン。GetOpenFileNameを読んでみる。 これはいわゆる「ファイルを開くダイアログ」で、GetOpenFileNameを呼ぶだけで表示できるが、GetOpenFileNameにOPENFILENAMEという結構複雑な構造体を渡さないといけない。 しかも、複数選択を有効にしたときは、OPENFILENAMEのlpstrFile変数に、「ディレクトリパス\0ファイル名1\0ファイル名2\0...ファイル名n\0\0」というフォーマットで結果が返ってくるのでそれをパースする必要がある。

def ctypes_get_open_file_name( is_multi_select = False ):
    from ctypes import Structure, c_ulong, c_wchar_p, c_buffer, c_ushort, sizeof, windll, byref, c_void_p, create_unicode_buffer, cast
    class OPENFILENAME(Structure):
        _fields_ = [
            ("lStructSize", c_ulong),
            ("hwndOwner", c_ulong),
            ("hInstance", c_ulong),
            ("lpstrFilter", c_wchar_p),
            ("lpstrCustomFilter", c_wchar_p),
            ("nMaxCustFilter", c_ulong),
            ("nFilterIndex", c_ulong),
            ("lpstrFile", c_wchar_p),
            ("nMaxFile", c_ulong),
            ("lpstrFileTitle", c_wchar_p),
            ("nMaxFileTitle", c_ulong),
            ("lpstrInitialDir", c_wchar_p),
            ("lpstrTitle", c_wchar_p),
            ("Flags", c_ulong),
            ("nFileOffset", c_ushort),
            ("nFileExtension", c_ushort),
            ("lpstrDefExt", c_wchar_p),
            ("lCustData", c_ulong),
            ("lpfnHook", c_void_p),
            ("lpTemplateName", c_wchar_p),
            ("pvReserved", c_void_p),
            ("dwReserved", c_ulong),
            ("FlagsEx", c_ulong),
        ]
        def __init__(self):
            Structure.__init__(self)
            self.lStructSize = sizeof( OPENFILENAME )
            self.set_file_path_size(512*5)
        def set_allow_multi_select(self):
            self.Flags |= 0x200 | 0x00080000
            min_size = 1024 * 5
            if self.nMaxFile < min_size:
                self.set_file_path_size( min_size )
        def set_file_path_size(self, size):
            self.lpstrFileBuffer = create_unicode_buffer(size)
            self.lpstrFile = cast( self.lpstrFileBuffer, c_wchar_p )
            self.nMaxFile = size
        def get_file_paths(self):
                results = []
                result = ""
                for ch in self.lpstrFileBuffer:
                        if u'\x00' == ch:
                                if "" == result:
                                        break
                                else:
                                        results.append( result )
                                        result = ""
                        else:
                                result += ch
                if 1 < len( results ):
                        import os
                        dirname = results[0]
                        results = [os.path.join( dirname, result ) for result in results[1:]]
                return results
        def set_file_title_size(self, size):
            self.lpstrFileTitleBuffer = create_unicode_buffer(size)
            self.lpstrFileTitle = cast( self.lpstrFileTitleBuffer, c_wchar_p )
            self.nMaxFileTitle = size
        def set_create_prompt(self):
            self.Flags |= 0x2000
        def set_over_write_prompt(self):
            self.Flags |= 0x2
        def set_path_must_exist(self):
            self.Flags |= 0x800
        def set_file_must_exist(self):
            self.Flags |= 0x1000
    ofn = OPENFILENAME()
    if is_multi_select:
        ofn.set_allow_multi_select()
    if windll.LoadLibrary("comdlg32").GetOpenFileNameW(byref( ofn )):
        if is_multi_select:
                return ofn.get_file_paths()
        else:
                return ofn.lpstrFile
    return None

if __name__ == '__main__':
    print ctypes_get_open_file_name( True )

こんな感じ。ポイントはlpstrFileにcreate_unicode_bufferをキャストしてつめているところ。 たとえばGetModuleFileNameとかにはcreate_unicode_bufferの戻り値をそのまま与えることができるが、構造体のメンバには_fields_で宣言した型に合わせたインスタンスを代入する必要がある。

が、create_unicode_bufferで返される配列には型を合わせてくれる機能がないのでキャストするとうまくいく。

チケットの履歴

更新者: mtamaki (2 年 前)

  • 説明 が変更されました (diff)
Note: チケットについてのヘルプは TracTickets を参照 して下さい。