minor corrections by M.Taguchi
[reactos.git] / freeldr / freeldr / reactos / registry.c
1 /*
2 * FreeLoader
3 *
4 * Copyright (C) 2001, 2002 Eric Kohl
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <freeldr.h>
22 #include <mm.h>
23 #include <rtl.h>
24 #include <debug.h>
25 #include "registry.h"
26
27 #include <ui.h>
28
29 static HKEY RootKey;
30
31
32 VOID
33 RegInitializeRegistry (VOID)
34 {
35 #if 0
36 HKEY TestKey;
37 #endif
38
39 /* Create root key */
40 RootKey = (HKEY) MmAllocateMemory (sizeof(KEY));
41
42 InitializeListHead (&RootKey->SubKeyList);
43 InitializeListHead (&RootKey->ValueList);
44 InitializeListHead (&RootKey->KeyList);
45
46 RootKey->SubKeyCount = 0;
47 RootKey->ValueCount = 0;
48
49 RootKey->NameSize = 2;
50 RootKey->Name = (PUCHAR)MmAllocateMemory (2);
51 strcpy (RootKey->Name, "\\");
52
53 RootKey->DataType = 0;
54 RootKey->DataSize = 0;
55 RootKey->Data = NULL;
56
57 /* Create 'SYSTEM' key */
58 RegCreateKey (RootKey,
59 "Registry\\Machine\\SYSTEM",
60 NULL);
61
62 /* Create 'HARDWARE' key */
63 RegCreateKey (RootKey,
64 "Registry\\Machine\\HARDWARE",
65 NULL);
66
67 /* Create 'HARDWARE\DESCRIPTION' key */
68 RegCreateKey (RootKey,
69 "Registry\\Machine\\HARDWARE\\DESCRIPTION",
70 NULL);
71
72 /* Create 'HARDWARE\DEVICEMAP' key */
73 RegCreateKey (RootKey,
74 "Registry\\Machine\\HARDWARE\\DEVICEMAP",
75 NULL);
76
77 /* Create 'HARDWARE\RESOURCEMAP' key */
78 RegCreateKey (RootKey,
79 "Registry\\Machine\\HARDWARE\\RESOURCEMAP",
80 NULL);
81
82 /* Testcode */
83 #if 0
84 RegCreateKey (RootKey,
85 "Registry\\Machine\\HARDWARE\\DESCRIPTION\\TestKey",
86 &TestKey);
87
88 RegSetValue (TestKey,
89 "TestValue",
90 REG_SZ,
91 (PUCHAR)"TestString",
92 11);
93 #endif
94 }
95
96
97 S32
98 RegInitCurrentControlSet(BOOL LastKnownGood)
99 {
100 CHAR ControlSetKeyName[80];
101 HKEY SelectKey;
102 HKEY SystemKey;
103 HKEY ControlSetKey;
104 HKEY LinkKey;
105 U32 CurrentSet = 0;
106 U32 DefaultSet = 0;
107 U32 LastKnownGoodSet = 0;
108 U32 DataSize;
109 S32 Error;
110
111 Error = RegOpenKey(NULL,
112 "\\Registry\\Machine\\SYSTEM\\Select",
113 &SelectKey);
114 if (Error != ERROR_SUCCESS)
115 {
116 DbgPrint((DPRINT_REGISTRY, "RegOpenKey() failed (Error %u)\n", (int)Error));
117 return(Error);
118 }
119
120 DataSize = sizeof(U32);
121 Error = RegQueryValue(SelectKey,
122 "Default",
123 NULL,
124 (PUCHAR)&DefaultSet,
125 &DataSize);
126 if (Error != ERROR_SUCCESS)
127 {
128 DbgPrint((DPRINT_REGISTRY, "RegQueryValue('Default') failed (Error %u)\n", (int)Error));
129 return(Error);
130 }
131
132 DataSize = sizeof(U32);
133 Error = RegQueryValue(SelectKey,
134 "LastKnownGood",
135 NULL,
136 (PUCHAR)&LastKnownGoodSet,
137 &DataSize);
138 if (Error != ERROR_SUCCESS)
139 {
140 DbgPrint((DPRINT_REGISTRY, "RegQueryValue('Default') failed (Error %u)\n", (int)Error));
141 return(Error);
142 }
143
144 CurrentSet = (LastKnownGood == TRUE) ? LastKnownGoodSet : DefaultSet;
145 strcpy(ControlSetKeyName, "ControlSet");
146 switch(CurrentSet)
147 {
148 case 1:
149 strcat(ControlSetKeyName, "001");
150 break;
151 case 2:
152 strcat(ControlSetKeyName, "002");
153 break;
154 case 3:
155 strcat(ControlSetKeyName, "003");
156 break;
157 case 4:
158 strcat(ControlSetKeyName, "004");
159 break;
160 case 5:
161 strcat(ControlSetKeyName, "005");
162 break;
163 }
164
165 Error = RegOpenKey(NULL,
166 "\\Registry\\Machine\\SYSTEM",
167 &SystemKey);
168 if (Error != ERROR_SUCCESS)
169 {
170 DbgPrint((DPRINT_REGISTRY, "RegOpenKey(SystemKey) failed (Error %u)\n", (int)Error));
171 return(Error);
172 }
173
174 Error = RegOpenKey(SystemKey,
175 ControlSetKeyName,
176 &ControlSetKey);
177 if (Error != ERROR_SUCCESS)
178 {
179 DbgPrint((DPRINT_REGISTRY, "RegOpenKey(ControlSetKey) failed (Error %u)\n", (int)Error));
180 return(Error);
181 }
182
183 Error = RegCreateKey(SystemKey,
184 "CurrentControlSet",
185 &LinkKey);
186 if (Error != ERROR_SUCCESS)
187 {
188 DbgPrint((DPRINT_REGISTRY, "RegCreateKey(LinkKey) failed (Error %u)\n", (int)Error));
189 return(Error);
190 }
191
192 Error = RegSetValue(LinkKey,
193 NULL,
194 REG_LINK,
195 (PUCHAR)&ControlSetKey,
196 sizeof(PVOID));
197 if (Error != ERROR_SUCCESS)
198 {
199 DbgPrint((DPRINT_REGISTRY, "RegSetValue(LinkKey) failed (Error %u)\n", (int)Error));
200 return(Error);
201 }
202
203 return(ERROR_SUCCESS);
204 }
205
206
207 S32
208 RegCreateKey(HKEY ParentKey,
209 PCHAR KeyName,
210 PHKEY Key)
211 {
212 PLIST_ENTRY Ptr;
213 HKEY SearchKey = INVALID_HANDLE_VALUE;
214 HKEY CurrentKey;
215 HKEY NewKey;
216 PCHAR p;
217 PCHAR name;
218 int subkeyLength;
219 int stringLength;
220
221 DbgPrint((DPRINT_REGISTRY, "KeyName '%s'\n", KeyName));
222
223 if (*KeyName == '\\')
224 {
225 KeyName++;
226 CurrentKey = RootKey;
227 }
228 else if (ParentKey == NULL)
229 {
230 CurrentKey = RootKey;
231 }
232 else
233 {
234 CurrentKey = ParentKey;
235 }
236
237 /* Check whether current key is a link */
238 if (CurrentKey->DataType == REG_LINK)
239 {
240 CurrentKey = (HKEY)CurrentKey->Data;
241 }
242
243 while (*KeyName != 0)
244 {
245 DbgPrint((DPRINT_REGISTRY, "KeyName '%s'\n", KeyName));
246
247 if (*KeyName == '\\')
248 KeyName++;
249 p = strchr(KeyName, '\\');
250 if ((p != NULL) && (p != KeyName))
251 {
252 subkeyLength = p - KeyName;
253 stringLength = subkeyLength + 1;
254 name = KeyName;
255 }
256 else
257 {
258 subkeyLength = strlen(KeyName);
259 stringLength = subkeyLength;
260 name = KeyName;
261 }
262
263 Ptr = CurrentKey->SubKeyList.Flink;
264 while (Ptr != &CurrentKey->SubKeyList)
265 {
266 DbgPrint((DPRINT_REGISTRY, "Ptr 0x%x\n", Ptr));
267
268 SearchKey = CONTAINING_RECORD(Ptr,
269 KEY,
270 KeyList);
271 DbgPrint((DPRINT_REGISTRY, "SearchKey 0x%x\n", SearchKey));
272 DbgPrint((DPRINT_REGISTRY, "Searching '%s'\n", SearchKey->Name));
273 if (strnicmp(SearchKey->Name, name, subkeyLength) == 0)
274 break;
275
276 Ptr = Ptr->Flink;
277 }
278
279 if (Ptr == &CurrentKey->SubKeyList)
280 {
281 /* no key found -> create new subkey */
282 NewKey = (HKEY)MmAllocateMemory(sizeof(KEY));
283 if (NewKey == NULL)
284 return(ERROR_OUTOFMEMORY);
285
286 InitializeListHead(&NewKey->SubKeyList);
287 InitializeListHead(&NewKey->ValueList);
288
289 NewKey->SubKeyCount = 0;
290 NewKey->ValueCount = 0;
291
292 NewKey->DataType = 0;
293 NewKey->DataSize = 0;
294 NewKey->Data = NULL;
295
296 InsertTailList(&CurrentKey->SubKeyList, &NewKey->KeyList);
297 CurrentKey->SubKeyCount++;
298
299 NewKey->NameSize = subkeyLength + 1;
300 NewKey->Name = (PCHAR)MmAllocateMemory(NewKey->NameSize);
301 if (NewKey->Name == NULL)
302 return(ERROR_OUTOFMEMORY);
303 memcpy(NewKey->Name, name, subkeyLength);
304 NewKey->Name[subkeyLength] = 0;
305
306 DbgPrint((DPRINT_REGISTRY, "NewKey 0x%x\n", NewKey));
307 DbgPrint((DPRINT_REGISTRY, "NewKey '%s' Length %d\n", NewKey->Name, NewKey->NameSize));
308
309 CurrentKey = NewKey;
310 }
311 else
312 {
313 CurrentKey = SearchKey;
314
315 /* Check whether current key is a link */
316 if (CurrentKey->DataType == REG_LINK)
317 {
318 CurrentKey = (HKEY)CurrentKey->Data;
319 }
320 }
321
322 KeyName = KeyName + stringLength;
323 }
324
325 if (Key != NULL)
326 *Key = CurrentKey;
327
328 return(ERROR_SUCCESS);
329 }
330
331
332 S32
333 RegDeleteKey(HKEY Key,
334 PCHAR Name)
335 {
336
337
338 if (strchr(Name, '\\') != NULL)
339 return(ERROR_INVALID_PARAMETER);
340
341
342
343 return(ERROR_SUCCESS);
344 }
345
346
347 S32
348 RegEnumKey(HKEY Key,
349 U32 Index,
350 PCHAR Name,
351 U32* NameSize)
352 {
353 PLIST_ENTRY Ptr;
354 HKEY SearchKey;
355 U32 Count = 0;
356 U32 Size;
357
358 Ptr = Key->SubKeyList.Flink;
359 while (Ptr != &Key->SubKeyList)
360 {
361 if (Index == Count)
362 break;
363
364 Count++;
365 Ptr = Ptr->Flink;
366 }
367
368 if (Ptr == &Key->SubKeyList)
369 return(ERROR_NO_MORE_ITEMS);
370
371 SearchKey = CONTAINING_RECORD(Ptr,
372 KEY,
373 KeyList);
374
375 DbgPrint((DPRINT_REGISTRY, "Name '%s' Length %d\n", SearchKey->Name, SearchKey->NameSize));
376
377 Size = min(SearchKey->NameSize, *NameSize);
378 *NameSize = Size;
379 memcpy(Name, SearchKey->Name, Size);
380
381 return(ERROR_SUCCESS);
382 }
383
384
385 S32
386 RegOpenKey(HKEY ParentKey,
387 PCHAR KeyName,
388 PHKEY Key)
389 {
390 PLIST_ENTRY Ptr;
391 HKEY SearchKey = INVALID_HANDLE_VALUE;
392 HKEY CurrentKey;
393 PCHAR p;
394 PCHAR name;
395 int subkeyLength;
396 int stringLength;
397
398 DbgPrint((DPRINT_REGISTRY, "KeyName '%s'\n", KeyName));
399
400 *Key = NULL;
401
402 if (*KeyName == '\\')
403 {
404 KeyName++;
405 CurrentKey = RootKey;
406 }
407 else if (ParentKey == NULL)
408 {
409 CurrentKey = RootKey;
410 }
411 else
412 {
413 CurrentKey = ParentKey;
414 }
415
416 /* Check whether current key is a link */
417 if (CurrentKey->DataType == REG_LINK)
418 {
419 CurrentKey = (HKEY)CurrentKey->Data;
420 }
421
422 while (*KeyName != 0)
423 {
424 DbgPrint((DPRINT_REGISTRY, "KeyName '%s'\n", KeyName));
425
426 if (*KeyName == '\\')
427 KeyName++;
428 p = strchr(KeyName, '\\');
429 if ((p != NULL) && (p != KeyName))
430 {
431 subkeyLength = p - KeyName;
432 stringLength = subkeyLength + 1;
433 name = KeyName;
434 }
435 else
436 {
437 subkeyLength = strlen(KeyName);
438 stringLength = subkeyLength;
439 name = KeyName;
440 }
441
442 Ptr = CurrentKey->SubKeyList.Flink;
443 while (Ptr != &CurrentKey->SubKeyList)
444 {
445 DbgPrint((DPRINT_REGISTRY, "Ptr 0x%x\n", Ptr));
446
447 SearchKey = CONTAINING_RECORD(Ptr,
448 KEY,
449 KeyList);
450
451 DbgPrint((DPRINT_REGISTRY, "SearchKey 0x%x\n", SearchKey));
452 DbgPrint((DPRINT_REGISTRY, "Searching '%s'\n", SearchKey->Name));
453
454 if (strnicmp(SearchKey->Name, name, subkeyLength) == 0)
455 break;
456
457 Ptr = Ptr->Flink;
458 }
459
460 if (Ptr == &CurrentKey->SubKeyList)
461 {
462 return(ERROR_PATH_NOT_FOUND);
463 }
464 else
465 {
466 CurrentKey = SearchKey;
467
468 /* Check whether current key is a link */
469 if (CurrentKey->DataType == REG_LINK)
470 {
471 CurrentKey = (HKEY)CurrentKey->Data;
472 }
473 }
474
475 KeyName = KeyName + stringLength;
476 }
477
478 if (Key != NULL)
479 *Key = CurrentKey;
480
481 return(ERROR_SUCCESS);
482 }
483
484
485 S32
486 RegSetValue(HKEY Key,
487 PCHAR ValueName,
488 U32 Type,
489 PUCHAR Data,
490 U32 DataSize)
491 {
492 PLIST_ENTRY Ptr;
493 PVALUE Value = NULL;
494
495 DbgPrint((DPRINT_REGISTRY, "Key 0x%x, ValueName '%s', Type %d, Data 0x%x, DataSize %d\n",
496 (int)Key, ValueName, (int)Type, (int)Data, (int)DataSize));
497
498 if ((ValueName == NULL) || (*ValueName == 0))
499 {
500 /* set default value */
501 if ((Key->Data != NULL) && (Key->DataSize > sizeof(PUCHAR)))
502 {
503 MmFreeMemory(Key->Data);
504 }
505
506 if (DataSize <= sizeof(PUCHAR))
507 {
508 Key->DataSize = DataSize;
509 Key->DataType = Type;
510 memcpy(&Key->Data, Data, DataSize);
511 }
512 else
513 {
514 Key->Data = (PUCHAR)MmAllocateMemory(DataSize);
515 Key->DataSize = DataSize;
516 Key->DataType = Type;
517 memcpy(Key->Data, Data, DataSize);
518 }
519 }
520 else
521 {
522 /* set non-default value */
523 Ptr = Key->ValueList.Flink;
524 while (Ptr != &Key->ValueList)
525 {
526 Value = CONTAINING_RECORD(Ptr,
527 VALUE,
528 ValueList);
529
530 DbgPrint((DPRINT_REGISTRY, "Value->Name '%s'\n", Value->Name));
531
532 if (stricmp(Value->Name, ValueName) == 0)
533 break;
534
535 Ptr = Ptr->Flink;
536 }
537
538 if (Ptr == &Key->ValueList)
539 {
540 /* add new value */
541 DbgPrint((DPRINT_REGISTRY, "No value found - adding new value\n"));
542
543 Value = (PVALUE)MmAllocateMemory(sizeof(VALUE));
544 if (Value == NULL)
545 return(ERROR_OUTOFMEMORY);
546
547 InsertTailList(&Key->ValueList, &Value->ValueList);
548 Key->ValueCount++;
549
550 Value->NameSize = strlen(ValueName)+1;
551 Value->Name = (PCHAR)MmAllocateMemory(Value->NameSize);
552 if (Value->Name == NULL)
553 return(ERROR_OUTOFMEMORY);
554 strcpy(Value->Name, ValueName);
555 Value->DataType = REG_NONE;
556 Value->DataSize = 0;
557 Value->Data = NULL;
558 }
559
560 /* set new value */
561 if ((Value->Data != NULL) && (Value->DataSize > sizeof(PUCHAR)))
562 {
563 MmFreeMemory(Value->Data);
564 }
565
566 if (DataSize <= sizeof(PUCHAR))
567 {
568 Value->DataSize = DataSize;
569 Value->DataType = Type;
570 memcpy(&Value->Data, Data, DataSize);
571 }
572 else
573 {
574 Value->Data = (PUCHAR)MmAllocateMemory(DataSize);
575 if (Value->Data == NULL)
576 return(ERROR_OUTOFMEMORY);
577 Value->DataType = Type;
578 Value->DataSize = DataSize;
579 memcpy(Value->Data, Data, DataSize);
580 }
581 }
582 return(ERROR_SUCCESS);
583 }
584
585
586 S32
587 RegQueryValue(HKEY Key,
588 PCHAR ValueName,
589 U32* Type,
590 PUCHAR Data,
591 U32* DataSize)
592 {
593 U32 Size;
594 PLIST_ENTRY Ptr;
595 PVALUE Value = NULL;
596
597 if ((ValueName == NULL) || (*ValueName == 0))
598 {
599 /* query default value */
600 if (Key->Data == NULL)
601 return(ERROR_INVALID_PARAMETER);
602
603 if (Type != NULL)
604 *Type = Key->DataType;
605 if ((Data != NULL) && (DataSize != NULL))
606 {
607 if (Key->DataSize <= sizeof(PUCHAR))
608 {
609 Size = min(Key->DataSize, *DataSize);
610 memcpy(Data, &Key->Data, Size);
611 *DataSize = Size;
612 }
613 else
614 {
615 Size = min(Key->DataSize, *DataSize);
616 memcpy(Data, Key->Data, Size);
617 *DataSize = Size;
618 }
619 }
620 else if ((Data == NULL) && (DataSize != NULL))
621 {
622 *DataSize = Key->DataSize;
623 }
624 }
625 else
626 {
627 /* query non-default value */
628 Ptr = Key->ValueList.Flink;
629 while (Ptr != &Key->ValueList)
630 {
631 Value = CONTAINING_RECORD(Ptr,
632 VALUE,
633 ValueList);
634
635 DbgPrint((DPRINT_REGISTRY, "Searching for '%s'. Value name '%s'\n", ValueName, Value->Name));
636
637 if (stricmp(Value->Name, ValueName) == 0)
638 break;
639
640 Ptr = Ptr->Flink;
641 }
642
643 if (Ptr == &Key->ValueList)
644 return(ERROR_INVALID_PARAMETER);
645
646 if (Type != NULL)
647 *Type = Value->DataType;
648 if ((Data != NULL) && (DataSize != NULL))
649 {
650 if (Value->DataSize <= sizeof(PUCHAR))
651 {
652 Size = min(Value->DataSize, *DataSize);
653 memcpy(Data, &Value->Data, Size);
654 *DataSize = Size;
655 }
656 else
657 {
658 Size = min(Value->DataSize, *DataSize);
659 memcpy(Data, Value->Data, Size);
660 *DataSize = Size;
661 }
662 }
663 else if ((Data == NULL) && (DataSize != NULL))
664 {
665 *DataSize = Value->DataSize;
666 }
667 }
668
669 return(ERROR_SUCCESS);
670 }
671
672
673 S32
674 RegDeleteValue(HKEY Key,
675 PCHAR ValueName)
676 {
677 PLIST_ENTRY Ptr;
678 PVALUE Value = NULL;
679
680 if ((ValueName == NULL) || (*ValueName == 0))
681 {
682 /* delete default value */
683 if (Key->Data != NULL)
684 MmFreeMemory(Key->Data);
685 Key->Data = NULL;
686 Key->DataSize = 0;
687 Key->DataType = 0;
688 }
689 else
690 {
691 /* delete non-default value */
692 Ptr = Key->ValueList.Flink;
693 while (Ptr != &Key->ValueList)
694 {
695 Value = CONTAINING_RECORD(Ptr,
696 VALUE,
697 ValueList);
698 if (stricmp(Value->Name, ValueName) == 0)
699 break;
700
701 Ptr = Ptr->Flink;
702 }
703
704 if (Ptr == &Key->ValueList)
705 return(ERROR_INVALID_PARAMETER);
706
707 /* delete value */
708 Key->ValueCount--;
709 if (Value->Name != NULL)
710 MmFreeMemory(Value->Name);
711 Value->Name = NULL;
712 Value->NameSize = 0;
713
714 if (Value->DataSize > sizeof(PUCHAR))
715 {
716 if (Value->Data != NULL)
717 MmFreeMemory(Value->Data);
718 }
719 Value->Data = NULL;
720 Value->DataSize = 0;
721 Value->DataType = 0;
722
723 RemoveEntryList(&Value->ValueList);
724 MmFreeMemory(Value);
725 }
726 return(ERROR_SUCCESS);
727 }
728
729
730 S32
731 RegEnumValue(HKEY Key,
732 U32 Index,
733 PCHAR ValueName,
734 U32* NameSize,
735 U32* Type,
736 PUCHAR Data,
737 U32* DataSize)
738 {
739 PLIST_ENTRY Ptr;
740 PVALUE Value;
741 U32 Count = 0;
742
743 if (Key->Data != NULL)
744 {
745 if (Index > 0)
746 {
747 Index--;
748 }
749 else
750 {
751 /* enumerate default value */
752 if (ValueName != NULL)
753 *ValueName = 0;
754 if (Type != NULL)
755 *Type = Key->DataType;
756 if (Data != NULL)
757 {
758 if (Key->DataSize <= sizeof(PUCHAR))
759 {
760 memcpy(Data, &Key->Data, min(Key->DataSize, *DataSize));
761 }
762 else
763 {
764 memcpy(Data, Key->Data, min(Key->DataSize, *DataSize));
765 }
766 }
767 if (DataSize != NULL)
768 *DataSize = min(Key->DataSize, *DataSize);
769
770 return(ERROR_SUCCESS);
771 }
772 }
773
774 Ptr = Key->ValueList.Flink;
775 while (Ptr != &Key->ValueList)
776 {
777 if (Index == Count)
778 break;
779
780 Count++;
781 Ptr = Ptr->Flink;
782 }
783
784 if (Ptr == &Key->ValueList)
785 return(ERROR_NO_MORE_ITEMS);
786
787 Value = CONTAINING_RECORD(Ptr,
788 VALUE,
789 ValueList);
790
791 /* enumerate non-default value */
792 if (ValueName != NULL)
793 memcpy(ValueName, Value->Name, min(Value->NameSize, *NameSize));
794 if (Type != NULL)
795 *Type = Value->DataType;
796
797 if (Data != NULL)
798 {
799 if (Value->DataSize <= sizeof(PUCHAR))
800 {
801 memcpy(Data, &Value->Data, min(Value->DataSize, *DataSize));
802 }
803 else
804 {
805 memcpy(Data, Value->Data, min(Value->DataSize, *DataSize));
806 }
807 }
808
809 if (DataSize != NULL)
810 *DataSize = min(Value->DataSize, *DataSize);
811
812 return(ERROR_SUCCESS);
813 }
814
815
816 U32
817 RegGetSubKeyCount (HKEY Key)
818 {
819 return Key->SubKeyCount;
820 }
821
822
823 U32
824 RegGetValueCount (HKEY Key)
825 {
826 if (Key->DataSize != 0)
827 return Key->ValueCount + 1;
828
829 return Key->ValueCount;
830 }
831
832 /* EOF */