読者です 読者をやめる 読者になる 読者になる

JavaからWindowsのレジストリにアクセスするライブラリ

JavaからWindowsのレジストリにアクセスするライブラリについて調査したのでまとめておきます。いくつかのライブラリがありますが、基本的にはJNI経由でWin32 APIを呼び出しています。

最初の二つはそのものズバリのライブラリです。Roxes Win32 for Javaは、DLLの関連か64bitバージョンのWindows(x64)では動作しませんでした(32bitのJREであれば問題ありません)。DLLをx64用にビルドしなおすことも可能なのでは?とは思いますが、、私のスキルの問題で今回はパスです。JRegistryは、x64用のDLLもあり一番よさそうですがライセンスがGPLです(RoxesはLGPL)。
SWTの内部にもWin32 APIのラッパークラスが存在しそれ経由でレジストリにアクセスできます。SWTであればx64用DLLもあります。ただし、読み込み系のAPIしかサポートされていないようです。
nlinkはレジストリアクセス専用ではありませんが、使用したいWindowsのAPIに対応するプロキシーを自動生成することでWindowsのAPIを利用できます。
まとめると以下のようになります。

方式 ライセンス 書き込み x64
Roxes Win32 for Java LGPL OK NG?
JRegistry GPL OK OK
SWT EPL(Eclipse Public License) NG OK
nlink MITライセンス OK? ??

以下、使い方のサンプルです。

Roxes Win32 for Java

こちらからjarファイルをダウンロードしてビルドパスに追加しておきます。IEのバージョンを取得する場合には以下のようなコードになります。

import com.roxes.win32.Registry;
import com.roxes.win32.Win32;
import com.roxes.win32.Win32Exception;

public class RoxesRegistryTest{
	public void testQuery() {
		Registry reg = new Registry( Registry.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Internet Explorer");
		try {
			System.out.println(reg.getStringValue("Version"));
		} catch (Win32Exception e) {
			e.printStackTrace
		}
}

nlink

nlinkは、使用したいAPIシグネチャをあわせたメソッドを定義したインタフェースを用意して、実行時にプロキシーを介してAPIにアクセスします。java.netの方からはソースしか取得できないのですが、seasar.orgのmavenリポジトリにもあるようなので、そちらからダウンロードします。
実際にレジストリを操作する方法ですが、オープンとクローズはできたのですが値を取得しようとすると、EXCEPTION_ACCESS_VIOLATIONで落ちてしまい動作させることができませんでした。コードは置いておきます。うまくできた方は教えてくださいm(__)m
まず、使用したいDLL名にあわせてインタフェースを作成し、@DllClassを付加します。メソッドには@DllMethodを付加します。正直なところ@MarshalAsの使い方はあやしいです。

import nlink.Holder;
import nlink.MarshalAs;
import nlink.NativeType;
import nlink.win32.DllClass;
import nlink.win32.DllMethod;

@DllClass
public interface Kernel32 {
	@DllMethod
	int RegOpenKeyEx(
			int hKey,
			String lpSubKey,
			int ulOptions,
			int samDesired,
			@MarshalAs(NativeType.Int32_ByRef) Holder<Integer> lpcbData);

	@DllMethod
	int RegCloseKey(
		int hkey
	);

	@DllMethod
	int RegQueryValueEx(
			int hKey,
			String lpValueName,
			@MarshalAs(NativeType.Int32_ByRef) Integer lpReserved,
			@MarshalAs(NativeType.Int32_ByRef) Integer lpType,
			@MarshalAs(NativeType.PVOID) byte[] lpData,
			@MarshalAs(NativeType.Int32_ByRef) Holder<Integer> lpcbData
	);

}

このインタフェースを使うコードは以下です。NLink.create()でプロキシーを取得します。インタフェースの方に間違いがあると、createのタイミングで例外が発生します。

import nlink.Holder;
import nlink.win32.NLink;

public class RoxesRegistryTest{
	public void testQuery() {
		Kernel32 proxy = NLink.create(Kernel32.class);

		Holder<Integer> phkResult = new Holder<Integer>();
		try {
			int result = proxy.RegOpenKeyEx(-2147483646, "SOFTWARE\\Microsoft\\Internet Explorer", 0, 0xF003F, phkResult);
			if(result == 0 && phkResult.value != null){
				byte[] data = new byte[256];
				Holder<Integer> length = new Holder<Integer>();
				int rc = proxy.RegQueryValueEx(phkResult.value, null, null, null, data, length); // ここでEXCEPTION_ACCESS_VIOLATION
			}
		} finally{
			if(phkResult.value != null){
				System.out.println(proxy.RegCloseKey(phkResult.value));
			}
		}

	}

うまくいけばdataに値が入ってくるはずなのですが。。

SWT

SWTでレジストリの値を取得 - a-sanの日記
上記のサイトを参考にIEのバージョンを取得しようとすると以下のようなコードになります。参照系のAPIしか用意されていないようです。

import org.eclipse.swt.internal.win32.OS;

public class RegistryTest{
    public static void main(String[] args){
        int hkey = OS.HKEY_LOCAL_MACHINE;
        String entry = "SOFTWARE\\Microsoft\\Internet Explorer";
        String name = "Version";
        int[] phkeyResult = new int[1];
        try {
            int rc = OS.RegOpenKeyEx(
            		hkey ,
                    new TCHAR(OS.CP_INSTALLED, entry, true),
                    0,
                    0xF003F/*KEY_ALL_ACCESS*/,
                    phkeyResult);
            if(phkeyResult[0] != 0){
                TCHAR buf = new TCHAR(OS.CP_INSTALLED, 256);
                int[] len = new int[] { 256 };
                rc = OS.RegQueryValueEx(phkeyResult[0],
                        new TCHAR(OS.CP_INSTALLED, name, true), 0, null, buf, len);
                System.out.println(rc ==0 ? buf.toString(0, len[0]/TCHAR.sizeof-1) : "no value exist.");
            }
        } finally {
            if (phkeyResult[0] != 0) OS.RegCloseKey(phkeyResult[0]);
        }
    }
}

APIについて

APIについてはMSDNの方を参照。

番外

あとは、Windowsのregコマンドを実行してパースするという方法もアリかもしれませんね。この場合は、Windows 2000みたくregコマンドが標準で入っていない環境があったり、OSのバージョンや言語によって結果が異なる可能性がありそうですが。

注意点

Windows Vista以降だと管理者として実行しないとレジストリにアクセスできないようです。