#!python # -*- mode: python; Encoding: utf-8; coding: utf-8 -*- # Last updated: <2025/12/06 13:52:05 +0900> """ ctypes sample. use GetOpenFileNameW() Python: ctypesパターン集 #Windows - Qiita https://qiita.com/everylittle/items/6e18ba23f38502c18f3e How to get null terminated strings from a buffer? - Python Help - Discussions on Python.org https://discuss.python.org/t/how-to-get-null-terminated-strings-from-a-buffer/26904/3 Windows11 x64 25H2 + Python 3.10.10 64bit """ import ctypes import os # ファイルダイアログの動作を指定するための定数 OFN_ALLOWMULTISELECT = 0x00000200 OFN_FILEMUSTEXIST = 0x00001000 OFN_PATHMUSTEXIST = 0x00000800 OFN_EXPLORER = 0x00080000 # 受け取るファイルパスを格納するバッファのサイズ BUFFER_SIZE = 32768 class OPENFILENAME(ctypes.Structure): """GetOpenFileNameW()を呼び出すために必要な構造体を定義""" _fields_ = [ ("lStructSize", ctypes.c_uint32), ("hwndOwner", ctypes.c_void_p), ("hInstance", ctypes.c_void_p), ("lpstrFilter", ctypes.c_wchar_p), ("lpstrCustomFilter", ctypes.c_wchar_p), ("nMaxCustFilter", ctypes.c_uint32), ("nFilterIndex", ctypes.c_uint32), ("lpstrFile", ctypes.c_wchar_p), ("nMaxFile", ctypes.c_uint32), ("lpstrFileTitle", ctypes.c_wchar_p), ("nMaxFileTitle", ctypes.c_uint32), ("lpstrInitialDir", ctypes.c_wchar_p), ("lpstrTitle", ctypes.c_wchar_p), ("Flags", ctypes.c_uint32), ("nFileOffset", ctypes.c_uint16), ("nFileExtension", ctypes.c_uint16), ("lpstrDefExt", ctypes.c_wchar_p), ("lCustData", ctypes.c_void_p), ("lpfnHook", ctypes.c_void_p), ("lpTemplateName", ctypes.c_wchar_p), ("pvReserved", ctypes.c_void_p), ("dwReserved", ctypes.c_uint32), ("FlagsEx", ctypes.c_uint32), ] def get_openfilename(initial_dir=None, filters=None): """ファイルダイアログを開いて複数ファイルを選択""" comdlg32 = ctypes.WinDLL("comdlg32") comdlg32.GetOpenFileNameW.restype = ctypes.c_bool comdlg32.GetOpenFileNameW.argtypes = (ctypes.POINTER(OPENFILENAME),) ofn = OPENFILENAME() lenFilenameBufferInChars = BUFFER_SIZE buf = ctypes.create_unicode_buffer(lenFilenameBufferInChars) ofn.lStructSize = ctypes.sizeof(OPENFILENAME) # ファイル種類のフィルターを設定 if filters: ofn.lpstrFilter = "\0".join(filters) + "\0\0" else: ofn.lpstrFilter = "All files {*.*}\0*.*\0\0" if initial_dir: ofn.lpstrInitialDir = initial_dir ofn.lpstrFile = ctypes.cast(buf, ctypes.c_wchar_p) ofn.nMaxFile = lenFilenameBufferInChars ofn.lpstrTitle = "Select file" ofn.Flags = ( OFN_ALLOWMULTISELECT | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST ) ret = comdlg32.GetOpenFileNameW(ofn) files = [] if ret: # 得られたファイルパスを分割してリストにする s = buf[:].rstrip("\0") path = s[: ofn.nFileOffset].rstrip("\0") filenames = s[ofn.nFileOffset :].split("\0") files = [os.path.abspath(os.path.join(path, f)) for f in filenames] # リストの形でファイルパス群を返す return files return [] def main(): filters = [ "All files {*.*}", "*.*", "Text {*.txt}", "*.txt", ] homedir = os.path.expanduser("~") print(f"Initial Directory : {homedir}") files = get_openfilename(initial_dir=homedir, filters=filters) if files: print(files) else: print("Cancel.") if __name__ == "__main__": main()