winreg でレジストリを読み書きするサンプルコード
標準ライブラリに入ってる winreg を使うとレジストリの読み書きができるけど、こいつは Windows API を薄く包んでいるだけなのでリファレンスを見ても使い方がさっぱりわからないという。
いろいろ調べて、おそらくこうするのが正しいと思われるサンプルコードを書いた。
目次
- 値の読み込み
- 値の書き込み
- コンテキスト・マネージャ
- キーの作成
- OpenKeyEx と CreateKeyEx の違い
- 値の削除
- キーの削除
- 値の列挙
- キーの列挙
- 既定値
- REG_EXPAND_SZ
- REG_BINARY
- REG_MULTI_SZ
- サブキーを開く
値の読み込み
# 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 API の RegOpenKeyEx を使っています。
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 は中身が違いますが、オプション機能を使わなければ同じはずです。
値の列挙
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_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 でも同様です。
参考
- winreg.c - winregモジュールの本体。
- winreg.c.h - Python側の引数解析。