Implementation of RtlGetNtVersionNumbers.
[wine] / dlls / ntdll / reg.c
1 /*
2  * Registry functions
3  *
4  * Copyright (C) 1999 Juergen Schmied
5  * Copyright (C) 2000 Alexandre Julliard
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * NOTES:
22  *      HKEY_LOCAL_MACHINE      \\REGISTRY\\MACHINE
23  *      HKEY_USERS              \\REGISTRY\\USER
24  *      HKEY_CURRENT_CONFIG     \\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT
25   *     HKEY_CLASSES            \\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #include <string.h>
32 #include "wine/debug.h"
33 #include "winreg.h"
34 #include "winerror.h"
35 #include "wine/unicode.h"
36 #include "wine/server.h"
37 #include "ntddk.h"
38 #include "ntdll_misc.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(reg);
41
42 /* maximum length of a key/value name in bytes (without terminating null) */
43 #define MAX_NAME_LENGTH ((MAX_PATH-1) * sizeof(WCHAR))
44
45
46 /******************************************************************************
47  * NtCreateKey [NTDLL.@]
48  * ZwCreateKey [NTDLL.@]
49  */
50 NTSTATUS WINAPI NtCreateKey( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
51                              ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
52                              PULONG dispos )
53 {
54     NTSTATUS ret;
55
56     TRACE( "(0x%x,%s,%s,%lx,%lx,%p)\n", attr->RootDirectory, debugstr_us(attr->ObjectName),
57            debugstr_us(class), options, access, retkey );
58
59     if (attr->ObjectName->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW;
60     if (!retkey) return STATUS_INVALID_PARAMETER;
61
62     SERVER_START_REQ( create_key )
63     {
64         req->parent  = attr->RootDirectory;
65         req->access  = access;
66         req->options = options;
67         req->modif   = 0;
68         req->namelen = attr->ObjectName->Length;
69         wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length );
70         if (class) wine_server_add_data( req, class->Buffer, class->Length );
71         if (!(ret = wine_server_call( req )))
72         {
73             *retkey = reply->hkey;
74             if (dispos) *dispos = reply->created ? REG_CREATED_NEW_KEY : REG_OPENED_EXISTING_KEY;
75         }
76     }
77     SERVER_END_REQ;
78     TRACE("<- 0x%04x\n", *retkey);
79     return ret;
80 }
81
82
83 /******************************************************************************
84  * NtOpenKey [NTDLL.@]
85  * ZwOpenKey [NTDLL.@]
86  *
87  *   OUT        PHANDLE                 retkey (returns 0 when failure)
88  *   IN         ACCESS_MASK             access
89  *   IN         POBJECT_ATTRIBUTES      attr
90  */
91 NTSTATUS WINAPI NtOpenKey( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr )
92 {
93     NTSTATUS ret;
94     DWORD len = attr->ObjectName->Length;
95
96     TRACE( "(0x%x,%s,%lx,%p)\n", attr->RootDirectory,
97            debugstr_us(attr->ObjectName), access, retkey );
98
99     if (len > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW;
100     if (!retkey) return STATUS_INVALID_PARAMETER;
101
102     SERVER_START_REQ( open_key )
103     {
104         req->parent = attr->RootDirectory;
105         req->access = access;
106         wine_server_add_data( req, attr->ObjectName->Buffer, len );
107         ret = wine_server_call( req );
108         *retkey = reply->hkey;
109     }
110     SERVER_END_REQ;
111     TRACE("<- 0x%04x\n", *retkey);
112     return ret;
113 }
114
115
116 /******************************************************************************
117  * NtDeleteKey [NTDLL.@]
118  * ZwDeleteKey [NTDLL.@]
119  */
120 NTSTATUS WINAPI NtDeleteKey( HANDLE hkey )
121 {
122     NTSTATUS ret;
123
124     TRACE( "(%x)\n", hkey );
125
126     SERVER_START_REQ( delete_key )
127     {
128         req->hkey = hkey;
129         ret = wine_server_call( req );
130     }
131     SERVER_END_REQ;
132     return ret;
133 }
134
135
136 /******************************************************************************
137  * NtDeleteValueKey [NTDLL.@]
138  * ZwDeleteValueKey [NTDLL.@]
139  */
140 NTSTATUS WINAPI NtDeleteValueKey( HANDLE hkey, const UNICODE_STRING *name )
141 {
142     NTSTATUS ret;
143
144     TRACE( "(0x%x,%s)\n", hkey, debugstr_us(name) );
145     if (name->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW;
146
147     SERVER_START_REQ( delete_key_value )
148     {
149         req->hkey = hkey;
150         wine_server_add_data( req, name->Buffer, name->Length );
151         ret = wine_server_call( req );
152     }
153     SERVER_END_REQ;
154     return ret;
155 }
156
157
158 /******************************************************************************
159  *     enumerate_key
160  *
161  * Implementation of NtQueryKey and NtEnumerateKey
162  */
163 static NTSTATUS enumerate_key( HANDLE handle, int index, KEY_INFORMATION_CLASS info_class,
164                                void *info, DWORD length, DWORD *result_len )
165
166 {
167     NTSTATUS ret;
168     void *data_ptr;
169     size_t fixed_size;
170
171     switch(info_class)
172     {
173     case KeyBasicInformation: data_ptr = ((KEY_BASIC_INFORMATION *)info)->Name; break;
174     case KeyFullInformation:  data_ptr = ((KEY_FULL_INFORMATION *)info)->Class; break;
175     case KeyNodeInformation:  data_ptr = ((KEY_NODE_INFORMATION *)info)->Name;  break;
176     default:
177         FIXME( "Information class %d not implemented\n", info_class );
178         return STATUS_INVALID_PARAMETER;
179     }
180     fixed_size = (char *)data_ptr - (char *)info;
181
182     SERVER_START_REQ( enum_key )
183     {
184         req->hkey       = handle;
185         req->index      = index;
186         req->info_class = info_class;
187         if (length > fixed_size) wine_server_set_reply( req, data_ptr, length - fixed_size );
188         if (!(ret = wine_server_call( req )))
189         {
190             LARGE_INTEGER modif;
191
192             RtlSecondsSince1970ToTime( reply->modif, (FILETIME *)&modif );
193
194             switch(info_class)
195             {
196             case KeyBasicInformation:
197                 {
198                     KEY_BASIC_INFORMATION keyinfo;
199                     fixed_size = (char *)keyinfo.Name - (char *)&keyinfo;
200                     keyinfo.LastWriteTime = modif;
201                     keyinfo.TitleIndex = 0;
202                     keyinfo.NameLength = reply->namelen;
203                     memcpy( info, &keyinfo, min( length, fixed_size ) );
204                 }
205                 break;
206             case KeyFullInformation:
207                 {
208                     KEY_FULL_INFORMATION keyinfo;
209                     fixed_size = (char *)keyinfo.Class - (char *)&keyinfo;
210                     keyinfo.LastWriteTime = modif;
211                     keyinfo.TitleIndex = 0;
212                     keyinfo.ClassLength = wine_server_reply_size(reply);
213                     keyinfo.ClassOffset = keyinfo.ClassLength ? fixed_size : -1;
214                     keyinfo.SubKeys = reply->subkeys;
215                     keyinfo.MaxNameLen = reply->max_subkey;
216                     keyinfo.MaxClassLen = reply->max_class;
217                     keyinfo.Values = reply->values;
218                     keyinfo.MaxValueNameLen = reply->max_value;
219                     keyinfo.MaxValueDataLen = reply->max_data;
220                     memcpy( info, &keyinfo, min( length, fixed_size ) );
221                 }
222                 break;
223             case KeyNodeInformation:
224                 {
225                     KEY_NODE_INFORMATION keyinfo;
226                     fixed_size = (char *)keyinfo.Name - (char *)&keyinfo;
227                     keyinfo.LastWriteTime = modif;
228                     keyinfo.TitleIndex = 0;
229                     keyinfo.ClassLength = max( 0, wine_server_reply_size(reply) - reply->namelen );
230                     keyinfo.ClassOffset = keyinfo.ClassLength ? fixed_size + reply->namelen : -1;
231                     keyinfo.NameLength = reply->namelen;
232                     memcpy( info, &keyinfo, min( length, fixed_size ) );
233                 }
234                 break;
235             }
236             *result_len = fixed_size + reply->total;
237             if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW;
238         }
239     }
240     SERVER_END_REQ;
241     return ret;
242 }
243
244
245
246 /******************************************************************************
247  * NtEnumerateKey [NTDLL.@]
248  * ZwEnumerateKey [NTDLL.@]
249  *
250  * NOTES
251  *  the name copied into the buffer is NOT 0-terminated
252  */
253 NTSTATUS WINAPI NtEnumerateKey( HANDLE handle, ULONG index, KEY_INFORMATION_CLASS info_class,
254                                 void *info, DWORD length, DWORD *result_len )
255 {
256     /* -1 means query key, so avoid it here */
257     if (index == (ULONG)-1) return STATUS_NO_MORE_ENTRIES;
258     return enumerate_key( handle, index, info_class, info, length, result_len );
259 }
260
261
262 /******************************************************************************
263  * NtQueryKey [NTDLL.@]
264  * ZwQueryKey [NTDLL.@]
265  */
266 NTSTATUS WINAPI NtQueryKey( HANDLE handle, KEY_INFORMATION_CLASS info_class,
267                             void *info, DWORD length, DWORD *result_len )
268 {
269     return enumerate_key( handle, -1, info_class, info, length, result_len );
270 }
271
272
273 /* fill the key value info structure for a specific info class */
274 static void copy_key_value_info( KEY_VALUE_INFORMATION_CLASS info_class, void *info,
275                                  DWORD length, int type, int name_len, int data_len )
276 {
277     switch(info_class)
278     {
279     case KeyValueBasicInformation:
280         {
281             KEY_VALUE_BASIC_INFORMATION keyinfo;
282             keyinfo.TitleIndex = 0;
283             keyinfo.Type       = type;
284             keyinfo.NameLength = name_len;
285             length = min( length, (char *)keyinfo.Name - (char *)&keyinfo );
286             memcpy( info, &keyinfo, length );
287             break;
288         }
289     case KeyValueFullInformation:
290         {
291             KEY_VALUE_FULL_INFORMATION keyinfo;
292             keyinfo.TitleIndex = 0;
293             keyinfo.Type       = type;
294             keyinfo.DataOffset = (char *)keyinfo.Name - (char *)&keyinfo + name_len;
295             keyinfo.DataLength = data_len;
296             keyinfo.NameLength = name_len;
297             length = min( length, (char *)keyinfo.Name - (char *)&keyinfo );
298             memcpy( info, &keyinfo, length );
299             break;
300         }
301     case KeyValuePartialInformation:
302         {
303             KEY_VALUE_PARTIAL_INFORMATION keyinfo;
304             keyinfo.TitleIndex = 0;
305             keyinfo.Type       = type;
306             keyinfo.DataLength = data_len;
307             length = min( length, (char *)keyinfo.Data - (char *)&keyinfo );
308             memcpy( info, &keyinfo, length );
309             break;
310         }
311     default:
312         break;
313     }
314 }
315
316
317 /******************************************************************************
318  *  NtEnumerateValueKey [NTDLL.@]
319  *  ZwEnumerateValueKey [NTDLL.@]
320  */
321 NTSTATUS WINAPI NtEnumerateValueKey( HANDLE handle, ULONG index,
322                                      KEY_VALUE_INFORMATION_CLASS info_class,
323                                      void *info, DWORD length, DWORD *result_len )
324 {
325     NTSTATUS ret;
326     void *ptr;
327     size_t fixed_size;
328
329     TRACE( "(0x%x,%lu,%d,%p,%ld)\n", handle, index, info_class, info, length );
330
331     /* compute the length we want to retrieve */
332     switch(info_class)
333     {
334     case KeyValueBasicInformation:   ptr = ((KEY_VALUE_BASIC_INFORMATION *)info)->Name; break;
335     case KeyValueFullInformation:    ptr = ((KEY_VALUE_FULL_INFORMATION *)info)->Name; break;
336     case KeyValuePartialInformation: ptr = ((KEY_VALUE_PARTIAL_INFORMATION *)info)->Data; break;
337     default:
338         FIXME( "Information class %d not implemented\n", info_class );
339         return STATUS_INVALID_PARAMETER;
340     }
341     fixed_size = (char *)ptr - (char *)info;
342
343     SERVER_START_REQ( enum_key_value )
344     {
345         req->hkey       = handle;
346         req->index      = index;
347         req->info_class = info_class;
348         if (length > fixed_size) wine_server_set_reply( req, ptr, length - fixed_size );
349         if (!(ret = wine_server_call( req )))
350         {
351             copy_key_value_info( info_class, info, length, reply->type, reply->namelen,
352                                  wine_server_reply_size(reply) - reply->namelen );
353             *result_len = fixed_size + reply->total;
354             if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW;
355         }
356     }
357     SERVER_END_REQ;
358     return ret;
359 }
360
361
362 /******************************************************************************
363  * NtQueryValueKey [NTDLL.@]
364  * ZwQueryValueKey [NTDLL.@]
365  *
366  * NOTES
367  *  the name in the KeyValueInformation is never set
368  */
369 NTSTATUS WINAPI NtQueryValueKey( HANDLE handle, const UNICODE_STRING *name,
370                                  KEY_VALUE_INFORMATION_CLASS info_class,
371                                  void *info, DWORD length, DWORD *result_len )
372 {
373     NTSTATUS ret;
374     UCHAR *data_ptr;
375     int fixed_size = 0;
376
377     TRACE( "(0x%x,%s,%d,%p,%ld)\n", handle, debugstr_us(name), info_class, info, length );
378
379     if (name->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW;
380
381     /* compute the length we want to retrieve */
382     switch(info_class)
383     {
384     case KeyValueBasicInformation:
385         fixed_size = (char *)((KEY_VALUE_BASIC_INFORMATION *)info)->Name - (char *)info;
386         data_ptr = NULL;
387         break;
388     case KeyValueFullInformation:
389         data_ptr = (UCHAR *)((KEY_VALUE_FULL_INFORMATION *)info)->Name;
390         fixed_size = (char *)data_ptr - (char *)info;
391         break;
392     case KeyValuePartialInformation:
393         data_ptr = ((KEY_VALUE_PARTIAL_INFORMATION *)info)->Data;
394         fixed_size = (char *)data_ptr - (char *)info;
395         break;
396     default:
397         FIXME( "Information class %d not implemented\n", info_class );
398         return STATUS_INVALID_PARAMETER;
399     }
400
401     SERVER_START_REQ( get_key_value )
402     {
403         req->hkey = handle;
404         wine_server_add_data( req, name->Buffer, name->Length );
405         if (length > fixed_size) wine_server_set_reply( req, data_ptr, length - fixed_size );
406         if (!(ret = wine_server_call( req )))
407         {
408             copy_key_value_info( info_class, info, length, reply->type,
409                                  0, wine_server_reply_size(reply) );
410             *result_len = fixed_size + reply->total;
411             if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW;
412         }
413     }
414     SERVER_END_REQ;
415     return ret;
416 }
417
418
419 /******************************************************************************
420  *  NtFlushKey  [NTDLL.@]
421  *  ZwFlushKey  [NTDLL.@]
422  */
423 NTSTATUS WINAPI NtFlushKey(HANDLE KeyHandle)
424 {
425         FIXME("(0x%08x) stub!\n",
426         KeyHandle);
427         return 1;
428 }
429
430 /******************************************************************************
431  *  NtLoadKey   [NTDLL.@]
432  *  ZwLoadKey   [NTDLL.@]
433  */
434 NTSTATUS WINAPI NtLoadKey( const OBJECT_ATTRIBUTES *attr, const OBJECT_ATTRIBUTES *file )
435 {
436     FIXME("stub!\n");
437     dump_ObjectAttributes(attr);
438     dump_ObjectAttributes(file);
439     return STATUS_SUCCESS;
440 }
441
442 /******************************************************************************
443  *  NtNotifyChangeKey   [NTDLL.@]
444  *  ZwNotifyChangeKey   [NTDLL.@]
445  */
446 NTSTATUS WINAPI NtNotifyChangeKey(
447         IN HANDLE KeyHandle,
448         IN HANDLE Event,
449         IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
450         IN PVOID ApcContext OPTIONAL,
451         OUT PIO_STATUS_BLOCK IoStatusBlock,
452         IN ULONG CompletionFilter,
453         IN BOOLEAN Asynchroneous,
454         OUT PVOID ChangeBuffer,
455         IN ULONG Length,
456         IN BOOLEAN WatchSubtree)
457 {
458         FIXME("(0x%08x,0x%08x,%p,%p,%p,0x%08lx, 0x%08x,%p,0x%08lx,0x%08x) stub!\n",
459         KeyHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, CompletionFilter,
460         Asynchroneous, ChangeBuffer, Length, WatchSubtree);
461         return STATUS_SUCCESS;
462 }
463
464 /******************************************************************************
465  * NtQueryMultipleValueKey [NTDLL]
466  * ZwQueryMultipleValueKey
467  */
468
469 NTSTATUS WINAPI NtQueryMultipleValueKey(
470         HANDLE KeyHandle,
471         PVALENTW ListOfValuesToQuery,
472         ULONG NumberOfItems,
473         PVOID MultipleValueInformation,
474         ULONG Length,
475         PULONG  ReturnLength)
476 {
477         FIXME("(0x%08x,%p,0x%08lx,%p,0x%08lx,%p) stub!\n",
478         KeyHandle, ListOfValuesToQuery, NumberOfItems, MultipleValueInformation,
479         Length,ReturnLength);
480         return STATUS_SUCCESS;
481 }
482
483 /******************************************************************************
484  * NtReplaceKey [NTDLL.@]
485  * ZwReplaceKey [NTDLL.@]
486  */
487 NTSTATUS WINAPI NtReplaceKey(
488         IN POBJECT_ATTRIBUTES ObjectAttributes,
489         IN HANDLE Key,
490         IN POBJECT_ATTRIBUTES ReplacedObjectAttributes)
491 {
492         FIXME("(0x%08x),stub!\n", Key);
493         dump_ObjectAttributes(ObjectAttributes);
494         dump_ObjectAttributes(ReplacedObjectAttributes);
495         return STATUS_SUCCESS;
496 }
497 /******************************************************************************
498  * NtRestoreKey [NTDLL.@]
499  * ZwRestoreKey [NTDLL.@]
500  */
501 NTSTATUS WINAPI NtRestoreKey(
502         HANDLE KeyHandle,
503         HANDLE FileHandle,
504         ULONG RestoreFlags)
505 {
506         FIXME("(0x%08x,0x%08x,0x%08lx) stub\n",
507         KeyHandle, FileHandle, RestoreFlags);
508         return STATUS_SUCCESS;
509 }
510 /******************************************************************************
511  * NtSaveKey [NTDLL.@]
512  * ZwSaveKey [NTDLL.@]
513  */
514 NTSTATUS WINAPI NtSaveKey(
515         IN HANDLE KeyHandle,
516         IN HANDLE FileHandle)
517 {
518         FIXME("(0x%08x,0x%08x) stub\n",
519         KeyHandle, FileHandle);
520         return STATUS_SUCCESS;
521 }
522 /******************************************************************************
523  * NtSetInformationKey [NTDLL.@]
524  * ZwSetInformationKey [NTDLL.@]
525  */
526 NTSTATUS WINAPI NtSetInformationKey(
527         IN HANDLE KeyHandle,
528         IN const int KeyInformationClass,
529         IN PVOID KeyInformation,
530         IN ULONG KeyInformationLength)
531 {
532         FIXME("(0x%08x,0x%08x,%p,0x%08lx) stub\n",
533         KeyHandle, KeyInformationClass, KeyInformation, KeyInformationLength);
534         return STATUS_SUCCESS;
535 }
536
537
538 /******************************************************************************
539  * NtSetValueKey [NTDLL.@]
540  * ZwSetValueKey [NTDLL.@]
541  *
542  * NOTES
543  *   win95 does not care about count for REG_SZ and finds out the len by itself (js)
544  *   NT does definitely care (aj)
545  */
546 NTSTATUS WINAPI NtSetValueKey( HANDLE hkey, const UNICODE_STRING *name, ULONG TitleIndex,
547                                ULONG type, const void *data, ULONG count )
548 {
549     NTSTATUS ret;
550
551     TRACE( "(0x%x,%s,%ld,%p,%ld)\n", hkey, debugstr_us(name), type, data, count );
552
553     if (name->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW;
554
555     SERVER_START_REQ( set_key_value )
556     {
557         req->hkey    = hkey;
558         req->type    = type;
559         req->namelen = name->Length;
560         wine_server_add_data( req, name->Buffer, name->Length );
561         wine_server_add_data( req, data, count );
562         ret = wine_server_call( req );
563     }
564     SERVER_END_REQ;
565     return ret;
566 }
567
568 /******************************************************************************
569  * NtUnloadKey [NTDLL.@]
570  * ZwUnloadKey [NTDLL.@]
571  */
572 NTSTATUS WINAPI NtUnloadKey(
573         IN HANDLE KeyHandle)
574 {
575         FIXME("(0x%08x) stub\n",
576         KeyHandle);
577         return STATUS_SUCCESS;
578 }
579
580 /******************************************************************************
581  *  RtlFormatCurrentUserKeyPath         [NTDLL.@]
582  */
583 NTSTATUS WINAPI RtlFormatCurrentUserKeyPath(
584         IN OUT PUNICODE_STRING KeyPath)
585 {
586 /*      LPSTR Path = "\\REGISTRY\\USER\\S-1-5-21-0000000000-000000000-0000000000-500";*/
587         LPSTR Path = "\\REGISTRY\\USER\\.DEFAULT";
588         ANSI_STRING AnsiPath;
589
590         FIXME("(%p) stub\n",KeyPath);
591         RtlInitAnsiString(&AnsiPath, Path);
592         return RtlAnsiStringToUnicodeString(KeyPath, &AnsiPath, TRUE);
593 }
594
595 /******************************************************************************
596  *  RtlOpenCurrentUser          [NTDLL.@]
597  *
598  * if we return just HKEY_CURRENT_USER the advapi tries to find a remote
599  * registry (odd handle) and fails
600  *
601  */
602 DWORD WINAPI RtlOpenCurrentUser(
603         IN ACCESS_MASK DesiredAccess, /* [in] */
604         OUT PHANDLE KeyHandle)        /* [out] handle of HKEY_CURRENT_USER */
605 {
606         OBJECT_ATTRIBUTES ObjectAttributes;
607         UNICODE_STRING ObjectName;
608         NTSTATUS ret;
609
610         TRACE("(0x%08lx, %p) stub\n",DesiredAccess, KeyHandle);
611
612         RtlFormatCurrentUserKeyPath(&ObjectName);
613         InitializeObjectAttributes(&ObjectAttributes,&ObjectName,OBJ_CASE_INSENSITIVE,0, NULL);
614         ret = NtOpenKey(KeyHandle, DesiredAccess, &ObjectAttributes);
615         RtlFreeUnicodeString(&ObjectName);
616         return ret;
617 }