使 用 Visual Basic 读 取 和 修 改 Windows 注 册 库

 

在 过 去 , Microsoft® Visual Basic® 应 用 程 序 的 开 发 者 们 对 于 如 何 在 两 次 应 用 程 序 的 运 行 之 间 保 留 状 态 信 息 感 到 非 常 的 困 难 。 这 是 因 为 基 于 Microsoft Windows® 的 应 用 程 序 ( 包 括 Visual Basic ) 需 要 将 应 用 程 序 的 状 态 信 息 写 入 到 一 个 文 本 文 件 ( *.INI 文 件 ) 中 , 并 且 需 要 经 常 对 这 些 文 件 中 的 文 本 进 行 分 析 , 以 包 含 有 用 的 状 态 信 息 。

现 在 , 随 着 Windows 95 以 及 Windows NT™ 这 类 32 位 操 作 系 统 的 到 来 , 应 用 程 序 可 以 通 过 使 用 一 个 叫 做 Windows 注 册 库 的 存 储 场 所 来 保 留 应 用 程 序 的 状 态 信 息 , 并 且 将 它 们 自 己 注 册 在 系 统 之 中 。 通 过 位 于 ADVAPI32.DLL 中 的 C 语 言 应 用 程 序 编 程 接 口 ( APIs ) , 对 注 册 库 的 提 取 和 修 改 变 得 非 常 容 易 。

这 对 于 使 用 C 和 C++ 的 程 序 员 来 说 真 是 太 好 了 , 不 过 对 于 那 些 使 用 其 它 编 程 语 言 , 例 如 Visual Basic 版 本 4.0 的 程 序 员 来 说 , 使 用 这 些 编 程 接 口 还 需 要 进 行 许 多 额 外 的 工 作 。

作 为 一 个 Visual Basic 4.0 程 序 员 , 你 首 先 必 须 在 一 个 *.BAS 文 件 中 定 义 注 册 库 函 数 的 原 形 , 这 些 函 数 原 形 必 须 同 存 储 在 ADVAPI32.DLL 中 的 所 需 要 的 注 册 库 APIs 相 兼 容 。 你 还 需 要 添 加 那 些 对 调 用 这 些 APIs 所 需 要 的 结 构 和 常 量 的 声 明 。 这 些 对 函 数 原 形 , 结 构 以 及 常 量 的 声 明 全 部 包 含 在 VBREG.BAS 文 件 中 , 该 文 件 是 VBREG , 本 文 的 配 套 样 例 代 码 的 一 部 份 。 要 在 任 何 32 位 的 Visual Basic 4.0 工 程 中 使 用 这 些 注 册 库 APIs , 只 需 要 简 单 地 将 VBREG.BAS 文 件 包 含 在 工 程 中 。

注 意 : 如 果 你 决 定 在 VBREG.BAS 文 件 中 添 加 额 外 的 APIs , 结 构 或 常 量 , 在 对 它 们 进 行 声 明 的 时 候 需 要 格 外 小 心 ( 特 别 是 这 些 函 数 的 参 数 ) 。 要 想 这 些 在 你 的 Visual Basic 代 码 中 被 调 用 的 APIs 按 照 你 所 预 想 的 工 作 , 正 确 声 明 这 些 函 数 的 参 数 是 异 常 重 要 的 。 许 多 这 类 声 明 的 例 子 可 以 在 WIN32API.TXT 文 件 中 找 到 , 该 文 件 作 为 Visual Basic 4.0 系 统 的 一 部 份 同 时 被 安 装 , 但 是 你 可 能 需 要 对 这 些 声 明 进 行 修 改 , 以 便 使 它 们 能 够 正 常 工 作 。 你 还 可 以 在 WINNT.H 文 件 中 找 到 额 外 的 一 些 有 用 的 注 册 库 常 量 , 在 WINERROR.H 文 件 中 找 到 额 外 的 一 些 有 用 的 错 误 常 量 , 这 些 文 件 随 着 Visual C++™ 一 起 被 安 装 。

一 旦 你 将 一 个 文 件 , 例 如 VBREG.BAS 包 括 在 你 的 工 程 中 , 你 就 可 以 在 你 的 Visual Basic 代 码 中 调 用 这 些 注 册 库 APIs 。 要 记 住 的 是 你 所 转 递 的 参 数 必 须 有 效 且 包 含 有 效 的 值 。 另 外 , 当 STRING 值 被 返 回 时 , 必 须 为 此 字 符 串 创 建 一 个 空 间 来 容 纳 它 。 这 些 APIs , 结 构 以 及 常 量 在 VBREG 样 例 应 用 程 序 中 都 有 说 明 。

Windows 注 册 库 通 过 四 个 主 要 注 册 关 键 字 来 组 织 : HKEY_CLASSES_ROOT , HKEY_CURRENT_ USER , HEKY_LOCAL_MACHINE , 以 及 HKEY_USERS 。 在 本 文 中 假 设 你 对 于 这 些 关 键 字 已 经 非 常 熟 悉 , 已 经 知 道 如 何 使 用 子 关 键 字 , 以 及 已 经 了 解 在 注 册 库 中 所 包 含 的 值 的 类 型 。 子 关 键 字 可 以 用 “ HighestSubkey \NextLevelSubkey\LowerLevelSubkey\ ” ( 不 包 括 引 号 ) 的 形 式 来 表 示 。 其 中 “ HighestSubkey ” 是 最 高 级 的 子 关 键 字 , 它 直 接 位 于 四 个 主 关 键 字 之 下 。

注 册 关 键 字 可 以 被 创 建 , 删 除 , 打 开 , 关 闭 。 正 如 你 可 能 所 想 到 的 , 一 个 注 册 关 键 字 在 能 够 使 用 之 前 需 要 首 先 创 建 它 , 在 对 一 个 注 册 关 键 字 进 行 修 改 之 前 必 须 要 首 先 打 开 它 。 在 应 用 程 序 执 行 期 间 , 你 应 该 关 闭 所 有 的 当 前 未 被 使 用 的 关 键 字 , 并 且 你 应 该 仅 仅 只 删 除 那 些 你 能 确 定 将 来 绝 对 不 会 再 使 用 的 关 键 字 。

注 册 值 可 以 被 创 建 , 查 询 , 修 改 以 及 删 除 。 一 个 值 在 它 能 够 被 查 询 之 前 必 须 已 经 被 创 建 或 是 修 改 ( 通 过 使 用 RegSetValueEx 来 完 成 ) 。 同 注 册 关 键 字 一 样 , 如 果 你 想 删 除 它 , 你 需 要 确 定 它 将 来 绝 对 不 会 再 被 使 用 。

另 外 , 注 册 关 键 字 和 值 可 以 被 枚 举 , 以 助 你 决 定 当 前 的 那 些 输 入 是 可 以 使 用 的 。 如 果 你 在 编 译 期 间 不 能 够 确 定 对 哪 些 关 键 字 或 值 感 兴 趣 , 枚 举 功 能 就 非 常 有 用 了 。

有 一 点 很 有 趣 的 注 意 事 项 是 RegDeleteKeyAPI 在 没 有 子 关 键 字 的 情 况 下 将 只 删 除 注 册 关 键 字 。 由 于 这 个 原 因 , VBREG 不 允 许 删 除 一 个 带 有 子 关 键 字 的 关 键 字 。 不 过 你 能 够 很 容 易 的 添 加 删 除 关 键 字 以 及 子 关 键 字 的 功 能 , 你 只 需 要 创 建 一 个 过 程 , 该 过 程 枚 举 你 需 要 操 作 的 关 键 字 下 的 所 有 子 关 键 字 ( 以 及 这 些 子 关 键 字 的 子 关 键 字 ) , 并 且 在 删 除 该 关 键 字 之 前 首 先 删 除 所 有 的 子 关 键 字 。 对 于 每 个 子 关 键 字 都 应 该 在 RegDeleteKey 函 数 中 检 查 ERROR_ACCESS_DENIED 标 志 , 这 样 的 有 效 的 递 归 进 程 能 工 作 得 很 好 。

VBREG 使 用 HKEY_LOCAL_MACHINE , 在 如 下 的 VBREG Form_Load() 过 程 中 被 设 置 :

hKey = HKEY_LOCAL_MACHINE

通 过 将 此 变 量 设 置 为 其 它 的 四 个 主 注 册 关 键 字 之 一 , 你 能 够 很 容 易 的 使 用 它 们 。 更 进 一 步 , 你 能 够 从 用 户 或 是 一 个 已 知 的 注 册 库 位 置 获 取 这 些 信 息 。

当 使 用 这 些 注 册 库 APIs , 一 个 比 较 好 的 方 法 是 检 查 函 数 所 返 回 的 值 以 确 定 函 数 的 调 用 是 否 成 功 ( 如 果 函 数 调 用 成 功 , 它 将 会 返 回 ERROR_SUCCESS 标 志 ) 。 如 果 不 成 功 , 你 还 可 以 检 查 错 误 值 , 基 于 错 误 发 生 的 类 型 , 你 可 以 决 定 你 的 应 用 程 序 接 下 来 该 怎 么 做 。 还 有 一 个 比 较 好 的 方 法 是 使 用 注 册 库 APIs ( 例 如 在 VBREG 样 例 中 的 CreateRegKey 函 数 ) 来 为 错 误 设 置 陷 井 , 以 决 定 你 的 应 用 程 序 在 错 误 发 生 后 的 行 为 。

VBREG 创 建 和 修 改 的 值 都 是 REG_SZ 类 型 ( Unicode 空 结 束 字 符 串 ) 的 值 。 这 里 还 有 几 种 其 它 类 型 的 值 可 以 在 注 册 库 中 使 用 , 例 如 REG_BINARY , REG_LINK , 以 及 REG_DWORD 。 例 如 , 要 写 一 个 REG_DWORD 类 型 来 替 换 REG_SZ 类 型 , 则 SetRegValue 过 程 的 RegSetValueEx 调 用 应 该 被 改 为 :

lResult = RegSetValueEx(phkResult , sSetValue , 0 , REG_DWORD , CInt(sValue) , 4&)

这 里 假 设 该 “ sValue ” 包 含 一 个 可 以 被 转 化 为 一 个 整 数 的 值 。 你 可 能 会 需 要 在 代 码 中 进 行 更 深 入 的 修 改 以 使 用 这 些 其 它 类 型 的 值 。

在 你 能 够 使 用 一 个 注 册 库 关 键 字 之 前 , 该 关 键 字 必 须 被 创 建 。 RegCreateKeyEx API 提 供 了 运 行 时 刻 的 这 个 功 能 。 如 果 关 键 字 不 存 在 , 则 RegCreateKeyEx 将 创 建 一 个 新 的 关 键 字 , 而 如 果 关 键 字 已 经 存 在 , 则 将 只 简 单 地 将 它 打 开 。 下 面 是 在 VBREG 的 CreateRegKey 过 程 中 对 RegCreateKeyEx 的 调 用 。

CreateRegKey = (RegCreateKeyEx(hKey , SubKey & NewSubKey , 0 , "" , _

REG_OPTION_NON_VOLATILE , KEY_ALL_ACCESS , _

SA , phkResult , Create) = ERROR_SUCCESS)

如 果 RegCreateKeyEx 函 数 返 回 了 ERROR_SUCCESS 标 志 , 则 你 可 以 为 REG_CREATED_NEW_KEY 或 REG_OPENED_EXISTING_KEY 检 查 Create 参 数 , 以 确 定 是 否 有 一 个 新 的 关 键 字 被 创 建 。 在 上 面 的 调 用 中 , REG_OPTION_NON_VOLATILE 创 建 一 个 持 久 的 关 键 字 , KEY_ALL_ACCESS 则 给 予 所 有 的 用 户 对 此 关 键 字 的 完 整 的 访 问 权 利 。 其 它 的 选 项 也 同 样 能 够 被 指 定 。 这 些 选 项 在 Visual C++ 所 带 的 Windows API 参 考 中 有 更 深 入 的 解 释 。

一 旦 你 有 需 要 的 注 册 库 关 键 字 , 你 可 以 使 用 RegOpenKey 或 RegKeyCreateEx 来 在 将 来 需 要 的 时 候 打 开 它 们 。 你 可 能 会 注 意 到 在 VBREG 中 在 看 上 去 应 该 使 用 RegOpenKey 的 地 方 使 用 了 RegKeyCreateEx 。 我 发 现 如 果 不 使 用 RegKeyCreateEx 来 打 开 此 关 键 字 , 则 有 一 些 操 作 将 会 返 回 ERROR_ACCESS_DENIED 标 志 。 这 可 能 会 构 成 额 外 的 开 支 ; 如 果 该 关 键 字 不 存 在 的 话 它 将 会 真 正 地 创 建 此 关 键 字 。 如 果 这 不 是 你 所 需 要 的 , 你 应 该 试 着 在 调 用 RegCreateKeyEx 之 前 首 先 调 用 RegOpenKey , 然 后 看 看 它 的 调 用 是 否 成 功 。

一 旦 一 个 关 键 字 被 打 开 , 你 可 以 使 用 RegSetValueEx 来 设 置 它 的 值 , 如 果 需 要 的 话 , 你 也 可 以 创 建 它 。 如 果 一 个 值 还 不 存 在 , 而 你 又 不 想 创 建 它 , 你 应 该 在 调 用 RegSetValueEx 之 前 首 先 调 用 RegQueryValueEx , 然 后 测 试 它 的 返 回 值 是 不 是 ERROR_SUCCESS 。 下 面 是 在 VBREG 的 SetRegValue 过 程 中 对 RegSetValueEx 的 调 用 :

lResult = RegSetValueEx(phkResult , sSetValue , 0 , REG_SZ , sValue , _

CLng(Len(sValue) + 1))

在 上 面 的 调 用 中 , REG_SZ 是 被 写 入 的 值 的 类 型 。 正 如 在 前 面 所 提 到 过 的 , 如 果 你 正 确 转 递 参 数 , 你 也 可 以 写 入 其 它 类 型 的 值 。

你 还 可 以 通 过 使 用 RegQueryValueEx 来 在 注 册 库 中 查 询 一 个 存 在 的 值 。 如 果 此 函 数 调 用 成 功 , 它 将 会 返 回 ERROR_SUCCESS 标 志 。 下 面 是 VBREG 的 GetRetValue 过 程 中 的 代 码 段 :

 

‘ Creat the buffer.

szBuffer = Space(255)

lBuffSize = Len(szBuffer)

‘ Open the key.

RegOpenKeyEx(hKey , lpszSubKey , 0 , 1 , phkResult)

‘ Query the value

lResult = RegQueryValueEx( phkResult , szKey , 0 , 0 , szBuffer , lBuffSize)

 

在 这 里 需 要 注 意 的 最 为 重 要 的 事 情 是 要 为 缓 冲 创 建 足 够 的 空 间 。 使 用 C 语 言 APIs 返 回 一 个 STRING 值 作 为 一 个 输 出 ( out ) 参 数 ( 例 如 RegQueryValueEx 的 第 5 个 参 数 ) , 你 必 须 显 式 地 为 此 参 数 定 义 一 个 空 间 。 如 果 这 项 操 作 失 败 , 将 会 导 致 产 生 一 个 不 可 预 料 的 结 果 值 。

这 篇 文 章 意 图 告 诉 你 在 Visual Basic 4.0 应 用 程 序 中 如 何 使 用 Windows 注 册 库 APIs 。 如 果 你 希 望 看 到 更 多 的 有 关 Windows 注 册 库 或 注 册 库 APIs 的 信 息 , 请 参 考 Visual C++ 所 带 的 Windows API 参 考 , 以 及 在 Microsoft 开 发 库 ( Microsoft Development Library ) 中 有 关 的 文 字 。