这里介绍的注册表是Freeldr实现的一个简易版本,和Windows系统提供的还是有一些差异的。注册表在磁盘中是以HIVE文件形式存在的,格式比较复杂,放到后面。这一节说的是注册表的内存结构。
Freeldr中注册表操作的代码都在freeldr/freeldr/reactos/registry.c中。
FRLDRHKEY结构表示注册表中的一个KEY(键)。
- typedef struct _REG_KEY
- {
- LIST_ENTRY KeyList; // 链表头
- LIST_ENTRY SubKeyList; // 子键列表
- LIST_ENTRY ValueList; // 值列表
- ULONG SubKeyCount; // 子键数量
- ULONG ValueCount; // 值数量
- ULONG NameSize; // 键名大小
- PWCHAR Name; // 键名
- /* default data */
- ULONG DataType; // 默认数据类型
- ULONG DataSize; // 默认数据大小
- PCHAR Data; // 默认数据指针
- } KEY, *FRLDRHKEY, **PFRLDRHKEY;
SubKeyList是该KEY的子键的列表, SubKeyList链接到子键的KeyList元素。
ValueList是在这个KEY下左右Value(值)的列表。
DataType、DataSize、Data是该健的默认数据,也就是无名数据。
freeldr/freeldr/reactos/registry.c
- VOID RegInitializeRegistry (VOID)
- {
- RootKey = (FRLDRHKEY) MmHeapAlloc (sizeof(KEY));
- InitializeListHead (&RootKey->SubKeyList);
- InitializeListHead (&RootKey->ValueList);
- InitializeListHead (&RootKey->KeyList);
- RootKey->SubKeyCount =
0; - RootKey->ValueCount =
0; - RootKey->NameSize =
4; - RootKey->Name = MmHeapAlloc (
4); - wcscpy (RootKey->Name, L"//");
- RootKey->DataType =
0; - RootKey->DataSize =
0; - RootKey->Data = NULL;
- RegCreateKey (RootKey,
- L"Registry//Machine//SYSTEM",
- NULL);
- }
这是注册表的初始化函数。RootKey是registry.c维护的全局变量,代表根键"/"。这个函数对RootKey进行初始化后调用RegCreateKey创建/Registry/Machine/SYSTEM子键。
RegCreateKey有三个参数
ParentKey是KeyName起始的键值(父键), 如果ParentKey是NULL, KeyName将以RootKey作为父键
KeyName是需要创建的键名或路径,函数将从父键开始按照这个路径进入子键,路径中任何不存在的子键将会被建立,最后函数返回代表路径中最后一个节点的KEY。如果这个字符串以"/"开头,无论ParentKey是什么,函数都会以RootKey作为父键。
Key返回路径中的最后一个节点。
freeldr/freeldr/reactos/registry.c
- LONG RegCreateKey(FRLDRHKEY ParentKey,
- PCWSTR KeyName,
- PFRLDRHKEY Key)
- {
- ......
- /* 如果KeyName第一个字符是"/"或者ParentKey为空, 表示从根键开始搜索
- 否则从ParentKey开始创建 */
- if (*KeyName == L'//')
- {
- KeyName++;
- CurrentKey = RootKey;
- }
- else if (ParentKey == NULL)
- {
- CurrentKey = RootKey;
- }
- else
- {
- CurrentKey = ParentKey;
- }
- /* 处理链接 */
- if (CurrentKey->DataType == REG_LINK)
- {
- CurrentKey = (FRLDRHKEY)CurrentKey->Data;
- }
- /* 根据传入的键名(xxx/yyy/zzz)创建新键 */
- while (*KeyName !=
0) - {
- /* 跳过开头的"/" */
- if (*KeyName == L'//')
- KeyName++;
- /* p是下一个键开始的位置
- subKeyLength是需要新建的键的名称长度, name是名称指针*/
- p = wcschr(KeyName, L'//');
- if ((p != NULL) && (p != KeyName))
- {
- subkeyLength = p - KeyName;
- stringLength = subkeyLength +
1; - name = KeyName;
- }
- else
- {
- subkeyLength = wcslen(KeyName);
- stringLength = subkeyLength;
- name = KeyName;
- }
- NameSize = (subkeyLength +
1) * sizeof(WCHAR); - /* 从当前当前键的子键中搜索需要新建的键名 */
- Ptr = CurrentKey->SubKeyList.Flink;
- CmpResult =
1; - while (Ptr != &CurrentKey->SubKeyList)
- {
- SearchKey = CONTAINING_RECORD(Ptr,
- KEY,
- KeyList);
- CmpResult = _wcsnicmp(SearchKey->Name, name, subkeyLength);
- if (CmpResult ==
0&& SearchKey->NameSize == NameSize) - break;
- else if (CmpResult == -
1) - break;
- Ptr = Ptr->Flink;
- }
- if (CmpResult !=
0) - {
- /* 没有找到需要新建 */
- NewKey = (FRLDRHKEY)MmHeapAlloc(sizeof(KEY));
- if (NewKey == NULL)
- return(ERROR_OUTOFMEMORY);
- InitializeListHead(&NewKey->SubKeyList);
- InitializeListHead(&NewKey->ValueList);
- NewKey->SubKeyCount =
0; - NewKey->ValueCount =
0; - NewKey->DataType =
0; - NewKey->DataSize =
0; - NewKey->Data = NULL;
- InsertTailList(Ptr, &NewKey->KeyList);
- CurrentKey->SubKeyCount++;
- NewKey->NameSize = NameSize;
- NewKey->Name = (PWCHAR)MmHeapAlloc(NewKey->NameSize);
- if (NewKey->Name == NULL)
- return(ERROR_OUTOFMEMORY);
- memcpy(NewKey->Name, name, NewKey->NameSize - sizeof(WCHAR));
- NewKey->Name[subkeyLength] =
0; - CurrentKey = NewKey;
- }
- else
- {
- /* 如果找到直接使用 */
- CurrentKey = SearchKey;
- if (CurrentKey->DataType == REG_LINK)
- {
- CurrentKey = (FRLDRHKEY)CurrentKey->Data;
- }
- }
- /* 下一段键名 */
- KeyName = KeyName + stringLength;
- }
- if (Key != NULL)
- *Key = CurrentKey;
- return(ERROR_SUCCESS);
- }
6-25行首先根据ParentKey和KeyName的值确定路径的起始键,并赋值给CurrentKey。
27-47行把KeyName按照"/"进行分解,每分解出一个元素就是一个需要寻找的子键名。遍历当前键CurrentKey的子键列表SubKeyList,如果存在相同的键名说明该键已经存在,可以直接使用这个键继续寻找下一层子健(88-96)。
如果不存在需要申请一个KEY结构,初始化后链接入CurrentKey.SubKeyList (65 - 84)。
最后函数创建完所有路径上所有的键,把最后一个KEY的指针返回给函数调用者。函数调用者可以使用这个指针调用RegQueryValue等函数操作它存储的Value(值)。
KEY的结构说完了,再来看看一个KEY是如何存储Value(值)的。
首先每个KEY都有一个无名值(默认值),存储在KEY结构的DataType、DataSize和Data中。如果这个KEY是一个连接,那么DataType将会是REG_LINK,Data直接指向连接的KEY结构。
同事每个KEY还有一个值链表——ValueList。
里面的节点的数据结构是
- typedef struct _REG_VALUE
- {
- LIST_ENTRY ValueList;
- /* value name */
- ULONG NameSize;
- PWCHAR Name;
- /* value data */
- ULONG DataType;
- ULONG DataSize;
- PCHAR Data;
- } VALUE, *PVALUE;
一目了然, ValueList是连入ValueList的表头, NameSize和Name表示值名,DataType、DataSize、Data表示值。
我们看一个为KEY赋值的函数RegSetValue。
函数有三个参数:
KEY是键的指针
ValueName是需要设置的键值名
Type是键值的类型
Data、DataSize表示键值数据指针和键值数据大小
freeldr/freeldr/reactos/registry.c
- LONG RegSetValue(FRLDRHKEY Key,
- PCWSTR ValueName,
- ULONG Type,
- PCSTR Data,
- ULONG DataSize)
- {
- PLIST_ENTRY Ptr;
- PVALUE Value = NULL;
- if ((ValueName == NULL) || (*ValueName ==
0)) - {
- /* 如果ValueName是NULL或空, 表示更改KEY的默认值 */
- if ((Key->Data != NULL) && (Key->DataSize > sizeof(PUCHAR)))
- {
- MmHeapFree(Key->Data);
- }
- /* 如果Data的大小小于Data指针本身的大小, 直接把值存入指针 */
- if (DataSize <= sizeof(PUCHAR))
- {
- Key->DataSize = DataSize;
- Key->DataType = Type;
- memcpy(&Key->Data, Data, DataSize);
- }
- else
- {
- Key->Data = MmHeapAlloc(DataSize);
- Key->DataSize = DataSize;
- Key->DataType = Type;
- memcpy(Key->Data, Data, DataSize);
- }
- }
- else
- {
- /* 处理默认属性 */
- Ptr = Key->ValueList.Flink;
- /* 遍历ValueList找到同名的VALUE结构 */
- while (Ptr != &Key->ValueList)
- {
- Value = CONTAINING_RECORD(Ptr,
- VALUE,
- ValueList);
- if (_wcsicmp(Value->Name, ValueName) ==
0) - break;
- Ptr = Ptr->Flink;
- }
- if (Ptr == &Key->ValueList)
- {
- /* 没有找到同名的VALUE, 创建一个 */
- Value = (PVALUE)MmHeapAlloc(sizeof(VALUE));
- if (Value == NULL)
- return(ERROR_OUTOFMEMORY);
- /* 插入Key->ValueList, 递增ValueCount */
- InsertTailList(&Key->ValueList, &Value->ValueList);
- Key->ValueCount++;
- /* 初始化VALUE */
- Value->NameSize = (wcslen(ValueName)+
1)*sizeof(WCHAR); - Value->Name = (PWCHAR)MmHeapAlloc(Value->NameSize);
- if (Value->Name == NULL)
- return(ERROR_OUTOFMEMORY);
- wcscpy(Value->Name, ValueName);
- Value->DataType = REG_NONE;
- Value->DataSize =
0; - Value->Data = NULL;
- }
- /* 填写VALUE.Data, 如果数据小于Data指针本身, Data直接存储数据 */
- if ((Value->Data != NULL) && (Value->DataSize > sizeof(PUCHAR)))
- {
- MmHeapFree(Value->Data);
- }
- if (DataSize <= sizeof(PUCHAR))
- {
- Value->DataSize = DataSize;
- Value->DataType = Type;
- memcpy(&Value->Data, Data, DataSize);
- }
- else
- {
- Value->Data = MmHeapAlloc(DataSize);
- if (Value->Data == NULL)
- return(ERROR_OUTOFMEMORY);
- Value->DataType = Type;
- Value->DataSize = DataSize;
- memcpy(Value->Data, Data, DataSize);
- }
- }
- return(ERROR_SUCCESS);
- }
9-30行,当ValueName为空时函数将设置KEY的默认键值,这里有一个优化,如果传入的Data的大小小于一个指针本身占用的空间时,函数直接将值本身赋值给Value.Data指针。
如果不是设置默认值,首先函数遍历Key.ValueList链表,搜索与ValueName相同键值(34-44)。
如果发现相同的则直接更改这个键值(45-63),如果没有同名键值那么需要生成一个Value结构并将该结构连接入Key.ValueList(47-62),最后填写Value的值(65-82)。
Registry.c 还有RegQueryValue取得值,RegDeleteKey删除键等函数,都是对KEY、VALUE结构的操作,大致都相同就不赘述了。
下一节我们将看看注册表的文件结构HIVE。