winreg でレジストリを読み書きするサンプルコード

標準ライブラリに入ってる winreg を使うとレジストリの読み書きができるけど、こいつは Windows API を薄く包んでいるだけなのでリファレンスを見ても使い方がさっぱりわからないという。

いろいろ調べて、おそらくこうするのが正しいと思われるサンプルコードを書いた。

目次

値の読み込み

# Python 3.6
import winreg
path = r'Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
key = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path)
data, regtype = winreg.QueryValueEx(key, 'Personal')
print('種類:', regtype)
print('データ:', data)
winreg.CloseKey(key)  # key.Close() と書いても同じ

OpenKey と OpenKeyEx はまったく同じです。好きな方を使ってください。両方とも内部では Windows APIRegOpenKeyEx を使っています。

QueryValue と QueryValueEx は中身が違います。Ex がつかない方は16bit時代の遺物なので使いません。

値の書き込み

import winreg
path = r'Software\7-Zip\FM'
key = winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path, access=winreg.KEY_WRITE)
winreg.SetValueEx(key, 'PanelPath0', 0, winreg.REG_SZ, 'C:\Program Files')
winreg.CloseKey(key)

値をいじる場合は、access=winreg.KEY_WRITE をつけてください。デフォルトでは KEY_READ なので書き込めません。OpenKeyEx の代わりに後述のCreateKeyExを使ってもいいです。

Ex の付かない SetValue は16bit時代の遺物なので使いません。SetValueEx を使ってください。

コンテキスト・マネージャ

import winreg
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, r'Software') as key:
    pass  # key を使った処理をここで行う

PyHKEY 型は with 文と一緒に使うと自動で CloseKey してくれます。

キーの作成

import winreg
newkey = winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, r'Software\__test__')
winreg.CloseKey(newkey)

CreateKeyEx は作ったキーを開いて返すので、閉じるのを忘れないように。

Ex の付かない CreateKey は16bit時代の遺物なので使いません。

OpenKeyEx と CreateKeyEx の違い

OpenKeyEx → これから開こうとするキーは既に存在しているはず。もし存在しなければエラー。値の閲覧用として使いやすい。
CreateKeyEx → これから開こうとするキーは存在しないかもしれない。もし存在しなければ自動で作成する。値の書き込み用として使いやすい。

import winreg

path = r'Software\__test2__'

with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    winreg.SetValueEx(key, 'name1', 0, winreg.REG_SZ, 'data1')
    print('キーを開いて値を書き込んだよ')

with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    data, _ = winreg.QueryValueEx(key, 'name1')
    print('読み取ったデータ:', data)

値の削除

import winreg
path = r'Software\__test2__'
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path, access=winreg.KEY_SET_VALUE) as key:
    winreg.DeleteValue(key, 'name1')

キーの削除

import winreg
winreg.DeleteKey(winreg.HKEY_CURRENT_USER, r'Software\__test2__')

DeleteKey と DeleteKeyEx は中身が違いますが、オプション機能を使わなければ同じはずです。

参考:32bit プロセス(WOW64 プロセス) から、64bit プロセス用のレジストリキーの参照

値の列挙

2つのやり方があるので、どちらか好きな方を選んでください。

1つ目は、予めキーの数を調べてから取得する方法です。

import winreg
path = r'Control Panel\Cursors'
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    n_subkeys, n_subvalues, timestamp = winreg.QueryInfoKey(key)
    for i in range(n_subvalues):
        name, data, regtype = winreg.EnumValue(key, i)
        print('{:>25s} → {}'.format(name, data))

2つ目は、エラーになるまで取得を繰り返す方法です。

import winreg
import itertools

ERROR_NO_MORE_ITEMS = 259
path = r'Control Panel\Cursors'

with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    for i in itertools.count():
        try:
            name, data, regtype = winreg.EnumValue(key, i)
        except WindowsError as err:
            if err.winerror == ERROR_NO_MORE_ITEMS:
                break
            raise
        print('{:>25s} → {}'.format(name, data))

キーの列挙

これも2つのやり方があるので、どちらか好きな方を選んでください。

import winreg
path = r'Software\Microsoft\Windows\CurrentVersion\Uninstall'
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    n_subkeys, n_subvalues, timestamp = winreg.QueryInfoKey(key)
    for i in range(n_subkeys):
        subkey = winreg.EnumKey(key, i)
        print('-', subkey)
import winreg
import itertools

ERROR_NO_MORE_ITEMS = 259
path = r'Software\Microsoft\Windows\CurrentVersion\Uninstall'

with winreg.OpenKey(winreg.HKEY_CURRENT_USER, path) as key:
    for i in itertools.count():
        try:
            subkey = winreg.EnumKey(key, i)
        except WindowsError as err:
            if err.winerror == ERROR_NO_MORE_ITEMS:
                break
            raise
        print('-', subkey)

ちなみに、キーや値を列挙しながら削除していく時は、インデックスを逆順に回します。前からやるとインデックス番号がずれて困ります。

既定値

import winreg

# 読み
with winreg.OpenKeyEx(winreg.HKEY_CLASSES_ROOT, r'Python.File') as key:
    name = ''  # Noneでも大丈夫
    data, regtype = winreg.QueryValueEx(key, name)
    print(data)

# 書き
with winreg.OpenKeyEx(winreg.HKEY_CLASSES_ROOT, r'Python.File', access=winreg.KEY_SET_VALUE) as key:
    name = ''  # Noneでも大丈夫
    winreg.SetValueEx(key, name, 0, winreg.REG_SZ, 'ぱいそん ふぁいる')

名前欄を空にすれば既定値の読み書きができます。

後ろに Ex の付かない QueryValue と SetValue を使っても既定値は操作できますが、この2つは中のAPIが古いので使いません。

中の人のブログによれば、レジストリに既定値(もしくは既定の値)が存在するのは歴史的な理由によるものなんだそうだ。大昔の Windows では、キーと値は1対1に対応していた。しかし32ビットの Windows では1つのキーで複数の値を持てるようにした。それで値同士を識別するのに「値の名前」が作られ、さらに従来のキーだけでアクセスしていた値は「既定値」になったということらしい。PowerShellレジストリの値がプロパティ扱いされるのも、この辺りに理由がありそうな気がする。

REG_EXPAND_SZ

import winreg
with winreg.OpenKey(winreg.HKEY_CURRENT_USER, r'Environment') as key:
    data, regtype = winreg.QueryValueEx(key, 'temp')
    print('展開前:', data)
    if regtype == winreg.REG_EXPAND_SZ:
        data_exp = winreg.ExpandEnvironmentStrings(data)
        print('展開後:', data_exp)

REG_EXPAND_SZ は、環境変数を含むかもしれない文字列です。環境変数の展開は自分でやります。

REG_BINARY

import winreg
path = r'Software\__test__'

# 書き込み
with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    winreg.SetValueEx(key, 'name1', 0, winreg.REG_BINARY, b'binary data here')

# 読み込み
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    data, _ = winreg.QueryValueEx(key, 'name1')
    print('読み取ったデータ:', data)

bytes型です。

REG_MULTI_SZ

import winreg
path = r'Software\__test__'

# 書き込み
with winreg.CreateKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    L = ['foo', 'bar', 'baz']
    winreg.SetValueEx(key, 'name2', 0, winreg.REG_MULTI_SZ, L)

# 読み込み
with winreg.OpenKeyEx(winreg.HKEY_CURRENT_USER, path) as key:
    data, _ = winreg.QueryValueEx(key, 'name2')
    print('読み取ったデータ:', data)

文字列のリストです。空文字列や \0 を含む文字列を混ぜるとバグります。

サブキーを開く

import winreg

k0 = winreg.HKEY_CURRENT_USER
k1 = winreg.OpenKeyEx(k0, 'Software')
k2 = winreg.OpenKeyEx(k1, 'Microsoft')
k3 = winreg.OpenKeyEx(k2, 'Windows')
k4 = winreg.OpenKeyEx(k3, 'CurrentVersion\Run')

for i in k1, k2, k3, k4:
    winreg.CloseKey(i)

このように、あるキーのサブキーを順繰りに開いていくことができます。CreateKeyEx でも同様です。

参考