Java操作注册表

从JDK 1.4开始,Java在java.util下加入了一个专门处理用户和系统配置信息的java.util.prefs包,其中一个类Preferences是一种比较“高级”的玩意。从本质上讲,Preferences本身是一个与平台无关的东西,但不同的OS对它的SPI(Service Provider Interface)的实现却是与平台相关的,因此,在不同的系统中你可能看到首选项保存为本地文件、LDAP目录项、数据库条目等,像在Windows平台下,它就保存到了系统注册表中,Linux中它存在于用户目录下的一个隐藏文件中。
本文仅讨论windows下的注册表操作,使用Preferences只能操作HKEY_LOCAL_MACHINE\SOFTWARE\Javasoft\PrefsHKEY_CURRENT_USER\SOFTWARE\Javasoft\Prefs下的信息,存储自身应用的偏好设置是没有问题的,但是如果要操作其他注册表项就不行,但是在windows环境中有一个reg可以用来操作注册表,我们可以通过java.lang.Process调用该程序程序完成间接的操作注册表,下面我们先来看一下这个命令。

第一部分 REG

REG Operation [Parameter List]
  Operation  [ QUERY   | ADD    | DELETE  | COPY    |
               SAVE    | LOAD   | UNLOAD  | RESTORE |
               COMPARE | EXPORT | IMPORT  | FLAGS ]
返回代码: (除了 REG COMPARE)
  0 - 成功
  1 - 失败
要得到有关某个操作的帮助,请键入:
  REG Operation /?
例如:
  REG QUERY /?
  REG ADD /?
  REG DELETE /?
  REG COPY /?
  REG SAVE /?
  REG RESTORE /?
  REG LOAD /?
  REG UNLOAD /?
  REG COMPARE /?
  REG EXPORT /?
  REG IMPORT /?
  REG FLAGS /?

上述为reg所有的可用操作,接下来我们具体的看一下常用的一些操作的说明:

1.1 query 查询

REG QUERY KeyName [/v [ValueName] | /ve] [/s]
          [/f Data [/k] [/d] [/c] [/e]] [/t Type] [/z] [/se Separator]

  KeyName  [\\Machine\]FullKey
           Machine - 远程机器名称,省略当前机器的默认值。在远程机器上
                     只有 HKLM 和 HKU 可用。
           FullKey - 以 ROOTKEY\SubKey 名称形式
                ROOTKEY - [ HKLM | HKCU | HKCR | HKU | HKCC ]
                SubKey  - 在选择的 ROOTKEY 下的注册表项的全名

  /v       具体的注册表项值的查询。
           如果省略,会查询该项的所有值。

           只有与 /f 开关一起指定的情况下,此开关的参数才是可选的。它指定
           只在值名称中搜索。

  /ve      查询默认值或空值名称(默认)。

  /s       循环查询所有子项和值(如 dir /s)。

  /se      为 REG_MULTI_SZ 在数据字符串中指定分隔符(长度只为 1 个字符)。
           默认分隔符为 "\0"。

  /f       指定搜索的数据或模式。
           如果字符串包含空格,请使用双引号。默认为 "*"。

  /k       指定只在项名称中搜索。

  /d       指定只在数据中搜索。

  /c       指定搜索时区分大小写。
           默认搜索为不区分大小写。

  /e       指定只返回完全匹配。
           默认是返回所有匹配。

  /t       指定注册表值数据类型。
           有效的值是:
             REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ,
             REG_DWORD, REG_QWORD, REG_BINARY, REG_NONE
           默认为所有类型。

  /z       详细: 显示值名称类型的数字等值。

示例:

  REG QUERY HKLM\Software\Microsoft\ResKit /v Version
    显示注册表值版本的值

  REG QUERY \\ABC\HKLM\Software\Microsoft\ResKit\Nt\Setup /s
    显示远程机器 ABC 上的、在注册表项设置下的所有子项和值

  REG QUERY HKLM\Software\Microsoft\ResKit\Nt\Setup /se #
    用 "#" 作为分隔符,显示类型为 REG_MULTI_SZ 的所有值名称的所有
    子项和值。

  REG QUERY HKLM /f SYSTEM /t REG_SZ /c /e
    以区分大小写的形式显示项、值和数据和数据类型 REG_SZ
    的、在 HKLM 更目录下的、"SYSTEM" 出现的精确次数

  REG QUERY HKCU /f 0F /d /t REG_BINARY
    显示在 HKCU 根目录下、数据类型为 REG_BINARY 的数据的项、值和
    数据的 "0F" 出现的次数。

  REG QUERY HKLM\SOFTWARE /ve 
    显示在 HKLM\SOFTWARE 下的项、值和数据(默认)

1.2 add 添加

REG ADD KeyName [/v ValueName | /ve] [/t Type] [/s Separator] [/d Data] [/f]

  KeyName  [\\Machine\]FullKey
           Machine  远程机器名 - 忽略默认到当前机器。远程机器上
                    只有 HKLM 和 HKU。
           FullKey  ROOTKEY\SubKey
           ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
           SubKey   所选 ROOTKEY 下注册表项的完整名。

  /v       所选项之下要添加的值名。

  /ve      为注册表项添加空白值名(默认)。

  /t       RegKey 数据类型
           [ REG_SZ    | REG_MULTI_SZ | REG_EXPAND_SZ |
             REG_DWORD | REG_QWORD    | REG_BINARY    | REG_NONE ]
           如果忽略,则采用 REG_SZ。

  /s       指定一个在 REG_MULTI_SZ 数据字符串中用作分隔符的字符
           如果忽略,则将 "\0" 用作分隔符。

  /d       要分配给添加的注册表 ValueName 的数据。

  /f       不用提示就强行覆盖现有注册表项。

例如:

  REG ADD \\ABC\HKLM\Software\MyCo
    添加远程机器 ABC 上的一个注册表项 HKLM\Software\MyCo

  REG ADD HKLM\Software\MyCo /v Data /t REG_BINARY /d fe340ead
    添加一个值(名称: Data,类型: REG_BINARY,数据: fe340ead)

  REG ADD HKLM\Software\MyCo /v MRU /t REG_MULTI_SZ /d fax\0mail
    添加一个值(名称: MRU,类型: REG_MUTLI_SZ,数据: fax\0mail\0\0)

  REG ADD HKLM\Software\MyCo /v Path /t REG_EXPAND_SZ /d ^%systemroot^%
    添加一个值(名称: Path,类型: REG_EXPAND_SZ,数据: %systemroot%)
    注意: 在扩充字符串中使用插入符号 ( ^ )

1.3 delete 删除

REG DELETE KeyName [/v ValueName | /ve | /va] [/f]

  KeyName    [\\Machine\]FullKey
    Machine  远程机器名 - 忽略当前机器的默认值。
             远程机器上只有 HKLM 和 HKU。
    FullKey  ROOTKEY\SubKey
    ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
    SubKey   所选 ROOTKEY 下的注册表项的全名。
  ValueName  所选项下的要删除的值的名称。
             省略时,该项下的所有子项和值都会被删除。

  /ve        删除空白值名称的值(默认)。

  /va        删除该项下的所有值。

  /f         不用提示就强行删除。

例如:

  REG DELETE HKLM\Software\MyCo\MyApp\Timeout
    删除注册表项 Timeout 及其所有子项和值

  REG DELETE \\ZODIAC\HKLM\Software\MyCo /v MTU
    删除 ZODIAC 上 MyCo 下的注册表项 MTU

1.4 copy 复制

REG COPY KeyName1 KeyName2 [/s] [/f]

  KeyName    [\\Machine\]FullKey
    Machine  远程机器名 - 忽略当前机器的默认值。
             远程机器上只有 HKLM 和 HKU。
    FullKey  ROOTKEY\SubKey
    ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
    SubKey   所选 ROOTKEY 下的注册表项的全名。
  /s         复制所有子项和值。

  /f         不用提示就强行复制。

例如:

  REG COPY HKLM\Software\MyCo\MyApp HKLM\Software\MyCo\SaveMyApp /s
    将注册表项 MyApp 下的所有子项和值复制到注册表项 SaveMyApp


  REG COPY \\ZODIAC\HKLM\Software\MyCo HKLM\Software\MyCo1
    将 ZODIAC 上注册表项 MyCo 下的所有值复制到当前机器上的
    注册表项 MyCo1

1.5 save 保存

REG SAVE KeyName FileName [/y]

  KeyName    ROOTKEY\SubKey
    ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
    SubKey   所选 ROOTKEY 下的注册表项的全名。
  FileName   要保存的磁盘文件名。如果没有指定路径,文件会在调用进程的
             当前文件夹中得到创建。

  /y         不用提示就强行覆盖现有文件。

例如:

  REG SAVE HKLM\Software\MyCo\MyApp AppBkUp.hiv
    将配置单元 MyApp 保存到当前文件夹中的文件 AppBkUp.hiv

1.6 restore 恢复

REG RESTORE KeyName FileName

  KeyName    ROOTKEY\SubKey (只是本地机器)
    ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
    SubKey   要将配置单元文件还原到的注册表项全名。
             覆盖现有项的值和子项。

  FileName   要还原的配置单元文件名。
             您必须使用 REG SAVE 来创建这个文件。

例如:

  REG RESTORE HKLM\Software\Microsoft\ResKit NTRKBkUp.hiv
    还原文件 NTRKBkUp.hiv,覆盖注册表项 ResKit

1.7 load 加载

REG LOAD KeyName FileName

  KeyName    ROOTKEY\SubKey (只是本地机器)
    ROOTKEY  [ HKLM | HKU ]
    SubKey   要将配置单元文件加载进的注册表项名称。创建一个新的注册表项。

  FileName   要加载的配置单元文件名。
             您必须使用 REG SAVE 来创建这个文件。

例如:

  REG LOAD HKLM\TempHive TempHive.hiv
    将文件 TempHive.hiv 加载到注册表项 HKLM\TempHive

1.8 unload 卸载

REG UNLOAD KeyName

  KeyName    ROOTKEY\SubKey (只是本地机器)
    ROOTKEY  [ HKLM | HKU ]
    SubKey   要卸载的配置单元的注册表项名称。

例如:

  REG UNLOAD HKLM\TempHive
    卸载 HKLM 中的配置单元 TempHive

1.9 compare 比较

REG COMPARE KeyName1 KeyName2 [/v ValueName | /ve] [Output] [/s]

  KeyName    [\\Machine\]FullKey
    Machine  远程机器名 - 省略当前机器的默认值。
             远程机器上只有 HKLM 和 HKU。
    FullKey  ROOTKEY\SubKey
             如果没有指定 FullKey2,FullKey2 则跟 FullKey1 相同。
    ROOTKEY  [ HKLM | HKCU | HKCR | HKU | HKCC ]
    SubKey   所选 ROOTKEY 下的注册表项的全名。

  ValueName  所选注册表项下的要比较的值的名称。
             省略时,该项下的所有值都会得到比较。

  /ve        比较空白值名称的值(默认)。

  /s         比较所有子项和值。

  Output     [/oa | /od | /os | /on]
             省略时,只显示不同的结果。
    /oa      显示所有不同和匹配结果。
    /od      只显示不同的结果。
    /os      只显示匹配结果。
    /on      不显示结果。

返回代码:

  0 - 成功,比较的结果相同
  1 - 失败
  2 - 成功,比较的结果不同

注意:
  每个输出行前面显示的符号定义为:
  = 表示 FullKey1 等于 FullKey2 数据
  < 指的是 FullKey1 数据,与 FullKey2 数据不同
  > 指的是 FullKey2 数据,与 Fullkey1 数据不同

例如:

  REG COMPARE HKLM\Software\MyCo\MyApp HKLM\Software\MyCo\SaveMyApp
    将注册表项 MyApp 下的所有值跟 SaveMyApp 比较

  REG COMPARE HKLM\Software\MyCo HKLM\Software\MyCo1 /v Version
    比较注册表项 MyCo 和 MyCo1 下的值 Version

  REG COMPARE \\ZODIAC\HKLM\Software\MyCo \\. /s
    将 ZODIAC 上 HKLM\Software\MyCo 下的所有子项和值和当前机器上
    的相同项比较

1.10 export 导出

REG IMPORT FileName

  FileName  要导入的磁盘文件名(只是本地机器)。

例如:

  REG IMPORT AppBkUp.reg
    从文件 AppBkUp.reg 导入注册表项

1.11 import 导入

REG IMPORT FileName

  FileName  要导入的磁盘文件名(只是本地机器)。

例如:

  REG IMPORT AppBkUp.reg
    从文件 AppBkUp.reg 导入注册表项

第二部分 JAVA封装

了解了reg的相关操作命令以及可用参数,下面我们就可以对其进行封装,方便我们调用。
首先我们先抽象出一个接口,用于将可选参数转换为选项命令部分

package com.jianggujin.registry;

/**
 * 附加选项
 * 
 * @author jianggujin
 * 
 */
public interface JOptions {
   String toOptions();
}

然后对操作的相关选项进行封装:

package com.jianggujin.registry;

/**
 * 查询选项
 * 
 * @author jianggujin
 * 
 */
public class JQueryOptions implements JOptions {
   // 值相关选项
   private boolean v = false;
   private String valueName;
   private boolean ve = false;

   // 搜索相关查询选项
   private String data;
   private boolean f = false, k = false, d = false, c = false, e = false;

   // 值循环查询所有子项和值
   private boolean s = false;

   // 指定注册表值数据类型
   private JValueType type;
   private boolean t = false;

   // 详细: 显示值名称类型的数字等值
   private boolean z = false;

   // 为 REG_MULTI_SZ 在数据字符串中指定分隔符(长度只为 1 个字符)。 默认分隔符为 "\0"
   private Character separator;
   private boolean se = false;

   /**
    * 具体的注册表项值的查询。 如果省略,会查询该项的所有值
    * 
    * @param valueName
    */
   public JQueryOptions useV(String valueName) {
      this.v = true;
      this.valueName = valueName;
      this.ve = false;
      return this;
   }

   /**
    * 查询默认值或空值名称(默认)
    */
   public JQueryOptions useVE() {
      this.ve = true;
      this.v = false;
      return this;
   }

   /**
    * 指定搜索的数据或模式。 如果字符串包含空格,请使用双引号。默认为 "*"。
    * 
    * @param data
    */
   public JQueryOptions useF(String data) {
      this.f = true;
      this.data = data;
      return this;
   }

   /**
    * 指定只在项名称中搜索,需要执行{@link #useF(String)}
    * 
    * @return
    */
   public JQueryOptions useK() {
      this.k = true;
      return this;
   }

   /**
    * 指定只在数据中搜索,需要执行{@link #useF(String)}
    * 
    * @return
    */
   public JQueryOptions useD() {
      this.d = true;
      return this;
   }

   /**
    * 指定搜索时区分大小写。 默认搜索为不区分大小写,需要执行{@link #useF(String)}
    * 
    * @return
    */
   public JQueryOptions useC() {
      this.c = true;
      return this;
   }

   /**
    * 指定只返回完全匹配。 默认是返回所有匹配,需要执行{@link #useF(String)}
    * 
    * @return
    */
   public JQueryOptions useE() {
      this.e = true;
      return this;
   }

   /**
    * 循环查询所有子项和值
    * 
    * @return
    */
   public JQueryOptions useS() {
      this.s = true;
      return this;
   }

   /**
    * 指定注册表值数据类型。 默认为所有类型。
    * 
    * @param type
    * @return
    */
   public JQueryOptions useT(JValueType type) {
      this.t = true;
      this.type = type;
      return this;
   }

   /**
    * 详细: 显示值名称类型的数字等值
    * 
    * @return
    */
   public JQueryOptions useZ() {
      this.z = true;
      return this;
   }

   /**
    * 为 REG_MULTI_SZ 在数据字符串中指定分隔符(长度只为 1 个字符)。 默认分隔符为 "\0"
    * 
    * @param separator
    * @return
    */
   public JQueryOptions useSE(Character separator) {
      this.separator = separator;
      this.se = true;
      return this;
   }

   @Override
   public String toOptions() {
      StringBuilder builder = new StringBuilder();
      if (this.v && this.valueName != null && this.valueName.length() > 0) {
         builder.append("/v ").append(this.valueName);
      }
      if (this.ve) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/ve");
      }

      if (this.f && this.data != null && this.data.length() > 0) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/f ").append(this.data);
         if (this.k) {
            builder.append(" /k");
         }
         if (this.d) {
            builder.append(" /d");
         }
         if (this.c) {
            builder.append(" /c");
         }
         if (this.e) {
            builder.append(" /e");
         }
      }

      if (this.s) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/s");
      }

      if (this.t && this.type != null) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         return "/t " + type.name();
      }

      if (this.z) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/z");
      }

      if (this.se && this.separator != null) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         return "/se " + separator;
      }
      return builder.toString();
   }

}
package com.jianggujin.registry;

/**
 * 添加选项
 * 
 * @author jianggujin
 * 
 */
public class JAddOptions implements JOptions {
   private boolean v = false;
   private String valueName;

   private boolean ve = false;

   private JValueType type;
   private boolean t = false;

   private Character separator;
   private boolean s = false;

   private boolean d = false;
   private String data;

   private boolean f = false;

   /**
    * 所选项之下要添加的值名
    * 
    * @param valueName
    */
   public JAddOptions useV(String valueName) {
      this.v = true;
      this.valueName = valueName;
      this.ve = false;
      return this;
   }

   /**
    * 为注册表项添加空白值名(默认)
    */
   public JAddOptions useVE() {
      this.ve = true;
      this.v = false;
      return this;
   }

   /**
    * RegKey 数据类型,如果忽略,则采用 REG_SZ
    * 
    * @param type
    * @return
    */
   public JAddOptions useT(JValueType type) {
      this.t = true;
      this.type = type;
      return this;
   }

   /**
    * 指定一个在 REG_MULTI_SZ 数据字符串中用作分隔符的字符 如果忽略,则将 "\0" 用作分隔符
    * 
    * @param separator
    * @return
    */
   public JAddOptions useS(Character separator) {
      this.separator = separator;
      this.s = true;
      return this;
   }

   /**
    * 要分配给添加的注册表 ValueName 的数据
    * 
    * @param data
    * @return
    */
   public JAddOptions useD(String data) {
      this.d = true;
      this.data = data;
      return this;
   }

   /**
    * 不用提示就强行覆盖现有注册表项
    * 
    * @return
    */
   public JAddOptions useF() {
      this.f = true;
      return this;
   }

   @Override
   public String toOptions() {
      StringBuilder builder = new StringBuilder();
      if (this.v && this.valueName != null && this.valueName.length() > 0) {
         builder.append("/v ").append(this.valueName);
      }
      if (this.ve) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/ve");
      }

      if (this.t && this.type != null) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         return "/t " + type.name();
      }

      if (this.s && this.separator != null) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         return "/s " + separator;
      }

      if (this.d && this.data != null && this.data.length() > 0) {
         builder.append("/d ").append(this.data);
      }
      if (this.f) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/f");
      }

      return builder.toString();
   }

}
package com.jianggujin.registry;

/**
 * 删除选项
 * 
 * @author jianggujin
 *
 */
public class JDeleteOptions implements JOptions {
   private boolean v;
   private String valueName;

   private boolean ve;
   private boolean va;

   private boolean f;

   /**
    * 所选项之下要删除的值的名称。省略时,该项下的所有子项的值都会被删除
    * 
    * @param valueName
    */
   public JDeleteOptions useV(String valueName) {
      this.v = true;
      this.valueName = valueName;
      this.ve = false;
      this.va = false;
      return this;
   }

   /**
    * 删除空值名称的值(默认)
    * 
    * @return
    */
   public JDeleteOptions useVE() {
      this.ve = true;
      this.v = false;
      this.va = false;
      return this;
   }

   /**
    * 删除该项下面的所有值
    * 
    * @return
    */
   public JDeleteOptions useVA() {
      this.va = true;
      this.v = false;
      this.ve = false;
      return this;
   }

   /**
    * 不用提示,强制删除
    * 
    * @return
    */
   public JDeleteOptions useF() {
      this.f = true;
      return this;
   }

   @Override
   public String toOptions() {
      StringBuilder builder = new StringBuilder();
      if (this.v && this.valueName != null && this.valueName.length() > 0) {
         builder.append("/v ").append(this.valueName);
      }
      if (this.ve) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/ve");
      }

      if (this.va) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/va");
      }

      if (this.f) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/f");
      }

      return builder.toString();
   }

}
package com.jianggujin.registry;

/**
 * 复制选项
 * 
 * @author jianggujin
 *
 */
public class JCopyOptions implements JOptions {
   private boolean s;
   private boolean f;

   /**
    * 复制所有子项和值
    * 
    * @return
    */
   public JCopyOptions useS() {
      this.s = true;
      return this;
   }

   /**
    * 不用提示,强制复制
    * 
    * @return
    */
   public JCopyOptions useF() {
      this.f = true;
      return this;
   }

   @Override
   public String toOptions() {
      StringBuilder builder = new StringBuilder();
      if (this.s) {
         builder.append("/s");
      }

      if (this.f) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/f");
      }

      return builder.toString();
   }
}
package com.jianggujin.registry;

/**
 * 比较选项
 * 
 * @author jianggujin
 *
 */
public class JCompareOptions implements JOptions {
   private boolean v = false;
   private String valueName;

   private boolean ve = false;

   private boolean s = false;
   private boolean oa = false, od = false, os = false, on = false;

   /**
    * 所选注册表项下的要比较的值的名称。 省略时,该项下的所有值都会得到比较
    * 
    * @param valueName
    */
   public JCompareOptions useV(String valueName) {
      this.v = true;
      this.valueName = valueName;
      this.ve = false;
      return this;
   }

   /**
    * 比较空白值名称的值(默认)
    */
   public JCompareOptions useVE() {
      this.ve = true;
      this.v = false;
      return this;
   }

   /**
    * 比较所有子项和值
    */
   public JCompareOptions useS() {
      this.s = true;
      return this;
   }

   /**
    * 显示所有不同和匹配结果
    * 
    * @return
    */
   public JCompareOptions useOA() {
      this.oa = true;
      this.od = false;
      this.os = false;
      this.on = false;
      return this;
   }

   /**
    * 只显示不同的结果
    * 
    * @return
    */
   public JCompareOptions useOD() {
      this.oa = false;
      this.od = true;
      this.os = false;
      this.on = false;
      return this;
   }

   /**
    * 只显示匹配结果
    * 
    * @return
    */
   public JCompareOptions useOS() {
      this.oa = false;
      this.od = false;
      this.os = true;
      this.on = false;
      return this;
   }

   /**
    * 不显示结果
    * 
    * @return
    */
   public JCompareOptions useON() {
      this.oa = false;
      this.od = false;
      this.os = false;
      this.on = true;
      return this;
   }

   @Override
   public String toOptions() {
      StringBuilder builder = new StringBuilder();
      if (this.v && this.valueName != null && this.valueName.length() > 0) {
         builder.append("/v ").append(this.valueName);
      }
      if (this.ve) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/ve");
      }

      if (this.s) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/s");
      }

      if (this.oa) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/oa");
      }

      if (this.od) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/od");
      }

      if (this.os) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/os");
      }

      if (this.on) {
         if (builder.length() > 0) {
            builder.append(" ");
         }
         builder.append("/on");
      }

      return builder.toString();
   }
}

上述选项实现类中还需要辅助的注册表值类型枚举,代码如下:

package com.jianggujin.registry;

/**
 * 注册表值类型
 * 
 * @author jianggujin
 * 
 */
public enum JValueType {
   REG_SZ, REG_MULTI_SZ, REG_EXPAND_SZ, REG_DWORD, REG_QWORD, REG_BINARY, REG_NONE
}

下面我们来编写核心的操作类,在开始之前,我们首先需要解决一个问题:中文乱码,很多时候我们在读取信息的时候会出现乱码,通常情况控制台的编码为:GB2312,但是也可能被修改,如果控制台的属性被编辑了,那么相关信息会存储在注册表中,我们可以先对其进行查询,然后解析是否有代码页的相关值,如果有。就是用配置的编码,如果没有则使用默认编码,代码页的处理类如下:

package com.jianggujin.registry;

import java.util.HashMap;
import java.util.Map;

/**
 * 代码页
 * 
 * @author jianggujin
 *
 */
public class JCodePage {
   private static Map<String, String> codePageMap = new HashMap<String, String>();
   static {
      codePageMap.put("37", "IBM037");
      codePageMap.put("437", "IBM437");
      codePageMap.put("500", "IBM500");
      codePageMap.put("708", "ASMO-708");
      codePageMap.put("720", "DOS-720");
      codePageMap.put("737", "ibm737");
      codePageMap.put("775", "ibm775");
      codePageMap.put("850", "ibm850");
      codePageMap.put("852", "ibm852");
      codePageMap.put("855", "IBM855");
      codePageMap.put("857", "ibm857");
      codePageMap.put("858", "IBM00858");
      codePageMap.put("860", "IBM860");
      codePageMap.put("861", "ibm861");
      codePageMap.put("862", "DOS-862");
      codePageMap.put("863", "IBM863");
      codePageMap.put("864", "IBM864");
      codePageMap.put("865", "IBM865");
      codePageMap.put("866", "cp866");
      codePageMap.put("869", "ibm869");
      codePageMap.put("870", "IBM870");
      codePageMap.put("874", "windows-874");
      codePageMap.put("875", "cp875");
      codePageMap.put("932", "shift_jis");
      codePageMap.put("936", "gb2312");
      codePageMap.put("949", "ks_c_5601-1987");
      codePageMap.put("950", "big5");
      codePageMap.put("1026", "IBM1026");
      codePageMap.put("1047", "IBM01047");
      codePageMap.put("1140", "IBM01140");
      codePageMap.put("1141", "IBM01141");
      codePageMap.put("1142", "IBM01142");
      codePageMap.put("1143", "IBM01143");
      codePageMap.put("1144", "IBM01144");
      codePageMap.put("1145", "IBM01145");
      codePageMap.put("1146", "IBM01146");
      codePageMap.put("1147", "IBM01147");
      codePageMap.put("1148", "IBM01148");
      codePageMap.put("1149", "IBM01149");
      codePageMap.put("1200", "utf-16");
      codePageMap.put("1201", "unicodeFFFE");
      codePageMap.put("1250", "windows-1250");
      codePageMap.put("1251", "windows-1251");
      codePageMap.put("1252", "Windows-1252");
      codePageMap.put("1253", "windows-1253");
      codePageMap.put("1254", "windows-1254");
      codePageMap.put("1255", "windows-1255");
      codePageMap.put("1256", "windows-1256");
      codePageMap.put("1257", "windows-1257");
      codePageMap.put("1258", "windows-1258");
      codePageMap.put("1361", "Johab");
      codePageMap.put("10000", "macintosh");
      codePageMap.put("10001", "x-mac-japanese");
      codePageMap.put("10002", "x-mac-chinesetrad");
      codePageMap.put("10003", "x-mac-korean");
      codePageMap.put("10004", "x-mac-arabic");
      codePageMap.put("10005", "x-mac-hebrew");
      codePageMap.put("10006", "x-mac-greek");
      codePageMap.put("10007", "x-mac-cyrillic");
      codePageMap.put("10008", "x-mac-chinesesimp");
      codePageMap.put("10010", "x-mac-romanian");
      codePageMap.put("10017", "x-mac-ukrainian");
      codePageMap.put("10021", "x-mac-thai");
      codePageMap.put("10029", "x-mac-ce");
      codePageMap.put("10079", "x-mac-icelandic");
      codePageMap.put("10081", "x-mac-turkish");
      codePageMap.put("10082", "x-mac-croatian");
      codePageMap.put("20000", "x-Chinese-CNS");
      codePageMap.put("20001", "x-cp20001");
      codePageMap.put("20002", "x-Chinese-Eten");
      codePageMap.put("20003", "x-cp20003");
      codePageMap.put("20004", "x-cp20004");
      codePageMap.put("20005", "x-cp20005");
      codePageMap.put("20105", "x-IA5");
      codePageMap.put("20106", "x-IA5-German");
      codePageMap.put("20107", "x-IA5-Swedish");
      codePageMap.put("20108", "x-IA5-Norwegian");
      codePageMap.put("20127", "us-ascii");
      codePageMap.put("20261", "x-cp20261");
      codePageMap.put("20269", "x-cp20269");
      codePageMap.put("20273", "IBM273");
      codePageMap.put("20277", "IBM277");
      codePageMap.put("20278", "IBM278");
      codePageMap.put("20280", "IBM280");
      codePageMap.put("20284", "IBM284");
      codePageMap.put("20285", "IBM285");
      codePageMap.put("20290", "IBM290");
      codePageMap.put("20297", "IBM297");
      codePageMap.put("20420", "IBM420");
      codePageMap.put("20423", "IBM423");
      codePageMap.put("20424", "IBM424");
      codePageMap.put("20833", "x-EBCDIC-KoreanExtended");
      codePageMap.put("20838", "IBM-Thai");
      codePageMap.put("20866", "koi8-r");
      codePageMap.put("20871", "IBM871");
      codePageMap.put("20880", "IBM880");
      codePageMap.put("20905", "IBM905");
      codePageMap.put("20924", "IBM00924");
      codePageMap.put("20932", "EUC-JP");
      codePageMap.put("20936", "x-cp20936");
      codePageMap.put("20949", "x-cp20949");
      codePageMap.put("21025", "cp1025");
      codePageMap.put("21866", "koi8-u");
      codePageMap.put("28591", "iso-8859-1");
      codePageMap.put("28592", "iso-8859-2");
      codePageMap.put("28593", "iso-8859-3");
      codePageMap.put("28594", "iso-8859-4");
      codePageMap.put("28595", "iso-8859-5");
      codePageMap.put("28596", "iso-8859-6");
      codePageMap.put("28597", "iso-8859-7");
      codePageMap.put("28598", "iso-8859-8");
      codePageMap.put("28599", "iso-8859-9");
      codePageMap.put("28603", "iso-8859-13");
      codePageMap.put("28605", "iso-8859-15");
      codePageMap.put("29001", "x-Europa");
      codePageMap.put("38598", "iso-8859-8-i");
      codePageMap.put("50220", "iso-2022-jp");
      codePageMap.put("50221", "csISO2022JP");
      codePageMap.put("50222", "iso-2022-jp");
      codePageMap.put("50225", "iso-2022-kr");
      codePageMap.put("50227", "x-cp50227");
      codePageMap.put("51932", "euc-jp");
      codePageMap.put("51936", "EUC-CN");
      codePageMap.put("51949", "euc-kr");
      codePageMap.put("52936", "hz-gb-2312");
      codePageMap.put("54936", "GB18030");
      codePageMap.put("57002", "x-iscii-de");
      codePageMap.put("57003", "x-iscii-be");
      codePageMap.put("57004", "x-iscii-ta");
      codePageMap.put("57005", "x-iscii-te");
      codePageMap.put("57006", "x-iscii-as");
      codePageMap.put("57007", "x-iscii-or");
      codePageMap.put("57008", "x-iscii-ka");
      codePageMap.put("57009", "x-iscii-ma");
      codePageMap.put("57010", "x-iscii-gu");
      codePageMap.put("57011", "x-iscii-pa");
      codePageMap.put("65000", "utf-7");
      codePageMap.put("65001", "utf-8");
      codePageMap.put("65005", "utf-32");
      codePageMap.put("65006", "utf-32BE");
   }

   /**
    * 获得字符集
    * 
    * @param codePage
    * @return
    */
   public static String getCharset(String codePage) {
      return codePageMap.get(codePage);
   }
}

万事俱备,只欠东风,真正的操作类可以亮相了:

package com.jianggujin.registry;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

/**
 * 注册表工具
 * 
 * @author jianggujin
 *
 */
public class JRegistry {
   /**
    * 控制台输出流编码,如果出现乱码可以设置
    */
   public static String CMD_ENCODING = null;
   public final static String DEFAULT_CMD_ENCODING = "GBK";

   /**
    * 查询
    * 
    * @param keyName
    *           [\\Machine\]FullKey<br/>
    *           Machine - 远程机器名称,省略当前机器的默认值。在远程机器上 只有 HKLM 和 HKU 可用<br/>
    *           FullKey - 以 ROOTKEY\SubKey 名称形式<br/>
    *           ROOTKEY - [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey - 在选择的 ROOTKEY 下的注册表项的全名
    * @param options
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult query(String keyName, JQueryOptions options) throws IOException, InterruptedException {
      return exec("query", keyName, options);
   }

   /**
    * 添加
    * 
    * @param keyName
    *           [\\Machine\]FullKey<br/>
    *           Machine 远程机器名 - 忽略默认到当前机器。远程机器上 只有 HKLM 和 HKU 可用<br/>
    *           FullKey ROOTKEY\SubKey <br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey 所选 ROOTKEY 下注册表项的完整名称
    * @param options
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult add(String keyName, JAddOptions options) throws IOException, InterruptedException {
      return exec("add", keyName, options);
   }

   /**
    * 删除
    * 
    * @param keyName
    *           [\\Machine\]FullKey 远程机器名 - 如果省略,默认情况下将使用当前机 远程机器上只有 HKLM 和 HKU
    *           可用。 FullKey ROOTKEY\SubKey ROOTKEY [ HKLM | HKCU | HKCR | HKU |
    *           HKCC ] SubKey 所选 ROOTKEY 下面的注册表项的全名。
    * @param options
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult delete(String keyName, JDeleteOptions options) throws IOException, InterruptedException {
      return exec("delete", keyName, options);
   }

   /**
    * 通用操作
    * 
    * @param operation
    * @param keyName
    * @param options
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   private static JExecResult exec(String operation, String keyName, JOptions options)
         throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add(operation);
      cmds.add(keyName);
      if (options != null) {
         String opts = options.toOptions();
         if (opts.length() > 0) {
            cmds.add(opts);
         }
      }
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 复制
    * 
    * @param keyName1
    *           [\\Machine\]FullKey<br/>
    *           Machine 远程机器名 - 如果省略,默认情况下将使用当前机器。 远程机器上只有 HKLM 和 HKU 可用。<br/>
    *           FullKey ROOTKEY\SubKey<br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ] <br/>
    *           SubKey 所选 ROOTKEY 下的注册表项的全名。
    * @param keyName2
    *           [\\Machine\]FullKey<br/>
    *           Machine 远程机器名 - 如果省略,默认情况下将使用当前机器。 远程机器上只有 HKLM 和 HKU 可用。<br/>
    *           FullKey ROOTKEY\SubKey<br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ] <br/>
    *           SubKey 所选 ROOTKEY 下的注册表项的全名。
    * @param options
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult copy(String keyName1, String keyName2, JCopyOptions options)
         throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("copy");
      cmds.add(keyName1);
      cmds.add(keyName2);
      if (options != null) {
         String opts = options.toOptions();
         if (opts.length() > 0) {
            cmds.add(opts);
         }
      }
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 保存
    * 
    * @param keyName
    *           ROOTKEY\SubKey<br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey 所选 ROOTKEY 下的注册表项的全名
    * @param filName
    *           要保存的磁盘文件名。如果没有指定路径,文件会在调用进程的 当前文件夹中得到创建
    * @param useY
    *           不用提示就强行覆盖现有文件
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult save(String keyName, String filName, boolean useY)
         throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("save");
      cmds.add(keyName);
      cmds.add(filName);
      if (useY) {
         cmds.add("/y");
      }
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 恢复
    * 
    * @param keyName
    *           ROOTKEY\SubKey (只是本地机器)<br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey 要将配置单元文件还原到的注册表项全名。 覆盖现有项的值和子项
    * @param filName
    *           要还原的配置单元文件名。 你必须使用{@link #save(String, String, boolean)}来创建这个文件
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult restore(String keyName, String filName) throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("restore");
      cmds.add(keyName);
      cmds.add(filName);
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 加载
    * 
    * @param keyName
    *           ROOTKEY\SubKey (只是本地机器) <br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ] <br/>
    *           SubKey 要将配置单元文件还原到的注册表项全名。 覆盖现有项的值和子项
    * @param filName
    *           要加载的配置单元文件名。你必须使用{@link #save(String, String, boolean)}来创建这个文件
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult load(String keyName, String filName) throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("load");
      cmds.add(keyName);
      cmds.add(filName);
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 卸载
    * 
    * @param keyName
    *           ROOTKEY\SubKey (只是本地机器)<br/>
    *           ROOTKEY [ HKLM | HKU ]<br/>
    *           SubKey 要卸载的配置单元的注册表项名称
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult unload(String keyName) throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("unload");
      cmds.add(keyName);
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 比较
    * 
    * @param keyName1
    *           [\\Machine\]FullKey<br/>
    *           Machine 远程机器名 - 如果省略,默认情况下将使用当前机器。 远程机器上只有 HKLM 和 HKU 可用<br/>
    *           FullKey ROOTKEY\SubKey <br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey 所选 ROOTKEY 下的注册表项的全名
    * 
    * @param keyName2
    *           [\\Machine\]FullKey<br/>
    *           Machine 远程机器名 - 如果省略,默认情况下将使用当前机器。 远程机器上只有 HKLM 和 HKU 可用<br/>
    *           FullKey ROOTKEY\SubKey <br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey 所选 ROOTKEY 下的注册表项的全名。
    * @param options
    * @return 每个输出行前面显示的符号定义为: = 表示 FullKey1 等于 FullKey2 数据 < 指的是 FullKey1 数据,与
    *         FullKey2 数据不同 > 指的是 FullKey2 数据,与 Fullkey1 数据不同
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult compare(String keyName1, String keyName2, JCompareOptions options)
         throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("compare");
      cmds.add(keyName1);
      cmds.add(keyName2);
      if (options != null) {
         String opts = options.toOptions();
         if (opts.length() > 0) {
            cmds.add(opts);
         }
      }
      JExecResult result = exec(true, cmds.toArray(new String[0]));
      result.setSuccess(result.getExitValue() == 0 || result.getExitValue() == 2);
      return result;
   }

   /**
    * 保存
    * 
    * @param keyName
    *           ROOTKEY[\SubKey] (只是本地机器)<br/>
    *           ROOTKEY [ HKLM | HKCU | HKCR | HKU | HKCC ]<br/>
    *           SubKey 所选 ROOTKEY 下的注册表项的全名
    * @param filName
    *           要导出的磁盘文件名
    * @param useY
    *           不用提示就强行覆盖现有文件
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult export(String keyName, String filName, boolean useY)
         throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("export");
      cmds.add(keyName);
      cmds.add(filName);
      if (useY) {
         cmds.add("/y");
      }
      return exec(true, cmds.toArray(new String[0]));
   }

   /**
    * 导入
    * 
    * @param filName
    *           要导入的磁盘文件名(只是本地机器)
    * @return
    * @throws IOException
    * @throws InterruptedException
    */
   public static JExecResult import2(String filName) throws IOException, InterruptedException {
      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("import");
      cmds.add(filName);
      return exec(true, cmds.toArray(new String[0]));
   }

   public static void dump(JExecResult result) {
      System.out.println("Exec Result: " + result.isSuccess());
      System.out.println("Lines:");
      for (String line : result.getLines()) {
         System.out.println(line);
      }
   }

   private static String queryCodePage() throws IOException, InterruptedException {

      List<String> cmds = new ArrayList<String>();
      cmds.add("reg");
      cmds.add("query");
      cmds.add("HKEY_CURRENT_USER\\Console");
      JQueryOptions options = new JQueryOptions().useF("CodePage").useS();
      if (options != null) {
         String opts = options.toOptions();
         if (opts.length() > 0) {
            cmds.add(opts);
         }
      }

      JExecResult result = exec0(true, cmds.toArray(new String[0]));
      String[] lines = null;
      boolean canParse = false;
      if (result.isSuccess() && (lines = result.getLines()).length > 2) {
         for (String line : lines) {
            if (!canParse && line.startsWith("HKEY_CURRENT_USER\\Console") && line.endsWith("cmd.exe")) {
               canParse = true;
            }
            if (canParse && line.trim().startsWith("CodePage")) {
               int index = line.lastIndexOf("0x");
               String hex = line.substring(index + 2);
               return JCodePage.getCharset(Integer.parseInt(hex, 16) + "");
            }
         }
      }
      return null;
   }

   private static void ensureEncoding() {
      if (CMD_ENCODING == null) {
         synchronized (JRegistry.class) {
            if (CMD_ENCODING == null) {
               try {
                  // 如果修改过cmd属性,可以查到注册表中的相关配置,所以先查询代码页,获得控制台编码
                  CMD_ENCODING = queryCodePage();
               } catch (Exception e) {
                  CMD_ENCODING = DEFAULT_CMD_ENCODING;
               }
            }
         }
      }
   }

   private static JExecResult exec(boolean skipEmptyLine, String[] cmds) throws IOException, InterruptedException {
      ensureEncoding();
      return exec0(skipEmptyLine, cmds);
   }

   private static JExecResult exec0(boolean skipEmptyLine, String[] cmds) throws IOException, InterruptedException {
      StringBuilder cmd = new StringBuilder();
      boolean first = true;
      for (String item : cmds) {
         if (!first) {
            cmd.append(" ");
         }
         cmd.append(item);
         first = false;
      }
      Process process = Runtime.getRuntime().exec(cmd.toString());
      int exitVal = process.waitFor();
      // 读取屏幕输出
      BufferedReader reader = new BufferedReader(
            new InputStreamReader(exitVal == 0 ? process.getInputStream() : process.getErrorStream(),
                  CMD_ENCODING == null ? DEFAULT_CMD_ENCODING : CMD_ENCODING));
      String line = null;
      List<String> lines = new ArrayList<String>();
      while ((line = reader.readLine()) != null) {
         if (line.length() > 0) {
            lines.add(line);
         }
      }
      reader.close();
      return new JExecResult(exitVal, lines.toArray(new String[0]));
   }
}

在该类中,对操作的结果都封装成了一个JExecResult对象,通过该对象可以获得执行后返回的结果以及输出的字符串信息,剩下的具体的该如何解析输出的字符串就需要按照实际场景进行编写了,JExecResult代码如下:

package com.jianggujin.registry;

/**
 * 执行结果
 * 
 * @author jianggujin
 *
 */
public class JExecResult {
   private final int exitValue;
   private boolean success;
   private final String[] lines;

   public JExecResult(int exitValue, String[] lines) {
      this.exitValue = exitValue;
      this.success = exitValue == 0;
      this.lines = lines;
   }

   public int getExitValue() {
      return exitValue;
   }

   public boolean isSuccess() {
      return success;
   }

   public String[] getLines() {
      return lines;
   }

   protected void setSuccess(boolean success) {
      this.success = success;
   }
}

最后可以编写一个测试类来查询一下

package com.jianggujin.registry;

import org.junit.Test;

/**
 * 注册表测试
 * 
 * @author jianggujin
 *
 */
public class JRegistryTest {
   @Test
   public void query() throws Exception {
      JExecResult result = JRegistry.query("HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft",
            new JQueryOptions().useF("\"Java Development Kit\""));
      JRegistry.dump(result);
   }
}

代码篇幅较长,完整代码也可以通过码云GitHub获取

猜你喜欢

转载自blog.csdn.net/jianggujin/article/details/80338574