要启用远程注册表操作,必需实现两个函数:对远程注册表读出和写入的能力。
读和写函数已经被封装进了 RegConnect DLL。该 DLL 本身隐藏了这些函数实际操作原理的复杂性,稍后我们将对此进行描述。DLL 执行实际连接/断开和读/写函数,并提供了两个易于使用的函数。
可以通过任何能够利用 DLL 的语言(如 C 或 Visual Basic)调用这些函数。要允许从 Java 环境使用 DLL,得做一些额外的工作。
清单 1. C/C++ 原型函数
int WINAPI RegDBGetKeyValueEx(LPCTSTR MachineName, HKEY RegKey, LPCTSTR KeyName, LPCTSTR ValueName, LPTSTR KeyValue) ; int WINAPI RegDBSetKeyValueEx(LPCTSTR MachineName, HKEY RegKey, LPCTSTR KeyName, LPCTSTR ValueName, LPTSTR KeyValue) ; |
清单 2. Visual Basic 原型函数
Declare Function RegDBGetKeyValueEx Lib "RegConnect.dll" (ByVal MachineName As String, ByVal RegKey As Long, ByVal KeyName As String, ByVal ValueName As String, ByVal KeyValue As String) As Integer Declare Function RegDBSetKeyValueEx Lib "RegConnect.dll" (ByVal MachineName As String, ByVal RegKey As Long, ByVal KeyName As String, ByVal ValueName As String, ByVal KeyValue As String) As Integer |
清单 3. Java 原型函数
public native String RegDBGetKeyValueEx(String MachineName, int RegKey, String KeyName, String ValueName) ; 注:因为函数将字符串作为调用结果返回,所以对于 Java 没有 KeyValue 参数。 public native int RegDBSetKeyValueEx(String MachineName, int RegKey, String KeyName, String ValueName, String KeyValue) ; 公用类 GetKey 和 SetKey 定义了调用和变量。这些类可以用来调用 DLL 函数。 |
对于所有语言,参数如下:
- MachineName:您希望询问的机器的名称 ― 如果这是一个空字符串,则询问本地机器
- RegKey:您希望查询的根键值,即 HKEY_LOCAL_MACHINE
- KeyName:您希望查询的键名称,即 SOFTWARE\IBM\DB2
- ValueName:您希望查询的键中值的名称,即 DB2 Folder Name
对于获取键(除了 Java 函数以外):
- KeyValue:在该参数中将返回键值
对于设置键:
- KeyValue:您希望设置的键值
通过用所需参数调用各个函数来实现对这些函数的调用。当调用 RegDBGetKeyValueEx 时,某些语言需要用于返回字符串的预定义存储器。如果没有预先分配这个存储器,那么,该函数可能会(并且很可能会)失败。
正如其实现的那样,RegDBGetKeyValueEx 函数仅限于返回注册表字符串(REG_SZ)和数字(DWORD)数据。还请注意,RegDBGetKeyValueEx 仅返回字符串 ― DWORD 值是作为与其等价的 ASCII 数值返回的。
当使用 RegDBSetKeyValueEx 函数来创建键时,如果该键不存在,则将创建键名和键值,记住这一点很重要。如果键已经存在,则原先的值将被覆盖。还请注意,正如其实现的那样,RegDBSetKeyValueEx 将仅存储字符串值。
清单 4显示了一个用 Visual Basic 函数封装 RegDBGetKeyValueEx 函数的示例。
清单 4. Visual Basic 示例
Function GetRegValue(szMachine$, hKey&, szRegTree$, szKey$) As String Dim Temp&, sReturn$ sReturn$ = Space(128) 'Pre-define storage for the returned value Temp& = RegDBGetKeyValueEx(szMachine$, hKey&, szRegTree$, szKey$, sReturn$) GetRegValue = Left$(sReturn$, InStr(sReturn$, Chr$(0))) End Function |
为了在 Java 环境中使用这些函数,使用类 GetKey 和 SetKey。 清单 5中的下列示例演示了使用 Java 设置和检索键。
清单 5. Java 示例
// Test for registry read/write using JNI call to RegConnect.dll
class RegTest
{
public static void main(String[] args)
{
// Set a registry key value
SetKey skey = new SetKey() ;
skey.RegKey = skey.HKEY_LOCAL_MACHINE ;
skey.MachineName = "" ;
//Change this to a remote machine if you wish
skey.KeyName = "software\\IBM\\MyKey" ;
skey.ValueName ="test" ;
skey.KeyValue ="My NEW reg value" ;
skey.iResult = skey.RegDBSetKeyValueEx(skey.MachineName, skey.RegKey,
skey.KeyName,
skey.ValueName, skey.KeyValue) ;
System.out.println("Set Result is " + skey.iResult); //Display the result of the call
// Now read the value back again
GetKey gkey = new GetKey() ;
gkey.RegKey = gkey.HKEY_LOCAL_MACHINE ;
gkey.MachineName = "" ;
//Change this to a remote machine if you wish
gkey.KeyName = "software\\IBM\\MyKey" ;
gkey.ValueName ="test" ;
gkey.KeyValue = gkey.RegDBGetKeyValueEx(gkey.MachineName, gkey.RegKey,
gkey.KeyName, gkey.ValueName) ;
System.out.println("Data is " + gkey.KeyValue); //Display the key value
}
}
|
本文所提供的实现 Registry 功能的 DLL 使用标准 Windows API 调用来执行其函数。两个函数的初始需求都是连接到远程注册表。使用下列函数调用实现这一点:
// Connect to the remote registry lRC = RegConnectRegistry(cMachine, hRegKey, &hRemoteKey) ; |
这个函数调用的重要参数是正在连接的远程机器名和您希望访问的注册表的根。这些参数包含于用于机器名的 cMachine 参数和用于注册表根的 hRegKey 参数中。hRegKey 参数实际上只是一个长数据类型,它是用与所需注册表根相关的值设置的。这些值被声明为常数值,即 HKEY_CURRENT_USER。剩下的那个参数返回一个指向该注册表的句柄,其余函数使用该句柄来访问该远程注册表。
一旦获得了到远程机器的注册表的连接,就可以执行对该注册表读或写的函数。
如果正在调用设置键值的函数,则要设置的注册表项可能不存在。为了确保该项存在,调用 RegCreateKeyEx 函数。这样做的效果是创建该项(如果它不存在)或仅打开该项以便访问(如果它已经存在)。返回的 hKey 值是一个句柄,它指向下列函数调用中使用的注册表键。下列代码显示了执行这个操作的函数调用:
// Initialize variables
lRC = RegCreateKeyEx(hRemoteKey, lpKeyName, 0, REG_NONE,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDisp) ;
|
当读取键时,只需要获得指向注册表键的句柄。使用 API 调用 RegOpenKeyEx 来获取必要的 hKey 注册表句柄,如下所示。
lRC = RegOpenKeyEx(hRemoteKey, lpKeyName, 0, KEY_ALL_ACCESS, &hKey); |
注册表项可以是多种类型,包括字符串和数字。当读入项时,了解正在读取的项的类型很重要。这可以通过调用 RegQueryValueEx 获取的注册表键数据类型进行确定。dwKeyDataType 参数返回键的数据类型。下面显示了该函数。
// Got key, get value. First, get the size of the key. lRC = RegQueryValueEx(hKey, lpValueName, NULL, &dwKeyDataType, NULL, &dwKeySize); |
我们可以使用 dwKeyDataType 值确定得到的是字符串值还是数字值,并查询该键以便正确返回该值,如下所示。
if(dwKeyDataType == REG_DWORD) //DWORD (Numeric value)
{
char cData[128] = {0} ;
lRC = RegQueryValueEx(hKey, lpValueName, NULL, &dwKeyDataType, (LPBYTE)cData
&dwKeySize);
ltoa((LONG)cData[0], lpszKeyValue, 10) ;
}
else
// Now get the actual key value
lRC = RegQueryValueEx(hKey, lpValueName, NULL, &dwKeyDataType,
(LPBYTE)lpszKeyValue, &dwKeySize);
|
为了使 DLL 函数保持简单,RegDBGetKeyValueEx 函数只返回字符串值。因为注册表可以包含数值,所以所有返回的数值都被转换成字符串。使用调用应用程序自身的函数,很容易将该字符串转换回数字值。如上所示,ltoa 函数是用来将数值转换成字符串的。在任一函数的结束部分都必须关闭注册表。这是通过调用 RegCloseKey API 函数完成的。
Java 能够通过使用 JNI 接口调用 DLL 中的函数。要允许与 Java 一起使用 DLL,需要在类文件中设置用于 DLL 的 Java 可调用函数。首要任务是为每个函数创建一个 Java 类。用于 GetKey 函数的类如下:
// Class to read a registry key from a local or remote machine
public class GetKey{
int RegKey ; //Key value, that is, HKEY_CLASSES_ROOT
String MachineName ; //Machine name we want to interrogate
String KeyName ; //Name of key we want to read
String ValueName ; //Value of key we want to read
String KeyValue ; //Return value from key
public native String RegDBGetKeyValueEx(String MachineName, int RegKey,
String KeyName, String ValueName) ;
public static final int HKEY_CLASSES_ROOT = 0x80000000 ;
public static final int HKEY_CURRENT_USER = 0x80000001 ;
public static final int HKEY_LOCAL_MACHINE = 0x80000002 ;
public static final int HKEY_USERS = 0x80000003 ;
public static final int HKEY_CURRENT_CONFIG = 0x80000005 ;
public static final int HKEY_DYN_DATA = 0x80000006 ;
static
{
System.loadLibrary("RegConnect") ;
}
}
|
如代码所示,值和函数都与 C 或 Visual Basic 所使用的值和函数非常相似。
一旦创建了类,就可以使用 JDK 函数 Javah 来为 DLL 创建一个能包含在 C 代码中的头文件。该头文件包含类函数的所有 C 实现代码。这和打开一个命令提示符、更改到驻留 Java 源文件的目录以及运行命令 javah GetKey 一样简单。这个操作将创建可以被包含到 C 代码中的 GetKey.h 文件。相同过程也适用于创建 SetKey.h 头文件。
最后,需要在 JNI 代码中封装 C 函数。从 Javah JDK 实用程序创建的头文件中复制函数原型:
// Java implementation of RegDBGetKeyValueEx JNIEXPORT jobject JNICALL Java_GetKey_RegDBGetKeyValueEx(JNIEnv* env, jobject this_obj, jstring jMachineName, jint hRegKey, jstring jKeyName, jstring jValueName) |
接下来,为调用 C 函数所需的每个字符串创建参数:
LPCTSTR lpMachineName ; LPCTSTR lpKeyName ; LPCTSTR lpValueName ; char cKeyValue[255] ; |
最后,为函数返回声明 Java 对象:
jobject ret ; |
当进行 JNI 调用时,Java 向该函数传递一个对象。这个对象必需是非绑定的,并且获取了字符串。JNI 函数 GetStringUTFChars 执行这个任务。用下列代码用来获取字符串:
lpMachineName = (*env)->GetStringUTFChars(env, jMachineName, NULL) ; lpKeyName = (*env)->GetStringUTFChars(env, jKeyName, NULL) ; lpValueName = (*env)->GetStringUTFChars(env, jValueName, NULL) ; |
现在,我们已准备好调用 C 函数了。下面显示了该调用,它带有从 Java 对象获取的参数:
RegDBGetKeyValueEx(lpMachineName, (HKEY)hRegKey, lpKeyName, lpValueName, cKeyValue) ; |
我们现在已经完成了 Java 对象的使用,所以需要释放它们使用的字符串和内存。ReleaseStringUTFChars 函数执行这个任务,如下所示:
(*env)->ReleaseStringUTFChars(env, jMachineName, NULL) ; (*env)->ReleaseStringUTFChars(env, jKeyName, NULL) ; (*env)->ReleaseStringUTFChars(env, jValueName, NULL) ; |
最后,我们需要将从注册表获取的字符串存放到正在返回的 Java 对象,然后将该对象返回到 Java 程序。JNI 函数 NewStringUTF 执行该任务,如下所示:
ret = (*env)->NewStringUTF(env, cKeyValue) ; return ret ; |
这就是所有步骤。如果您检查所提供的代码,就会发现 SetKey 函数几乎和 GetKey 函数相同。
本文提供的代码(请参阅 参考资料)包含下列内容:
- RegConnect.DLL 源代码,Visual Studio 6 工程以及预构建的发行版 DLL
- Java 类和头文件以及用来试验函数的 Java 样本
- 封装了函数调用并演示函数的 Visual Basic 工程
本文说明了如何通过使用 DLL 执行 Windows API 函数调用来实现本地或远程注册表操作。它提供了从 Java 和 Visual Basic 环境使用 DLL 的样本。注册表操作可以成为任何程序的有用补充,因为它允许保存和检索运行时配置数据。
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
- 请阅读 Brian Venn 的文章
利用远程注册表访问(
developerWorks,2002 年 1 月)
-
Core JAVA 1.1 Volume II:Advanced Features,第四版(Cay S. Horstmann 和 Gary Cornell 合著,1998 年 12 月,ISBN 0-13-092798-4)描述了如何使用 JNI 调用封装 DLL 以及如何从 Java 使用它们。
- Microsoft Windows System Information ―
About the Registry
- Microsoft Developer Network
Registry Functions提供了关于注册表访问 API 调用的信息
- zip 文件中有本文的
示例代码。
Nigel Morton 是在英国 Hursley Park 工作的软件工程师。自从 2000 年 9 月以来,他一直为 IBM 的商业集成(Business Integration)项目工作。在加入 IBM 以前,他开发过银行业和电信业的集成解决方案和消息传递软件。可通过 nmorton1@uk.ibm.com与 Nigel Morton 联系。