/* * Registry functions * * Copyright (C) 1999 Juergen Schmied * Copyright (C) 2000 Alexandre Julliard * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * NOTES: * HKEY_LOCAL_MACHINE \\REGISTRY\\MACHINE * HKEY_USERS \\REGISTRY\\USER * HKEY_CURRENT_CONFIG \\REGISTRY\\MACHINE\\SYSTEM\\CURRENTCONTROLSET\\HARDWARE PROFILES\\CURRENT * HKEY_CLASSES \\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES */ #include "config.h" #include "wine/port.h" #include #include #include #include "winerror.h" #include "wine/unicode.h" #include "wine/library.h" #include "wine/server.h" #include "windef.h" #include "winbase.h" #include "winreg.h" #include "winternl.h" #include "ntdll_misc.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(reg); /* maximum length of a key/value name in bytes (without terminating null) */ #define MAX_NAME_LENGTH ((MAX_PATH-1) * sizeof(WCHAR)) /****************************************************************************** * NtCreateKey [NTDLL.@] * ZwCreateKey [NTDLL.@] */ NTSTATUS WINAPI NtCreateKey( PHKEY retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr, ULONG TitleIndex, const UNICODE_STRING *class, ULONG options, PULONG dispos ) { NTSTATUS ret; TRACE( "(%p,%s,%s,%lx,%lx,%p)\n", attr->RootDirectory, debugstr_us(attr->ObjectName), debugstr_us(class), options, access, retkey ); if (attr->ObjectName->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW; if (!retkey) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( create_key ) { req->parent = attr->RootDirectory; req->access = access; req->options = options; req->modif = 0; req->namelen = attr->ObjectName->Length; wine_server_add_data( req, attr->ObjectName->Buffer, attr->ObjectName->Length ); if (class) wine_server_add_data( req, class->Buffer, class->Length ); if (!(ret = wine_server_call( req ))) { *retkey = reply->hkey; if (dispos) *dispos = reply->created ? REG_CREATED_NEW_KEY : REG_OPENED_EXISTING_KEY; } } SERVER_END_REQ; TRACE("<- %p\n", *retkey); return ret; } /****************************************************************************** * RtlpNtCreateKey [NTDLL.@] * * See NtCreateKey. */ NTSTATUS WINAPI RtlpNtCreateKey( PHKEY retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr, ULONG TitleIndex, const UNICODE_STRING *class, ULONG options, PULONG dispos ) { if (attr) attr->Attributes &= ~(OBJ_PERMANENT|OBJ_EXCLUSIVE); return NtCreateKey(retkey, access, attr, 0, NULL, 0, dispos); } /****************************************************************************** * NtOpenKey [NTDLL.@] * ZwOpenKey [NTDLL.@] * * OUT PHKEY retkey (returns 0 when failure) * IN ACCESS_MASK access * IN POBJECT_ATTRIBUTES attr */ NTSTATUS WINAPI NtOpenKey( PHKEY retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr ) { NTSTATUS ret; DWORD len = attr->ObjectName->Length; TRACE( "(%p,%s,%lx,%p)\n", attr->RootDirectory, debugstr_us(attr->ObjectName), access, retkey ); if (len > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW; if (!retkey) return STATUS_INVALID_PARAMETER; SERVER_START_REQ( open_key ) { req->parent = attr->RootDirectory; req->access = access; wine_server_add_data( req, attr->ObjectName->Buffer, len ); ret = wine_server_call( req ); *retkey = reply->hkey; } SERVER_END_REQ; TRACE("<- %p\n", *retkey); return ret; } /****************************************************************************** * RtlpNtOpenKey [NTDLL.@] * * See NtOpenKey. */ NTSTATUS WINAPI RtlpNtOpenKey( PHKEY retkey, ACCESS_MASK access, OBJECT_ATTRIBUTES *attr ) { if (attr) attr->Attributes &= ~(OBJ_PERMANENT|OBJ_EXCLUSIVE); return NtOpenKey(retkey, access, attr); } /****************************************************************************** * NtDeleteKey [NTDLL.@] * ZwDeleteKey [NTDLL.@] */ NTSTATUS WINAPI NtDeleteKey( HKEY hkey ) { NTSTATUS ret; TRACE( "(%p)\n", hkey ); SERVER_START_REQ( delete_key ) { req->hkey = hkey; ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * RtlpNtMakeTemporaryKey [NTDLL.@] * * See NtDeleteKey. */ NTSTATUS WINAPI RtlpNtMakeTemporaryKey( HKEY hkey ) { return NtDeleteKey(hkey); } /****************************************************************************** * NtDeleteValueKey [NTDLL.@] * ZwDeleteValueKey [NTDLL.@] */ NTSTATUS WINAPI NtDeleteValueKey( HKEY hkey, const UNICODE_STRING *name ) { NTSTATUS ret; TRACE( "(%p,%s)\n", hkey, debugstr_us(name) ); if (name->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW; SERVER_START_REQ( delete_key_value ) { req->hkey = hkey; wine_server_add_data( req, name->Buffer, name->Length ); ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * enumerate_key * * Implementation of NtQueryKey and NtEnumerateKey */ static NTSTATUS enumerate_key( HKEY handle, int index, KEY_INFORMATION_CLASS info_class, void *info, DWORD length, DWORD *result_len ) { NTSTATUS ret; void *data_ptr; size_t fixed_size; switch(info_class) { case KeyBasicInformation: data_ptr = ((KEY_BASIC_INFORMATION *)info)->Name; break; case KeyFullInformation: data_ptr = ((KEY_FULL_INFORMATION *)info)->Class; break; case KeyNodeInformation: data_ptr = ((KEY_NODE_INFORMATION *)info)->Name; break; default: FIXME( "Information class %d not implemented\n", info_class ); return STATUS_INVALID_PARAMETER; } fixed_size = (char *)data_ptr - (char *)info; SERVER_START_REQ( enum_key ) { req->hkey = handle; req->index = index; req->info_class = info_class; if (length > fixed_size) wine_server_set_reply( req, data_ptr, length - fixed_size ); if (!(ret = wine_server_call( req ))) { LARGE_INTEGER modif; RtlSecondsSince1970ToTime( reply->modif, &modif ); switch(info_class) { case KeyBasicInformation: { KEY_BASIC_INFORMATION keyinfo; fixed_size = (char *)keyinfo.Name - (char *)&keyinfo; keyinfo.LastWriteTime = modif; keyinfo.TitleIndex = 0; keyinfo.NameLength = reply->namelen; memcpy( info, &keyinfo, min( length, fixed_size ) ); } break; case KeyFullInformation: { KEY_FULL_INFORMATION keyinfo; fixed_size = (char *)keyinfo.Class - (char *)&keyinfo; keyinfo.LastWriteTime = modif; keyinfo.TitleIndex = 0; keyinfo.ClassLength = wine_server_reply_size(reply); keyinfo.ClassOffset = keyinfo.ClassLength ? fixed_size : -1; keyinfo.SubKeys = reply->subkeys; keyinfo.MaxNameLen = reply->max_subkey; keyinfo.MaxClassLen = reply->max_class; keyinfo.Values = reply->values; keyinfo.MaxValueNameLen = reply->max_value; keyinfo.MaxValueDataLen = reply->max_data; memcpy( info, &keyinfo, min( length, fixed_size ) ); } break; case KeyNodeInformation: { KEY_NODE_INFORMATION keyinfo; fixed_size = (char *)keyinfo.Name - (char *)&keyinfo; keyinfo.LastWriteTime = modif; keyinfo.TitleIndex = 0; keyinfo.ClassLength = max( 0, wine_server_reply_size(reply) - reply->namelen ); keyinfo.ClassOffset = keyinfo.ClassLength ? fixed_size + reply->namelen : -1; keyinfo.NameLength = reply->namelen; memcpy( info, &keyinfo, min( length, fixed_size ) ); } break; } *result_len = fixed_size + reply->total; if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW; } } SERVER_END_REQ; return ret; } /****************************************************************************** * NtEnumerateKey [NTDLL.@] * ZwEnumerateKey [NTDLL.@] * * NOTES * the name copied into the buffer is NOT 0-terminated */ NTSTATUS WINAPI NtEnumerateKey( HKEY handle, ULONG index, KEY_INFORMATION_CLASS info_class, void *info, DWORD length, DWORD *result_len ) { /* -1 means query key, so avoid it here */ if (index == (ULONG)-1) return STATUS_NO_MORE_ENTRIES; return enumerate_key( handle, index, info_class, info, length, result_len ); } /****************************************************************************** * RtlpNtEnumerateSubKey [NTDLL.@] * */ NTSTATUS WINAPI RtlpNtEnumerateSubKey( HKEY handle, UNICODE_STRING *out, ULONG index ) { KEY_BASIC_INFORMATION *info; DWORD dwLen, dwResultLen; NTSTATUS ret; if (out->Length) { dwLen = out->Length + sizeof(KEY_BASIC_INFORMATION); info = (KEY_BASIC_INFORMATION*)RtlAllocateHeap( GetProcessHeap(), 0, dwLen ); if (!info) return STATUS_NO_MEMORY; } else { dwLen = 0; info = NULL; } ret = NtEnumerateKey( handle, index, KeyBasicInformation, info, dwLen, &dwResultLen ); dwResultLen -= sizeof(KEY_BASIC_INFORMATION); if (ret == STATUS_BUFFER_OVERFLOW) out->Length = dwResultLen; else if (!ret) { if (out->Length < info->NameLength) { out->Length = dwResultLen; ret = STATUS_BUFFER_OVERFLOW; } else { out->Length = info->NameLength; memcpy(out->Buffer, info->Name, info->NameLength); } } if (info) RtlFreeHeap( GetProcessHeap(), 0, info ); return ret; } /****************************************************************************** * NtQueryKey [NTDLL.@] * ZwQueryKey [NTDLL.@] */ NTSTATUS WINAPI NtQueryKey( HKEY handle, KEY_INFORMATION_CLASS info_class, void *info, DWORD length, DWORD *result_len ) { return enumerate_key( handle, -1, info_class, info, length, result_len ); } /* fill the key value info structure for a specific info class */ static void copy_key_value_info( KEY_VALUE_INFORMATION_CLASS info_class, void *info, DWORD length, int type, int name_len, int data_len ) { switch(info_class) { case KeyValueBasicInformation: { KEY_VALUE_BASIC_INFORMATION keyinfo; keyinfo.TitleIndex = 0; keyinfo.Type = type; keyinfo.NameLength = name_len; length = min( length, (char *)keyinfo.Name - (char *)&keyinfo ); memcpy( info, &keyinfo, length ); break; } case KeyValueFullInformation: { KEY_VALUE_FULL_INFORMATION keyinfo; keyinfo.TitleIndex = 0; keyinfo.Type = type; keyinfo.DataOffset = (char *)keyinfo.Name - (char *)&keyinfo + name_len; keyinfo.DataLength = data_len; keyinfo.NameLength = name_len; length = min( length, (char *)keyinfo.Name - (char *)&keyinfo ); memcpy( info, &keyinfo, length ); break; } case KeyValuePartialInformation: { KEY_VALUE_PARTIAL_INFORMATION keyinfo; keyinfo.TitleIndex = 0; keyinfo.Type = type; keyinfo.DataLength = data_len; length = min( length, (char *)keyinfo.Data - (char *)&keyinfo ); memcpy( info, &keyinfo, length ); break; } default: break; } } /****************************************************************************** * NtEnumerateValueKey [NTDLL.@] * ZwEnumerateValueKey [NTDLL.@] */ NTSTATUS WINAPI NtEnumerateValueKey( HKEY handle, ULONG index, KEY_VALUE_INFORMATION_CLASS info_class, void *info, DWORD length, DWORD *result_len ) { NTSTATUS ret; void *ptr; size_t fixed_size; TRACE( "(%p,%lu,%d,%p,%ld)\n", handle, index, info_class, info, length ); /* compute the length we want to retrieve */ switch(info_class) { case KeyValueBasicInformation: ptr = ((KEY_VALUE_BASIC_INFORMATION *)info)->Name; break; case KeyValueFullInformation: ptr = ((KEY_VALUE_FULL_INFORMATION *)info)->Name; break; case KeyValuePartialInformation: ptr = ((KEY_VALUE_PARTIAL_INFORMATION *)info)->Data; break; default: FIXME( "Information class %d not implemented\n", info_class ); return STATUS_INVALID_PARAMETER; } fixed_size = (char *)ptr - (char *)info; SERVER_START_REQ( enum_key_value ) { req->hkey = handle; req->index = index; req->info_class = info_class; if (length > fixed_size) wine_server_set_reply( req, ptr, length - fixed_size ); if (!(ret = wine_server_call( req ))) { copy_key_value_info( info_class, info, length, reply->type, reply->namelen, wine_server_reply_size(reply) - reply->namelen ); *result_len = fixed_size + reply->total; if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW; } } SERVER_END_REQ; return ret; } /****************************************************************************** * NtQueryValueKey [NTDLL.@] * ZwQueryValueKey [NTDLL.@] * * NOTES * the name in the KeyValueInformation is never set */ NTSTATUS WINAPI NtQueryValueKey( HKEY handle, const UNICODE_STRING *name, KEY_VALUE_INFORMATION_CLASS info_class, void *info, DWORD length, DWORD *result_len ) { NTSTATUS ret; UCHAR *data_ptr; unsigned int fixed_size = 0; TRACE( "(%p,%s,%d,%p,%ld)\n", handle, debugstr_us(name), info_class, info, length ); if (name->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW; /* compute the length we want to retrieve */ switch(info_class) { case KeyValueBasicInformation: fixed_size = (char *)((KEY_VALUE_BASIC_INFORMATION *)info)->Name - (char *)info; data_ptr = NULL; break; case KeyValueFullInformation: data_ptr = (UCHAR *)((KEY_VALUE_FULL_INFORMATION *)info)->Name; fixed_size = (char *)data_ptr - (char *)info; break; case KeyValuePartialInformation: data_ptr = ((KEY_VALUE_PARTIAL_INFORMATION *)info)->Data; fixed_size = (char *)data_ptr - (char *)info; break; default: FIXME( "Information class %d not implemented\n", info_class ); return STATUS_INVALID_PARAMETER; } SERVER_START_REQ( get_key_value ) { req->hkey = handle; wine_server_add_data( req, name->Buffer, name->Length ); if (length > fixed_size) wine_server_set_reply( req, data_ptr, length - fixed_size ); if (!(ret = wine_server_call( req ))) { copy_key_value_info( info_class, info, length, reply->type, 0, wine_server_reply_size(reply) ); *result_len = fixed_size + reply->total; if (length < *result_len) ret = STATUS_BUFFER_OVERFLOW; } } SERVER_END_REQ; return ret; } /****************************************************************************** * RtlpNtQueryValueKey [NTDLL.@] * */ NTSTATUS WINAPI RtlpNtQueryValueKey( HKEY handle, ULONG *result_type, PBYTE dest, DWORD *result_len ) { KEY_VALUE_PARTIAL_INFORMATION *info; UNICODE_STRING name; NTSTATUS ret; DWORD dwResultLen; DWORD dwLen = sizeof (KEY_VALUE_PARTIAL_INFORMATION) + result_len ? *result_len : 0; info = (KEY_VALUE_PARTIAL_INFORMATION*)RtlAllocateHeap( GetProcessHeap(), 0, dwLen ); if (!info) return STATUS_NO_MEMORY; name.Length = 0; ret = NtQueryValueKey( handle, &name, KeyValuePartialInformation, info, dwLen, &dwResultLen ); if (!ret || ret == STATUS_BUFFER_OVERFLOW) { if (result_len) *result_len = info->DataLength; if (result_type) *result_type = info->Type; if (ret != STATUS_BUFFER_OVERFLOW) memcpy( dest, info->Data, info->DataLength ); } RtlFreeHeap( GetProcessHeap(), 0, info ); return ret; } /****************************************************************************** * NtFlushKey [NTDLL.@] * ZwFlushKey [NTDLL.@] */ NTSTATUS WINAPI NtFlushKey(HKEY key) { NTSTATUS ret; TRACE("key=%p\n", key); SERVER_START_REQ( flush_key ) { req->hkey = key; ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * NtLoadKey [NTDLL.@] * ZwLoadKey [NTDLL.@] */ NTSTATUS WINAPI NtLoadKey( const OBJECT_ATTRIBUTES *attr, const OBJECT_ATTRIBUTES *file ) { FIXME("stub!\n"); dump_ObjectAttributes(attr); dump_ObjectAttributes(file); return STATUS_SUCCESS; } /****************************************************************************** * NtNotifyChangeKey [NTDLL.@] * ZwNotifyChangeKey [NTDLL.@] */ NTSTATUS WINAPI NtNotifyChangeKey( IN HKEY KeyHandle, IN HANDLE Event, IN PIO_APC_ROUTINE ApcRoutine OPTIONAL, IN PVOID ApcContext OPTIONAL, OUT PIO_STATUS_BLOCK IoStatusBlock, IN ULONG CompletionFilter, IN BOOLEAN Asynchroneous, OUT PVOID ChangeBuffer, IN ULONG Length, IN BOOLEAN WatchSubtree) { FIXME("(%p,%p,%p,%p,%p,0x%08lx, 0x%08x,%p,0x%08lx,0x%08x) stub!\n", KeyHandle, Event, ApcRoutine, ApcContext, IoStatusBlock, CompletionFilter, Asynchroneous, ChangeBuffer, Length, WatchSubtree); return STATUS_SUCCESS; } /****************************************************************************** * NtQueryMultipleValueKey [NTDLL] * ZwQueryMultipleValueKey */ NTSTATUS WINAPI NtQueryMultipleValueKey( HKEY KeyHandle, PVALENTW ListOfValuesToQuery, ULONG NumberOfItems, PVOID MultipleValueInformation, ULONG Length, PULONG ReturnLength) { FIXME("(%p,%p,0x%08lx,%p,0x%08lx,%p) stub!\n", KeyHandle, ListOfValuesToQuery, NumberOfItems, MultipleValueInformation, Length,ReturnLength); return STATUS_SUCCESS; } /****************************************************************************** * NtReplaceKey [NTDLL.@] * ZwReplaceKey [NTDLL.@] */ NTSTATUS WINAPI NtReplaceKey( IN POBJECT_ATTRIBUTES ObjectAttributes, IN HKEY Key, IN POBJECT_ATTRIBUTES ReplacedObjectAttributes) { FIXME("(%p),stub!\n", Key); dump_ObjectAttributes(ObjectAttributes); dump_ObjectAttributes(ReplacedObjectAttributes); return STATUS_SUCCESS; } /****************************************************************************** * NtRestoreKey [NTDLL.@] * ZwRestoreKey [NTDLL.@] */ NTSTATUS WINAPI NtRestoreKey( HKEY KeyHandle, HANDLE FileHandle, ULONG RestoreFlags) { FIXME("(%p,%p,0x%08lx) stub\n", KeyHandle, FileHandle, RestoreFlags); return STATUS_SUCCESS; } /****************************************************************************** * NtSaveKey [NTDLL.@] * ZwSaveKey [NTDLL.@] */ NTSTATUS WINAPI NtSaveKey( IN HKEY KeyHandle, IN HANDLE FileHandle) { FIXME("(%p,%p) stub\n", KeyHandle, FileHandle); return STATUS_SUCCESS; } /****************************************************************************** * NtSetInformationKey [NTDLL.@] * ZwSetInformationKey [NTDLL.@] */ NTSTATUS WINAPI NtSetInformationKey( IN HKEY KeyHandle, IN const int KeyInformationClass, IN PVOID KeyInformation, IN ULONG KeyInformationLength) { FIXME("(%p,0x%08x,%p,0x%08lx) stub\n", KeyHandle, KeyInformationClass, KeyInformation, KeyInformationLength); return STATUS_SUCCESS; } /****************************************************************************** * NtSetValueKey [NTDLL.@] * ZwSetValueKey [NTDLL.@] * * NOTES * win95 does not care about count for REG_SZ and finds out the len by itself (js) * NT does definitely care (aj) */ NTSTATUS WINAPI NtSetValueKey( HKEY hkey, const UNICODE_STRING *name, ULONG TitleIndex, ULONG type, const void *data, ULONG count ) { NTSTATUS ret; TRACE( "(%p,%s,%ld,%p,%ld)\n", hkey, debugstr_us(name), type, data, count ); if (name->Length > MAX_NAME_LENGTH) return STATUS_BUFFER_OVERFLOW; SERVER_START_REQ( set_key_value ) { req->hkey = hkey; req->type = type; req->namelen = name->Length; wine_server_add_data( req, name->Buffer, name->Length ); wine_server_add_data( req, data, count ); ret = wine_server_call( req ); } SERVER_END_REQ; return ret; } /****************************************************************************** * RtlpNtSetValueKey [NTDLL.@] * */ NTSTATUS WINAPI RtlpNtSetValueKey( HKEY hkey, ULONG type, const void *data, ULONG count ) { UNICODE_STRING name; name.Length = 0; return NtSetValueKey( hkey, &name, 0, type, data, count ); } /****************************************************************************** * NtUnloadKey [NTDLL.@] * ZwUnloadKey [NTDLL.@] */ NTSTATUS WINAPI NtUnloadKey( IN HKEY KeyHandle) { FIXME("(%p) stub\n", KeyHandle); return STATUS_SUCCESS; } /****************************************************************************** * RtlFormatCurrentUserKeyPath [NTDLL.@] * * NOTE: under NT the user name part of the path is an SID. */ NTSTATUS WINAPI RtlFormatCurrentUserKeyPath( IN OUT PUNICODE_STRING KeyPath) { static const WCHAR pathW[] = {'\\','R','e','g','i','s','t','r','y','\\','U','s','e','r','\\'}; const char *user = wine_get_user_name(); int len = ntdll_umbstowcs( 0, user, strlen(user)+1, NULL, 0 ); KeyPath->MaximumLength = sizeof(pathW) + len * sizeof(WCHAR); KeyPath->Length = KeyPath->MaximumLength - sizeof(WCHAR); if (!(KeyPath->Buffer = RtlAllocateHeap( GetProcessHeap(), 0, KeyPath->MaximumLength ))) return STATUS_NO_MEMORY; memcpy( KeyPath->Buffer, pathW, sizeof(pathW) ); ntdll_umbstowcs( 0, user, strlen(user)+1, KeyPath->Buffer + sizeof(pathW)/sizeof(WCHAR), len ); return STATUS_SUCCESS; } /****************************************************************************** * RtlOpenCurrentUser [NTDLL.@] * * if we return just HKEY_CURRENT_USER the advapi tries to find a remote * registry (odd handle) and fails * */ DWORD WINAPI RtlOpenCurrentUser( IN ACCESS_MASK DesiredAccess, /* [in] */ OUT PHKEY KeyHandle) /* [out] handle of HKEY_CURRENT_USER */ { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING ObjectName; NTSTATUS ret; TRACE("(0x%08lx, %p) stub\n",DesiredAccess, KeyHandle); RtlFormatCurrentUserKeyPath(&ObjectName); InitializeObjectAttributes(&ObjectAttributes,&ObjectName,OBJ_CASE_INSENSITIVE,0, NULL); ret = NtCreateKey(KeyHandle, DesiredAccess, &ObjectAttributes, 0, NULL, 0, NULL); RtlFreeUnicodeString(&ObjectName); return ret; }