ReactOS-Freeldr注册表

这里介绍的注册表是Freeldr实现的一个简易版本,和Windows系统提供的还是有一些差异的。注册表在磁盘中是以HIVE文件形式存在的,格式比较复杂,放到后面。这一节说的是注册表的内存结构。
Freeldr中注册表操作的代码都在freeldr/freeldr/reactos/registry.c中。
FRLDRHKEY结构表示注册表中的一个KEY(键)。
  1. typedef struct _REG_KEY
  2. {
  3.   LIST_ENTRY KeyList;      // 链表头
  4.   LIST_ENTRY SubKeyList;   // 子键列表
  5.   LIST_ENTRY ValueList;    // 值列表
  6.   ULONG SubKeyCount;       // 子键数量
  7.   ULONG ValueCount;        // 值数量
  8.   ULONG NameSize;          // 键名大小
  9.   PWCHAR Name;             // 键名
  10.   /* default data */
  11.   ULONG DataType;          // 默认数据类型
  12.   ULONG DataSize;          // 默认数据大小
  13.   PCHAR Data;              // 默认数据指针
  14. } KEY, *FRLDRHKEY, **PFRLDRHKEY;
SubKeyList是该KEY的子键的列表, SubKeyList链接到子键的KeyList元素。
ValueList是在这个KEY下左右Value(值)的列表。
DataType、DataSize、Data是该健的默认数据,也就是无名数据。

freeldr/freeldr/reactos/registry.c
  1. VOID RegInitializeRegistry (VOID)
  2. {
  3.   RootKey = (FRLDRHKEY) MmHeapAlloc (sizeof(KEY));
  4.   InitializeListHead (&RootKey->SubKeyList);
  5.   InitializeListHead (&RootKey->ValueList);
  6.   InitializeListHead (&RootKey->KeyList);
  7.   RootKey->SubKeyCount = 0;
  8.   RootKey->ValueCount = 0;
  9.   RootKey->NameSize = 4;
  10.   RootKey->Name = MmHeapAlloc (4);
  11.   wcscpy (RootKey->Name, L"//");
  12.   RootKey->DataType = 0;
  13.   RootKey->DataSize = 0;
  14.   RootKey->Data = NULL;
  15.   RegCreateKey (RootKey,
  16.         L"Registry//Machine//SYSTEM",
  17.         NULL);
  18. }
这是注册表的初始化函数。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
  1. LONG RegCreateKey(FRLDRHKEY ParentKey,
  2.                   PCWSTR KeyName,
  3.                   PFRLDRHKEY Key)
  4. {
  5.    ......
  6.   /* 如果KeyName第一个字符是"/"或者ParentKey为空, 表示从根键开始搜索
  7.      否则从ParentKey开始创建 */
  8.   if (*KeyName == L'//')
  9.     {
  10.       KeyName++;
  11.       CurrentKey = RootKey;
  12.     }
  13.   else if (ParentKey == NULL)
  14.     {
  15.       CurrentKey = RootKey;
  16.     }
  17.   else
  18.     {
  19.       CurrentKey = ParentKey;
  20.     }
  21.   /* 处理链接 */
  22.   if (CurrentKey->DataType == REG_LINK)
  23.     {
  24.       CurrentKey = (FRLDRHKEY)CurrentKey->Data;
  25.     }
  26.   /* 根据传入的键名(xxx/yyy/zzz)创建新键 */
  27.   while (*KeyName != 0)
  28.   {
  29.       /* 跳过开头的"/" */
  30.       if (*KeyName == L'//')
  31.          KeyName++;
  32.       /* p是下一个键开始的位置
  33.          subKeyLength是需要新建的键的名称长度, name是名称指针*/
  34.       p = wcschr(KeyName, L'//');
  35.       if ((p != NULL) && (p != KeyName))
  36.     {
  37.       subkeyLength = p - KeyName;
  38.       stringLength = subkeyLength + 1;
  39.       name = KeyName;
  40.     }
  41.       else
  42.     {
  43.       subkeyLength = wcslen(KeyName);
  44.       stringLength = subkeyLength;
  45.       name = KeyName;
  46.     }
  47.       NameSize = (subkeyLength + 1) * sizeof(WCHAR);
  48.       /* 从当前当前键的子键中搜索需要新建的键名 */
  49.       Ptr = CurrentKey->SubKeyList.Flink;
  50.       CmpResult = 1;
  51.       while (Ptr != &CurrentKey->SubKeyList)
  52.     {
  53.       SearchKey = CONTAINING_RECORD(Ptr,
  54.                     KEY,
  55.                     KeyList);
  56.       CmpResult = _wcsnicmp(SearchKey->Name, name, subkeyLength);
  57.       if (CmpResult == 0 && SearchKey->NameSize == NameSize)
  58.         break;
  59.       else if (CmpResult == -1)
  60.         break;
  61.       Ptr = Ptr->Flink;
  62.     }
  63.       if (CmpResult != 0)
  64.     {
  65.       /* 没有找到需要新建 */
  66.       NewKey = (FRLDRHKEY)MmHeapAlloc(sizeof(KEY));
  67.       if (NewKey == NULL)
  68.         return(ERROR_OUTOFMEMORY);
  69.       InitializeListHead(&NewKey->SubKeyList);
  70.       InitializeListHead(&NewKey->ValueList);
  71.       NewKey->SubKeyCount = 0;
  72.       NewKey->ValueCount = 0;
  73.       NewKey->DataType = 0;
  74.       NewKey->DataSize = 0;
  75.       NewKey->Data = NULL;
  76.       InsertTailList(Ptr, &NewKey->KeyList);
  77.       CurrentKey->SubKeyCount++;
  78.       NewKey->NameSize = NameSize;
  79.       NewKey->Name = (PWCHAR)MmHeapAlloc(NewKey->NameSize);
  80.       if (NewKey->Name == NULL)
  81.         return(ERROR_OUTOFMEMORY);
  82.       memcpy(NewKey->Name, name, NewKey->NameSize - sizeof(WCHAR));
  83.       NewKey->Name[subkeyLength] = 0;
  84.       CurrentKey = NewKey;
  85.     }
  86.       else
  87.     {
  88.       /* 如果找到直接使用 */
  89.       CurrentKey = SearchKey;
  90.       if (CurrentKey->DataType == REG_LINK)
  91.         {
  92.           CurrentKey = (FRLDRHKEY)CurrentKey->Data;
  93.         }
  94.     }
  95.       /* 下一段键名 */
  96.       KeyName = KeyName + stringLength;
  97.   }
  98.  
  99.   if (Key != NULL)
  100.     *Key = CurrentKey;
  101.   return(ERROR_SUCCESS);
  102. }
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。
里面的节点的数据结构是
  1. typedef struct _REG_VALUE
  2. {
  3.   LIST_ENTRY ValueList;
  4.   /* value name */
  5.   ULONG NameSize;
  6.   PWCHAR Name;
  7.   /* value data */
  8.   ULONG DataType;
  9.   ULONG DataSize;
  10.   PCHAR Data;
  11. } VALUE, *PVALUE;
一目了然, ValueList是连入ValueList的表头, NameSize和Name表示值名,DataType、DataSize、Data表示值。
我们看一个为KEY赋值的函数RegSetValue。
函数有三个参数:
KEY是键的指针
ValueName是需要设置的键值名
Type是键值的类型
Data、DataSize表示键值数据指针和键值数据大小
freeldr/freeldr/reactos/registry.c
  1. LONG RegSetValue(FRLDRHKEY Key,
  2.                  PCWSTR ValueName,
  3.                  ULONG Type,
  4.                  PCSTR Data,
  5.                  ULONG DataSize)
  6. {
  7.   PLIST_ENTRY Ptr;
  8.   PVALUE Value = NULL;
  9.   if ((ValueName == NULL) || (*ValueName == 0))
  10.   {
  11.       /* 如果ValueName是NULL或空, 表示更改KEY的默认值 */
  12.     if ((Key->Data != NULL) && (Key->DataSize > sizeof(PUCHAR)))
  13.     {
  14.       MmHeapFree(Key->Data);
  15.     }
  16.     /* 如果Data的大小小于Data指针本身的大小, 直接把值存入指针 */
  17.     if (DataSize <= sizeof(PUCHAR))
  18.     {
  19.       Key->DataSize = DataSize;
  20.       Key->DataType = Type;
  21.       memcpy(&Key->Data, Data, DataSize);
  22.     }
  23.     else
  24.     {
  25.       Key->Data = MmHeapAlloc(DataSize);
  26.       Key->DataSize = DataSize;
  27.       Key->DataType = Type;
  28.       memcpy(Key->Data, Data, DataSize);
  29.     }
  30.   }
  31.   else
  32.   {
  33.       /* 处理默认属性 */
  34.       Ptr = Key->ValueList.Flink;
  35.       /* 遍历ValueList找到同名的VALUE结构 */
  36.       while (Ptr != &Key->ValueList)
  37.     {
  38.       Value = CONTAINING_RECORD(Ptr,
  39.                     VALUE,
  40.                     ValueList);
  41.       if (_wcsicmp(Value->Name, ValueName) == 0)
  42.         break;
  43.       Ptr = Ptr->Flink;
  44.     }
  45.       if (Ptr == &Key->ValueList)
  46.     {
  47.       /* 没有找到同名的VALUE, 创建一个 */
  48.       Value = (PVALUE)MmHeapAlloc(sizeof(VALUE));
  49.       if (Value == NULL)
  50.         return(ERROR_OUTOFMEMORY);
  51.       /* 插入Key->ValueList, 递增ValueCount */
  52.       InsertTailList(&Key->ValueList, &Value->ValueList);
  53.       Key->ValueCount++;
  54.       /* 初始化VALUE */
  55.       Value->NameSize = (wcslen(ValueName)+1)*sizeof(WCHAR);
  56.       Value->Name = (PWCHAR)MmHeapAlloc(Value->NameSize);
  57.       if (Value->Name == NULL)
  58.         return(ERROR_OUTOFMEMORY);
  59.       wcscpy(Value->Name, ValueName);
  60.       Value->DataType = REG_NONE;
  61.       Value->DataSize = 0;
  62.       Value->Data = NULL;
  63.     }
  64.       /* 填写VALUE.Data, 如果数据小于Data指针本身, Data直接存储数据 */
  65.       if ((Value->Data != NULL) && (Value->DataSize > sizeof(PUCHAR)))
  66.     {
  67.       MmHeapFree(Value->Data);
  68.     }
  69.       if (DataSize <= sizeof(PUCHAR))
  70.     {
  71.       Value->DataSize = DataSize;
  72.       Value->DataType = Type;
  73.       memcpy(&Value->Data, Data, DataSize);
  74.     }
  75.       else
  76.     {
  77.       Value->Data = MmHeapAlloc(DataSize);
  78.       if (Value->Data == NULL)
  79.         return(ERROR_OUTOFMEMORY);
  80.       Value->DataType = Type;
  81.       Value->DataSize = DataSize;
  82.       memcpy(Value->Data, Data, DataSize);
  83.     }
  84.   }
  85.   return(ERROR_SUCCESS);
  86. }
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。

你可能感兴趣的:(ReactOS代码精读)