銀月の符号

Python 使い見習いの日記・雑記

Python 2.6 の _winreg.ExpandEnvironmentStrings を ctypes で

Python 2.6 より _winreg モジュールに追加された ExpandEnvironmentStrings 関数。REG_EXPAND_SZ 型のレジストリ値のように % で囲まれた環境変数を展開するというもの。ソースコードを見ると PC/_winreg.c の 1159 行目からの PyExpandEnvironmentStrings 関数で実装されていた。 ExpandEnvironmentStringsW 関数を2度呼んでいる。ひとまず lpDst を NULL ポインタ、 nSize を 0 で呼び出しておいて得られる文字列長を事前確認、その長さ分のメモリ確保、再度実行という流れ。なるほどこうやって使うのか、と。コード読むのはホント、ためになる。

そして、意味も無く作ってみた。このくらいならば ctypes でも実装できそうな気がして。いや、 Python 2.3 以上で使えるのでまったくの無駄でもないか…。C コンパイラに頼らずともさくっと API アクセスできてしまう ctypes 。なんという Python 界ひきこもり用モジュール。いけない、 C, C++ もたまにはかかないと。

# coding: utf-8

from ctypes import windll, POINTER, WINFUNCTYPE
from ctypes import c_wchar_p, create_unicode_buffer
from ctypes.wintypes import DWORD

__all__ = ['ExpandEnvironmentStrings']

LPCTSTR = LPTSTR = c_wchar_p

_ExpandEnvironmentStringsW = WINFUNCTYPE(DWORD, LPCTSTR, LPTSTR, DWORD)(
        ('ExpandEnvironmentStringsW', windll.kernel32),
        ((1, 'lpSrc'), (1, 'lpDst'), (1, 'nSize')))
def _errcheck(result, func, args):
    if not result:
        raise WindowsError('ExpandEnvironmentStrings')
    return args
_ExpandEnvironmentStringsW.errcheck = _errcheck

def ExpandEnvironmentStrings(unicode):
    size = _ExpandEnvironmentStringsW(lpSrc=unicode, lpDst=None, nSize=0)
    dst = create_unicode_buffer(size)
    _ExpandEnvironmentStringsW(lpSrc=unicode, lpDst=dst, nSize=size)
    return dst.value

def _test():
    o = ExpandEnvironmentStrings(u'%Path%')
    print repr(o)

if __name__ == '__main__':
    _test()

追記、そして、わざわざ ctypes を使わなくても正規表現と os.getenv の組み合わせで何とでもなってしまいそうだということに気づいたのは1日後のことでした。