[ADVAPI32_WINETEST] Sync with Wine Staging 1.7.55. CORE-10536
[reactos.git] / rostests / winetests / advapi32 / registry.c
index 6ec84ee..7d16cb3 100644 (file)
@@ -28,7 +28,7 @@
 #include "wine/test.h"
 #include "windef.h"
 #include "winbase.h"
-#include "winternl.h"
+#include "wine/winternl.h"
 #include "winreg.h"
 #include "winsvc.h"
 #include "winerror.h"
@@ -50,6 +50,10 @@ static BOOL (WINAPI *pIsWow64Process)(HANDLE,PBOOL);
 static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE);
 static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(UNICODE_STRING*);
 static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
+static LONG (WINAPI *pRegDeleteKeyValueA)(HKEY,LPCSTR,LPCSTR);
+static LONG (WINAPI *pRegSetKeyValueW)(HKEY,LPCWSTR,LPCWSTR,DWORD,const void*,DWORD);
+
+static BOOL limited_user;
 
 
 /* Debugging functions from wine/libs/wine/debug.c */
@@ -136,6 +140,8 @@ static void InitFunctionPtrs(void)
     ADVAPI32_GET_PROC(RegGetValueA);
     ADVAPI32_GET_PROC(RegDeleteTreeA);
     ADVAPI32_GET_PROC(RegDeleteKeyExA);
+    ADVAPI32_GET_PROC(RegDeleteKeyValueA);
+    ADVAPI32_GET_PROC(RegSetKeyValueW);
 
     pIsWow64Process = (void *)GetProcAddress( hkernel32, "IsWow64Process" );
     pRtlFormatCurrentUserKeyPath = (void *)GetProcAddress( hntdll, "RtlFormatCurrentUserKeyPath" );
@@ -173,6 +179,26 @@ static void setup_main_key(void)
     assert (!RegCreateKeyA( HKEY_CURRENT_USER, "Software\\Wine\\Test", &hkey_main ));
 }
 
+static void check_user_privs(void)
+{
+    DWORD ret;
+    HKEY hkey = (HKEY)0xdeadbeef;
+
+    ret = RegOpenKeyExA( HKEY_LOCAL_MACHINE, "Software", 0, KEY_READ|KEY_WRITE, &hkey);
+    ok(ret == ERROR_SUCCESS || ret == ERROR_ACCESS_DENIED, "expected success or access denied, got %i\n", ret);
+    if (ret == ERROR_SUCCESS)
+    {
+        ok(hkey != NULL, "RegOpenKeyExA succeeded but returned NULL hkey\n");
+        RegCloseKey(hkey);
+    }
+    else
+    {
+        ok(hkey == NULL, "RegOpenKeyExA failed but returned hkey %p\n", hkey);
+        limited_user = TRUE;
+        trace("running as limited user\n");
+    }
+}
+
 #define lok ok_(__FILE__, line)
 #define test_hkey_main_Value_A(name, string, full_byte_len) _test_hkey_main_Value_A(__LINE__, name, string, full_byte_len)
 static void _test_hkey_main_Value_A(int line, LPCSTR name, LPCSTR string,
@@ -412,6 +438,58 @@ static void test_set_value(void)
     ok(ret == ERROR_NOACCESS, "RegSetValueExW should have failed with ERROR_NOACCESS: %d, GLE=%d\n", ret, GetLastError());
     ret = RegSetValueExW(hkey_main, name2W, 0, REG_DWORD, (const BYTE *)1, 1);
     ok(ret == ERROR_NOACCESS, "RegSetValueExW should have failed with ERROR_NOACCESS: %d, GLE=%d\n", ret, GetLastError());
+
+    if (pRegGetValueA) /* avoid a crash on Windows 2000 */
+    {
+        ret = RegSetValueExW(hkey_main, NULL, 0, REG_SZ, NULL, 4);
+        ok(ret == ERROR_NOACCESS, "RegSetValueExW should have failed with ERROR_NOACCESS: %d, GLE=%d\n", ret, GetLastError());
+
+        ret = RegSetValueExW(hkey_main, NULL, 0, REG_SZ, NULL, 0);
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+        ret = RegSetValueExW(hkey_main, NULL, 0, REG_DWORD, NULL, 4);
+        ok(ret == ERROR_NOACCESS, "RegSetValueExW should have failed with ERROR_NOACCESS: %d, GLE=%d\n", ret, GetLastError());
+
+        ret = RegSetValueExW(hkey_main, NULL, 0, REG_DWORD, NULL, 0);
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+    }
+
+    /* RegSetKeyValue */
+    if (!pRegSetKeyValueW)
+        win_skip("RegSetKeyValue() is not supported.\n");
+    else
+    {
+        static const WCHAR subkeyW[] = {'s','u','b','k','e','y',0};
+        DWORD len, type;
+        HKEY subkey;
+
+        ret = pRegSetKeyValueW(hkey_main, NULL, name1W, REG_SZ, (const BYTE*)string2W, sizeof(string2W));
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+        test_hkey_main_Value_A(name1A, string2A, sizeof(string2A));
+        test_hkey_main_Value_W(name1W, string2W, sizeof(string2W));
+
+        ret = pRegSetKeyValueW(hkey_main, subkeyW, name1W, REG_SZ, string1W, sizeof(string1W));
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+        ret = RegOpenKeyExW(hkey_main, subkeyW, 0, KEY_QUERY_VALUE, &subkey);
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+        type = len = 0;
+        ret = RegQueryValueExW(subkey, name1W, 0, &type, NULL, &len);
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+        ok(len == sizeof(string1W), "got %d\n", len);
+        ok(type == REG_SZ, "got type %d\n", type);
+
+        ret = pRegSetKeyValueW(hkey_main, subkeyW, name1W, REG_SZ, NULL, 0);
+        ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+        ret = pRegSetKeyValueW(hkey_main, subkeyW, name1W, REG_SZ, NULL, 4);
+        ok(ret == ERROR_NOACCESS, "got %d\n", ret);
+
+        ret = pRegSetKeyValueW(hkey_main, subkeyW, name1W, REG_DWORD, NULL, 4);
+        ok(ret == ERROR_NOACCESS, "got %d\n", ret);
+
+        RegCloseKey(subkey);
+    }
 }
 
 static void create_test_entries(void)
@@ -463,6 +541,33 @@ static void test_enum_value(void)
     res = RegSetValueExA( test_key, "Test", 0, REG_BINARY, NULL, 0 );
     ok( ERROR_SUCCESS == res || ERROR_INVALID_PARAMETER == res, "RegSetValueExA returned %d\n", res );
 
+    /* test reading the value and data without setting them */
+    val_count = 20;
+    data_count = 20;
+    type = 1234;
+    strcpy( value, "xxxxxxxxxx" );
+    strcpy( data, "xxxxxxxxxx" );
+    res = RegEnumValueA( test_key, 0, value, &val_count, NULL, &type, (LPBYTE)data, &data_count );
+    ok( res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", res );
+    ok( val_count == 4, "val_count set to %d instead of 4\n", val_count );
+    ok( data_count == 0, "data_count set to %d instead of 0\n", data_count );
+    ok( type == REG_BINARY, "type %d is not REG_BINARY\n", type );
+    ok( !strcmp( value, "Test" ), "value is '%s' instead of Test\n", value );
+    ok( !strcmp( data, "xxxxxxxxxx" ), "data is '%s' instead of xxxxxxxxxx\n", data );
+
+    val_count = 20;
+    data_count = 20;
+    type = 1234;
+    memcpy( valueW, xxxW, sizeof(xxxW) );
+    memcpy( dataW, xxxW, sizeof(xxxW) );
+    res = RegEnumValueW( test_key, 0, valueW, &val_count, NULL, &type, (BYTE*)dataW, &data_count );
+    ok( res == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", res );
+    ok( val_count == 4, "val_count set to %d instead of 4\n", val_count );
+    ok( data_count == 0, "data_count set to %d instead of 0\n", data_count );
+    ok( type == REG_BINARY, "type %d is not REG_BINARY\n", type );
+    ok( !memcmp( valueW, testW, sizeof(testW) ), "value is not 'Test'\n" );
+    ok( !memcmp( dataW, xxxW, sizeof(xxxW) ), "data is not 'xxxxxxxxxx'\n" );
+
     res = RegSetValueExA( test_key, "Test", 0, REG_SZ, (const BYTE *)"foobar", 7 );
     ok( res == 0, "RegSetValueExA failed error %d\n", res );
 
@@ -541,6 +646,32 @@ static void test_enum_value(void)
     ok( !strcmp( value, "Test" ), "value is '%s' instead of Test\n", value );
     ok( !strcmp( data, "foobar" ), "data is '%s' instead of foobar\n", data );
 
+    if (pRegGetValueA) /* avoid a crash on Windows 2000 */
+    {
+        /* no value and no val_count parameter */
+        data_count = 20;
+        type = 1234;
+        strcpy( data, "xxxxxxxxxx" );
+        res = RegEnumValueA( test_key, 0, NULL, NULL, NULL, &type, (BYTE*)data, &data_count );
+        ok( res == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", res );
+
+        /* no value parameter */
+        val_count = 20;
+        data_count = 20;
+        type = 1234;
+        strcpy( data, "xxxxxxxxxx" );
+        res = RegEnumValueA( test_key, 0, NULL, &val_count, NULL, &type, (BYTE*)data, &data_count );
+        ok( res == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", res );
+
+        /* no val_count parameter */
+        data_count = 20;
+        type = 1234;
+        strcpy( value, "xxxxxxxxxx" );
+        strcpy( data, "xxxxxxxxxx" );
+        res = RegEnumValueA( test_key, 0, value, NULL, NULL, &type, (BYTE*)data, &data_count );
+        ok( res == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", res );
+    }
+
     /* Unicode tests */
 
     SetLastError(0xdeadbeef);
@@ -608,6 +739,32 @@ static void test_enum_value(void)
     ok( !memcmp( valueW, testW, sizeof(testW) ), "value is not 'Test'\n" );
     ok( !memcmp( dataW, foobarW, sizeof(foobarW) ), "data is not 'foobar'\n" );
 
+    if (pRegGetValueA) /* avoid a crash on Windows 2000 */
+    {
+        /* no valueW and no val_count parameter */
+        data_count = 20;
+        type = 1234;
+        memcpy( dataW, xxxW, sizeof(xxxW) );
+        res = RegEnumValueW( test_key, 0, NULL, NULL, NULL, &type, (BYTE*)dataW, &data_count );
+        ok( res == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", res );
+
+        /* no valueW parameter */
+        val_count = 20;
+        data_count = 20;
+        type = 1234;
+        memcpy( dataW, xxxW, sizeof(xxxW) );
+        res = RegEnumValueW( test_key, 0, NULL, &val_count, NULL, &type, (BYTE*)dataW, &data_count );
+        ok( res == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", res );
+
+        /* no val_count parameter */
+        data_count = 20;
+        type = 1234;
+        memcpy( valueW, xxxW, sizeof(xxxW) );
+        memcpy( dataW, xxxW, sizeof(xxxW) );
+        res = RegEnumValueW( test_key, 0, valueW, NULL, NULL, &type, (BYTE*)dataW, &data_count );
+        ok( res == ERROR_INVALID_PARAMETER, "expected ERROR_INVALID_PARAMETER, got %d\n", res );
+    }
+
 cleanup:
     RegDeleteKeyA(test_key, "");
     RegCloseKey(test_key);
@@ -872,6 +1029,11 @@ static void test_reg_open_key(void)
     ok(hkResult != NULL, "hkResult != NULL\n");
     RegCloseKey(hkResult);
 
+    /* trailing slashes */
+    ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test\\\\", &hkResult);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+    RegCloseKey(hkResult);
+
     /* open nonexistent key
     * check that hkResult is set to NULL
     */
@@ -916,7 +1078,6 @@ static void test_reg_open_key(void)
     ok(ret == ERROR_INVALID_HANDLE || ret == ERROR_BADKEY, /* Windows 95 returns BADKEY */
        "expected ERROR_INVALID_HANDLE or ERROR_BADKEY, got %d\n", ret);
     ok(hkResult == hkPreserve, "expected hkResult == hkPreserve\n");
-    RegCloseKey(hkResult);
 
     /* send in NULL hkResult */
     ret = RegOpenKeyA(HKEY_CURRENT_USER, "Software\\Wine\\Test", NULL);
@@ -942,6 +1103,45 @@ static void test_reg_open_key(void)
        "expected ERROR_SUCCESS, ERROR_BAD_PATHNAME or ERROR_FILE_NOT_FOUND, got %d\n", ret);
     RegCloseKey(hkResult);
 
+    /* NULL or empty subkey of special root */
+    hkResult = NULL;
+    ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, NULL, 0, KEY_QUERY_VALUE, &hkResult);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+    ok(hkResult == HKEY_CLASSES_ROOT, "expected hkResult == HKEY_CLASSES_ROOT\n");
+
+    hkResult = NULL;
+    ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, "", 0, KEY_QUERY_VALUE, &hkResult);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+    ok(hkResult == HKEY_CLASSES_ROOT, "expected hkResult == HKEY_CLASSES_ROOT\n");
+
+    hkResult = NULL;
+    ret = RegOpenKeyExA(HKEY_CLASSES_ROOT, "\\", 0, KEY_QUERY_VALUE, &hkResult);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+    ok(hkResult != HKEY_CLASSES_ROOT, "expected hkResult to be a new key\n");
+    ok(!RegCloseKey(hkResult), "got invalid hkey\n");
+
+    /* empty subkey of existing handle */
+    hkResult = hkPreserve;
+    ret = RegOpenKeyExA(hkPreserve, "", 0, KEY_QUERY_VALUE, &hkResult);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+    ok(hkResult != hkPreserve, "expected hkResult != hkPreserve\n");
+    ok(!RegCloseKey(hkResult), "got invalid hkey\n");
+
+    /* NULL subkey of existing handle */
+    hkResult = hkPreserve;
+    ret = RegOpenKeyExA(hkPreserve, NULL, 0, KEY_QUERY_VALUE, &hkResult);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+    ok(hkResult != hkPreserve, "expected hkResult != hkPreserve\n");
+    ok(!RegCloseKey(hkResult), "got invalid hkey\n");
+
+    /* empty subkey of NULL */
+    hkResult = hkPreserve;
+    ret = RegOpenKeyExA(NULL, "", 0, KEY_QUERY_VALUE, &hkResult);
+    ok(ret == ERROR_INVALID_HANDLE, "expected ERROR_INVALID_HANDLE, got %d\n", ret);
+    ok(hkResult == hkPreserve, "expected hkResult == hkPreserve\n");
+
+    RegCloseKey(hkPreserve);
+
     /* WOW64 flags */
     hkResult = NULL;
     ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software", 0, KEY_READ|KEY_WOW64_32KEY, &hkResult);
@@ -1000,13 +1200,21 @@ static void test_reg_open_key(void)
 
     ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
                           KEY_WOW64_32KEY | KEY_ALL_ACCESS, NULL, &hkRoot32, NULL);
-    ok(ret == ERROR_SUCCESS && hkRoot32 != NULL,
-       "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
+    if (limited_user)
+        ok(ret == ERROR_ACCESS_DENIED && hkRoot32 == NULL,
+           "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
+    else
+        ok(ret == ERROR_SUCCESS && hkRoot32 != NULL,
+           "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
 
     ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
                           KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &hkRoot64, NULL);
-    ok(ret == ERROR_SUCCESS && hkRoot64 != NULL,
-       "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
+    if (limited_user)
+        ok(ret == ERROR_ACCESS_DENIED && hkRoot64 == NULL,
+           "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
+    else
+        ok(ret == ERROR_SUCCESS && hkRoot64 != NULL,
+           "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
 
     bRet = AllocateAndInitializeSid(&sid_authority, 1, SECURITY_WORLD_RID,
                                     0, 0, 0, 0, 0, 0, 0, &world_sid);
@@ -1035,26 +1243,34 @@ static void test_reg_open_key(void)
     ok(bRet == TRUE,
        "Expected SetSecurityDescriptorDacl to return TRUE, got %d, last error %u\n", bRet, GetLastError());
 
-    /* The "sanctioned" methods of setting a registry ACL aren't implemented in Wine. */
-    bRet = SetKernelObjectSecurity(hkRoot64, DACL_SECURITY_INFORMATION, sd);
-    ok(bRet == TRUE,
-       "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError());
+    if (limited_user)
+    {
+        skip("not enough privileges to modify HKLM\n");
+    }
+    else
+    {
+        LONG error;
 
-    bRet = SetKernelObjectSecurity(hkRoot32, DACL_SECURITY_INFORMATION, sd);
-    ok(bRet == TRUE,
-       "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError());
+        error = RegSetKeySecurity(hkRoot64, DACL_SECURITY_INFORMATION, sd);
+        ok(error == ERROR_SUCCESS,
+           "Expected RegSetKeySecurity to return success, got error %u\n", error);
 
-    hkResult = NULL;
-    ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_WOW64_64KEY | KEY_READ, &hkResult);
-    ok(ret == ERROR_SUCCESS && hkResult != NULL,
-       "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
-    RegCloseKey(hkResult);
+        error = RegSetKeySecurity(hkRoot32, DACL_SECURITY_INFORMATION, sd);
+        ok(error == ERROR_SUCCESS,
+           "Expected RegSetKeySecurity to return success, got error %u\n", error);
 
-    hkResult = NULL;
-    ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_WOW64_32KEY | KEY_READ, &hkResult);
-    ok(ret == ERROR_SUCCESS && hkResult != NULL,
-       "RegOpenKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
-    RegCloseKey(hkResult);
+        hkResult = NULL;
+        ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_WOW64_64KEY | KEY_READ, &hkResult);
+        ok(ret == ERROR_SUCCESS && hkResult != NULL,
+           "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
+        RegCloseKey(hkResult);
+
+        hkResult = NULL;
+        ret = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_WOW64_32KEY | KEY_READ, &hkResult);
+        ok(ret == ERROR_SUCCESS && hkResult != NULL,
+           "RegOpenKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
+        RegCloseKey(hkResult);
+    }
 
     HeapFree(GetProcessHeap(), 0, sd);
     LocalFree(key_acl);
@@ -1087,8 +1303,8 @@ static void test_reg_create_key(void)
     ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
 
     /* clean up */
-    RegDeleteKey(hkey2, "");
-    RegDeleteKey(hkey1, "");
+    RegDeleteKeyA(hkey2, "");
+    RegDeleteKeyA(hkey1, "");
     RegCloseKey(hkey2);
     RegCloseKey(hkey1);
 
@@ -1106,8 +1322,8 @@ static void test_reg_create_key(void)
     ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
 
     /* clean up */
-    RegDeleteKey(hkey2, "");
-    RegDeleteKey(hkey1, "");
+    RegDeleteKeyA(hkey2, "");
+    RegDeleteKeyA(hkey1, "");
     RegCloseKey(hkey2);
     RegCloseKey(hkey1);
 
@@ -1117,10 +1333,16 @@ static void test_reg_create_key(void)
         ok(ret == ERROR_BAD_PATHNAME, "expected ERROR_BAD_PATHNAME, got %d\n", ret);
     else {
         ok(!ret, "RegCreateKeyExA failed with error %d\n", ret);
-        RegDeleteKey(hkey1, NULL);
+        RegDeleteKeyA(hkey1, "");
         RegCloseKey(hkey1);
     }
 
+    /* trailing backslash characters */
+    ret = RegCreateKeyExA(hkey_main, "Subkey4\\\\", 0, NULL, 0, KEY_NOTIFY, NULL, &hkey1, NULL);
+    ok(ret == ERROR_SUCCESS, "RegCreateKeyExA failed with error %d\n", ret);
+    RegDeleteKeyA(hkey1, "");
+    RegCloseKey(hkey1);
+
     /* WOW64 flags - open an existing key */
     hkey1 = NULL;
     ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software", 0, NULL, 0, KEY_READ|KEY_WOW64_32KEY, NULL, &hkey1, NULL);
@@ -1146,13 +1368,21 @@ static void test_reg_create_key(void)
 
     ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
                           KEY_WOW64_32KEY | KEY_ALL_ACCESS, NULL, &hkRoot32, NULL);
-    ok(ret == ERROR_SUCCESS && hkRoot32 != NULL,
-       "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%d)\n", ret);
+    if (limited_user)
+        ok(ret == ERROR_ACCESS_DENIED && hkRoot32 == NULL,
+           "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%d)\n", ret);
+    else
+        ok(ret == ERROR_SUCCESS && hkRoot32 != NULL,
+           "RegCreateKeyEx with KEY_WOW64_32KEY failed (err=%d)\n", ret);
 
     ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
                           KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &hkRoot64, NULL);
-    ok(ret == ERROR_SUCCESS && hkRoot64 != NULL,
-       "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%d)\n", ret);
+    if (limited_user)
+        ok(ret == ERROR_ACCESS_DENIED && hkRoot64 == NULL,
+           "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%d)\n", ret);
+    else
+        ok(ret == ERROR_SUCCESS && hkRoot64 != NULL,
+           "RegCreateKeyEx with KEY_WOW64_64KEY failed (err=%d)\n", ret);
 
     bRet = AllocateAndInitializeSid(&sid_authority, 1, SECURITY_WORLD_RID,
                                     0, 0, 0, 0, 0, 0, 0, &world_sid);
@@ -1169,7 +1399,7 @@ static void test_reg_create_key(void)
     access.Trustee.ptstrName = (char *)world_sid;
 
     dwRet = SetEntriesInAclA(1, &access, NULL, &key_acl);
-    ok(ret == ERROR_SUCCESS,
+    ok(dwRet == ERROR_SUCCESS,
        "Expected SetEntriesInAclA to return ERROR_SUCCESS, got %u, last error %u\n", dwRet, GetLastError());
 
     sd = HeapAlloc(GetProcessHeap(), 0, SECURITY_DESCRIPTOR_MIN_LENGTH);
@@ -1181,28 +1411,34 @@ static void test_reg_create_key(void)
     ok(bRet == TRUE,
        "Expected SetSecurityDescriptorDacl to return TRUE, got %d, last error %u\n", bRet, GetLastError());
 
-    /* The "sanctioned" methods of setting a registry ACL aren't implemented in Wine. */
-    bRet = SetKernelObjectSecurity(hkRoot64, DACL_SECURITY_INFORMATION, sd);
-    ok(bRet == TRUE,
-       "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError());
-
-    bRet = SetKernelObjectSecurity(hkRoot32, DACL_SECURITY_INFORMATION, sd);
-    ok(bRet == TRUE,
-       "Expected SetKernelObjectSecurity to return TRUE, got %d, last error %u\n", bRet, GetLastError());
-
-    hkey1 = NULL;
-    ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
-                          KEY_WOW64_64KEY | KEY_READ, NULL, &hkey1, NULL);
-    ok(ret == ERROR_SUCCESS && hkey1 != NULL,
-       "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
-    RegCloseKey(hkey1);
+    if (limited_user)
+    {
+        skip("not enough privileges to modify HKLM\n");
+    }
+    else
+    {
+        ret = RegSetKeySecurity(hkRoot64, DACL_SECURITY_INFORMATION, sd);
+        ok(ret == ERROR_SUCCESS,
+           "Expected RegSetKeySecurity to return success, got error %u\n", ret);
+
+        ret = RegSetKeySecurity(hkRoot32, DACL_SECURITY_INFORMATION, sd);
+        ok(ret == ERROR_SUCCESS,
+           "Expected RegSetKeySecurity to return success, got error %u\n", ret);
+
+        hkey1 = NULL;
+        ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
+                              KEY_WOW64_64KEY | KEY_READ, NULL, &hkey1, NULL);
+        ok(ret == ERROR_SUCCESS && hkey1 != NULL,
+           "RegOpenKeyEx with KEY_WOW64_64KEY failed (err=%u)\n", ret);
+        RegCloseKey(hkey1);
 
-    hkey1 = NULL;
-    ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
-                          KEY_WOW64_32KEY | KEY_READ, NULL, &hkey1, NULL);
-    ok(ret == ERROR_SUCCESS && hkey1 != NULL,
-       "RegOpenKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
-    RegCloseKey(hkey1);
+        hkey1 = NULL;
+        ret = RegCreateKeyExA(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, NULL, 0,
+                              KEY_WOW64_32KEY | KEY_READ, NULL, &hkey1, NULL);
+        ok(ret == ERROR_SUCCESS && hkey1 != NULL,
+           "RegOpenKeyEx with KEY_WOW64_32KEY failed (err=%u)\n", ret);
+        RegCloseKey(hkey1);
+    }
 
     HeapFree(GetProcessHeap(), 0, sd);
     LocalFree(key_acl);
@@ -1250,8 +1486,9 @@ static void test_reg_close_key(void)
 static void test_reg_delete_key(void)
 {
     DWORD ret;
+    HKEY key;
 
-    ret = RegDeleteKey(hkey_main, NULL);
+    ret = RegDeleteKeyA(hkey_main, NULL);
 
     /* There is a bug in NT4 and W2K that doesn't check if the subkey is NULL. If
      * there are also no subkeys available it will delete the key pointed to by hkey_main.
@@ -1268,39 +1505,15 @@ static void test_reg_delete_key(void)
            ret == ERROR_ACCESS_DENIED ||
            ret == ERROR_BADKEY, /* Win95 */
            "ret=%d\n", ret);
-}
-
-static void test_reg_save_key(void)
-{
-    DWORD ret;
-
-    ret = RegSaveKey(hkey_main, "saved_key", NULL);
-    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
-}
-
-static void test_reg_load_key(void)
-{
-    DWORD ret;
-    HKEY hkHandle;
 
-    ret = RegLoadKey(HKEY_LOCAL_MACHINE, "Test", "saved_key");
-    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
-
-    ret = RegOpenKey(HKEY_LOCAL_MACHINE, "Test", &hkHandle);
-    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
-
-    RegCloseKey(hkHandle);
-}
-
-static void test_reg_unload_key(void)
-{
-    DWORD ret;
-
-    ret = RegUnLoadKey(HKEY_LOCAL_MACHINE, "Test");
-    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
-
-    DeleteFile("saved_key");
-    DeleteFile("saved_key.LOG");
+    ret = RegCreateKeyA(hkey_main, "deleteme", &key);
+    ok(ret == ERROR_SUCCESS, "Could not create key, got %d\n", ret);
+    ret = RegDeleteKeyA(key, "");
+    ok(ret == ERROR_SUCCESS, "RegDeleteKeyA failed, got %d\n", ret);
+    RegCloseKey(key);
+    ret = RegOpenKeyA(hkey_main, "deleteme", &key);
+    ok(ret == ERROR_FILE_NOT_FOUND, "Key was not deleted, got %d\n", ret);
+    RegCloseKey(key);
 }
 
 static BOOL set_privileges(LPCSTR privilege, BOOL set)
@@ -1312,7 +1525,7 @@ static BOOL set_privileges(LPCSTR privilege, BOOL set)
     if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
         return FALSE;
 
-    if(!LookupPrivilegeValue(NULL, privilege, &luid))
+    if(!LookupPrivilegeValueA(NULL, privilege, &luid))
     {
         CloseHandle(hToken);
         return FALSE;
@@ -1337,6 +1550,66 @@ static BOOL set_privileges(LPCSTR privilege, BOOL set)
     return TRUE;
 }
 
+static void test_reg_save_key(void)
+{
+    DWORD ret;
+
+    if (!set_privileges(SE_BACKUP_NAME, TRUE) ||
+        !set_privileges(SE_RESTORE_NAME, FALSE))
+    {
+        win_skip("Failed to set SE_BACKUP_NAME privileges, skipping tests\n");
+        return;
+    }
+
+    ret = RegSaveKeyA(hkey_main, "saved_key", NULL);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+
+    set_privileges(SE_BACKUP_NAME, FALSE);
+}
+
+static void test_reg_load_key(void)
+{
+    DWORD ret;
+    HKEY hkHandle;
+
+    if (!set_privileges(SE_RESTORE_NAME, TRUE) ||
+        !set_privileges(SE_BACKUP_NAME, FALSE))
+    {
+        win_skip("Failed to set SE_RESTORE_NAME privileges, skipping tests\n");
+        return;
+    }
+
+    ret = RegLoadKeyA(HKEY_LOCAL_MACHINE, "Test", "saved_key");
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+
+    set_privileges(SE_RESTORE_NAME, FALSE);
+
+    ret = RegOpenKeyA(HKEY_LOCAL_MACHINE, "Test", &hkHandle);
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+
+    RegCloseKey(hkHandle);
+}
+
+static void test_reg_unload_key(void)
+{
+    DWORD ret;
+
+    if (!set_privileges(SE_RESTORE_NAME, TRUE) ||
+        !set_privileges(SE_BACKUP_NAME, FALSE))
+    {
+        win_skip("Failed to set SE_RESTORE_NAME privileges, skipping tests\n");
+        return;
+    }
+
+    ret = RegUnLoadKeyA(HKEY_LOCAL_MACHINE, "Test");
+    ok(ret == ERROR_SUCCESS, "expected ERROR_SUCCESS, got %d\n", ret);
+
+    set_privileges(SE_RESTORE_NAME, FALSE);
+
+    DeleteFileA("saved_key");
+    DeleteFileA("saved_key.LOG");
+}
+
 /* tests that show that RegConnectRegistry and 
    OpenSCManager accept computer names without the
    \\ prefix (what MSDN says).   */
@@ -1435,7 +1708,7 @@ static void test_reg_query_value(void)
     ret = RegQueryValueA(hkey_main, "subkey", val, NULL);
     ok(ret == ERROR_INVALID_PARAMETER, "Expected ERROR_INVALID_PARAMETER, got %d\n", ret);
     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(lstrlenA(val) == 0, "Expected val to be untouched, got %s\n", val);
+    ok(!val[0], "Expected val to be untouched, got %s\n", val);
 
     /* try a NULL value and size */
     ret = RegQueryValueA(hkey_main, "subkey", NULL, NULL);
@@ -1448,7 +1721,7 @@ static void test_reg_query_value(void)
     ret = RegQueryValueA(hkey_main, "subkey", val, &size);
     ok(ret == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", ret);
     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(lstrlenA(val) == 0, "Expected val to be untouched, got %s\n", val);
+    ok(!val[0], "Expected val to be untouched, got %s\n", val);
     ok(size == 5, "Expected 5, got %d\n", size);
 
     /* successfully read the value using 'subkey' */
@@ -1477,7 +1750,7 @@ static void test_reg_query_value(void)
     }
     ok(ret == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", ret);
     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(lstrlenW(valW) == 0, "Expected valW to be untouched\n");
+    ok(!valW[0], "Expected valW to be untouched\n");
     ok(size == sizeof(expected), "Got wrong size: %d\n", size);
 
     /* unicode - try size in WCHARS */
@@ -1486,7 +1759,7 @@ static void test_reg_query_value(void)
     ret = RegQueryValueW(subkey, NULL, valW, &size);
     ok(ret == ERROR_MORE_DATA, "Expected ERROR_MORE_DATA, got %d\n", ret);
     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
-    ok(lstrlenW(valW) == 0, "Expected valW to be untouched\n");
+    ok(!valW[0], "Expected valW to be untouched\n");
     ok(size == sizeof(expected), "Got wrong size: %d\n", size);
 
     /* unicode - successfully read the value */
@@ -1513,6 +1786,225 @@ cleanup:
     RegCloseKey(subkey);
 }
 
+static void test_reg_query_info(void)
+{
+    HKEY subkey;
+    HKEY subsubkey;
+    LONG ret;
+    char classbuffer[32];
+    WCHAR classbufferW[32];
+    char expectbuffer[32];
+    WCHAR expectbufferW[32];
+    char subkey_class[] = "subkey class";
+    WCHAR subkey_classW[] = {'s','u','b','k','e','y',' ','c','l','a','s','s',0};
+    char subsubkey_class[] = "subsubkey class";
+    DWORD classlen;
+    DWORD subkeys, maxsubkeylen, maxclasslen;
+    DWORD values, maxvaluenamelen, maxvaluelen;
+    DWORD sdlen;
+    FILETIME lastwrite;
+
+    ret = RegCreateKeyExA(hkey_main, "subkey", 0, subkey_class, 0, KEY_ALL_ACCESS, NULL, &subkey, NULL);
+    ok(ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", ret);
+
+    /* all parameters NULL */
+    ret = RegQueryInfoKeyA(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_INVALID_HANDLE, "ret = %d\n", ret);
+
+    ret = RegQueryInfoKeyW(NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_INVALID_HANDLE, "ret = %d\n", ret);
+
+    /* not requesting any information */
+    ret = RegQueryInfoKeyA(subkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+
+    ret = RegQueryInfoKeyW(subkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+
+    /* class without length is invalid */
+    memset(classbuffer, 0x55, sizeof(classbuffer));
+    ret = RegQueryInfoKeyA(subkey, classbuffer, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_INVALID_PARAMETER, "ret = %d\n", ret);
+    ok(classbuffer[0] == 0x55, "classbuffer[0] = 0x%x\n", classbuffer[0]);
+
+    memset(classbufferW, 0x55, sizeof(classbufferW));
+    ret = RegQueryInfoKeyW(subkey, classbufferW, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_INVALID_PARAMETER, "ret = %d\n", ret);
+    ok(classbufferW[0] == 0x5555, "classbufferW[0] = 0x%x\n", classbufferW[0]);
+
+    /* empty key */
+    sdlen = 0;
+    ret = RegQueryInfoKeyA(subkey, NULL, &classlen, NULL, &subkeys, &maxsubkeylen, &maxclasslen, &values, &maxvaluenamelen, &maxvaluelen, &sdlen, &lastwrite);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == strlen(subkey_class), "classlen = %u\n", classlen);
+    ok(subkeys == 0, "subkeys = %u\n", subkeys);
+    ok(maxsubkeylen == 0, "maxsubkeylen = %u\n", maxsubkeylen);
+    ok(maxclasslen == 0, "maxclasslen = %u\n", maxclasslen);
+    ok(values == 0, "values = %u\n", values);
+    ok(maxvaluenamelen == 0, "maxvaluenamelen = %u\n", maxvaluenamelen);
+    ok(maxvaluelen == 0, "maxvaluelen = %u\n", maxvaluelen);
+    todo_wine ok(sdlen != 0, "sdlen = %u\n", sdlen);
+    ok(lastwrite.dwLowDateTime != 0, "lastwrite.dwLowDateTime = %u\n", lastwrite.dwLowDateTime);
+    ok(lastwrite.dwHighDateTime != 0, "lastwrite.dwHighDateTime = %u\n", lastwrite.dwHighDateTime);
+
+    sdlen = 0;
+    ret = RegQueryInfoKeyW(subkey, NULL, &classlen, NULL, &subkeys, &maxsubkeylen, &maxclasslen, &values, &maxvaluenamelen, &maxvaluelen, &sdlen, &lastwrite);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == strlen(subkey_class), "classlen = %u\n", classlen);
+    ok(subkeys == 0, "subkeys = %u\n", subkeys);
+    ok(maxsubkeylen == 0, "maxsubkeylen = %u\n", maxsubkeylen);
+    ok(maxclasslen == 0, "maxclasslen = %u\n", maxclasslen);
+    ok(values == 0, "values = %u\n", values);
+    ok(maxvaluenamelen == 0, "maxvaluenamelen = %u\n", maxvaluenamelen);
+    ok(maxvaluelen == 0, "maxvaluelen = %u\n", maxvaluelen);
+    todo_wine ok(sdlen != 0, "sdlen = %u\n", sdlen);
+    ok(lastwrite.dwLowDateTime != 0, "lastwrite.dwLowDateTime = %u\n", lastwrite.dwLowDateTime);
+    ok(lastwrite.dwHighDateTime != 0, "lastwrite.dwHighDateTime = %u\n", lastwrite.dwHighDateTime);
+
+    ret = RegCreateKeyExA(subkey, "subsubkey", 0, subsubkey_class, 0, KEY_ALL_ACCESS, NULL, &subsubkey, NULL);
+    ok(ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", ret);
+
+    ret = RegSetValueExA(subkey, NULL, 0, REG_SZ, (const BYTE*)"data", 5);
+    ok(ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", ret);
+
+    /* with subkey & default value */
+    sdlen = 0;
+    ret = RegQueryInfoKeyA(subkey, NULL, &classlen, NULL, &subkeys, &maxsubkeylen, &maxclasslen, &values, &maxvaluenamelen, &maxvaluelen, &sdlen, &lastwrite);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == strlen(subkey_class), "classlen = %u\n", classlen);
+    ok(subkeys == 1, "subkeys = %u\n", subkeys);
+    ok(maxsubkeylen == strlen("subsubkey"), "maxsubkeylen = %u\n", maxsubkeylen);
+    ok(maxclasslen == strlen(subsubkey_class), "maxclasslen = %u\n", maxclasslen);
+    ok(values == 1, "values = %u\n", values);
+    ok(maxvaluenamelen == 0, "maxvaluenamelen = %u\n", maxvaluenamelen);
+    ok(maxvaluelen == sizeof("data") * sizeof(WCHAR), "maxvaluelen = %u\n", maxvaluelen);
+    todo_wine ok(sdlen != 0, "sdlen = %u\n", sdlen);
+    ok(lastwrite.dwLowDateTime != 0, "lastwrite.dwLowDateTime = %u\n", lastwrite.dwLowDateTime);
+    ok(lastwrite.dwHighDateTime != 0, "lastwrite.dwHighDateTime = %u\n", lastwrite.dwHighDateTime);
+
+    sdlen = 0;
+    ret = RegQueryInfoKeyW(subkey, NULL, &classlen, NULL, &subkeys, &maxsubkeylen, &maxclasslen, &values, &maxvaluenamelen, &maxvaluelen, &sdlen, &lastwrite);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == strlen(subkey_class), "classlen = %u\n", classlen);
+    ok(subkeys == 1, "subkeys = %u\n", subkeys);
+    ok(maxsubkeylen == strlen("subsubkey"), "maxsubkeylen = %u\n", maxsubkeylen);
+    ok(maxclasslen == strlen(subsubkey_class), "maxclasslen = %u\n", maxclasslen);
+    ok(values == 1, "values = %u\n", values);
+    ok(maxvaluenamelen == 0, "maxvaluenamelen = %u\n", maxvaluenamelen);
+    ok(maxvaluelen == sizeof("data") * sizeof(WCHAR), "maxvaluelen = %u\n", maxvaluelen);
+    todo_wine ok(sdlen != 0, "sdlen = %u\n", sdlen);
+    ok(lastwrite.dwLowDateTime != 0, "lastwrite.dwLowDateTime = %u\n", lastwrite.dwLowDateTime);
+    ok(lastwrite.dwHighDateTime != 0, "lastwrite.dwHighDateTime = %u\n", lastwrite.dwHighDateTime);
+
+    ret = RegSetValueExA(subkey, "value one", 0, REG_SZ, (const BYTE*)"first value data", 17);
+    ok(ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", ret);
+
+    ret = RegSetValueExA(subkey, "value 2", 0, REG_SZ, (const BYTE*)"second value data", 18);
+    ok(ret == ERROR_SUCCESS, "Expected ERROR_SUCCESS, got %d\n", ret);
+
+    /* with named value */
+    ret = RegQueryInfoKeyA(subkey, NULL, &classlen, NULL, &subkeys, &maxsubkeylen, &maxclasslen, &values, &maxvaluenamelen, &maxvaluelen, &sdlen, &lastwrite);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(values == 3, "values = %u\n", values);
+    ok(maxvaluenamelen == strlen("value one"), "maxvaluenamelen = %u\n", maxvaluenamelen);
+    ok(maxvaluelen == sizeof("second value data") * sizeof(WCHAR), "maxvaluelen = %u\n", maxvaluelen);
+
+    ret = RegQueryInfoKeyW(subkey, NULL, &classlen, NULL, &subkeys, &maxsubkeylen, &maxclasslen, &values, &maxvaluenamelen, &maxvaluelen, &sdlen, &lastwrite);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(values == 3, "values = %u\n", values);
+    ok(maxvaluenamelen == strlen("value one"), "maxvaluenamelen = %u\n", maxvaluenamelen);
+    ok(maxvaluelen == sizeof("second value data") * sizeof(WCHAR), "maxvaluelen = %u\n", maxvaluelen);
+
+    /* class name with zero size buffer */
+    memset(classbuffer, 0x55, sizeof(classbuffer));
+    classlen = 0;
+    ret = RegQueryInfoKeyA(subkey, classbuffer, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == strlen(subkey_class) /* win2k */ ||
+       classlen == 0, "classlen = %u\n", classlen);
+    memset(expectbuffer, 0x55, sizeof(expectbuffer));
+    ok(!memcmp(classbuffer, expectbuffer, sizeof(classbuffer)), "classbuffer was modified\n");
+
+    memset(classbufferW, 0x55, sizeof(classbufferW));
+    classlen = 0;
+    ret = RegQueryInfoKeyW(subkey, classbufferW, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == strlen(subkey_class) /* win2k */ ||
+       classlen == 0, "classlen = %u\n", classlen);
+    memset(expectbufferW, 0x55, sizeof(expectbufferW));
+    ok(!memcmp(classbufferW, expectbufferW, sizeof(classbufferW)), "classbufferW was modified\n");
+
+    /* class name with one char buffer */
+    memset(classbuffer, 0x55, sizeof(classbuffer));
+    classlen = 1;
+    ret = RegQueryInfoKeyA(subkey, classbuffer, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_MORE_DATA, "ret = %d\n", ret);
+    ok(classlen == 0, "classlen = %u\n", classlen);
+    memset(expectbuffer, 0x55, sizeof(expectbuffer));
+    expectbuffer[0] = 0;
+    ok(!memcmp(classbuffer, expectbuffer, sizeof(classbuffer)), "classbuffer was modified\n");
+
+    memset(classbufferW, 0x55, sizeof(classbufferW));
+    classlen = 1;
+    ret = RegQueryInfoKeyW(subkey, classbufferW, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_INSUFFICIENT_BUFFER, "ret = %d\n", ret);
+    ok(classlen == 0 /* win8 */ ||
+       classlen == strlen(subkey_class), "classlen = %u\n", classlen);
+    memset(expectbufferW, 0x55, sizeof(expectbufferW));
+    ok(!memcmp(classbufferW, expectbufferW, sizeof(classbufferW)), "classbufferW was modified\n");
+
+    /* class name with buffer one char too small */
+    memset(classbuffer, 0x55, sizeof(classbuffer));
+    classlen = sizeof(subkey_class) - 1;
+    ret = RegQueryInfoKeyA(subkey, classbuffer, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_MORE_DATA, "ret = %d\n", ret);
+    ok(classlen == sizeof(subkey_class) - 2, "classlen = %u\n", classlen);
+    memset(expectbuffer, 0x55, sizeof(expectbuffer));
+    strcpy(expectbuffer, subkey_class);
+    expectbuffer[sizeof(subkey_class) - 2] = 0;
+    expectbuffer[sizeof(subkey_class) - 1] = 0x55;
+    ok(!memcmp(classbuffer, expectbuffer, sizeof(classbuffer)),
+       "classbuffer = %.*s, expected %s\n",
+       (int)sizeof(classbuffer), classbuffer, expectbuffer);
+
+    memset(classbufferW, 0x55, sizeof(classbufferW));
+    classlen = sizeof(subkey_class) - 1;
+    ret = RegQueryInfoKeyW(subkey, classbufferW, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_INSUFFICIENT_BUFFER, "ret = %d\n", ret);
+    ok(classlen == sizeof(subkey_class) - 2 /* win8 */ ||
+       classlen == strlen(subkey_class), "classlen = %u\n", classlen);
+    memset(expectbufferW, 0x55, sizeof(expectbufferW));
+    ok(!memcmp(classbufferW, expectbufferW, sizeof(classbufferW)), "classbufferW was modified\n");
+
+    /* class name with large enough buffer */
+    memset(classbuffer, 0x55, sizeof(classbuffer));
+    classlen = sizeof(subkey_class);
+    ret = RegQueryInfoKeyA(subkey, classbuffer, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == sizeof(subkey_class) - 1, "classlen = %u\n", classlen);
+    memset(expectbuffer, 0x55, sizeof(expectbuffer));
+    strcpy(expectbuffer, subkey_class);
+    ok(!memcmp(classbuffer, expectbuffer, sizeof(classbuffer)),
+       "classbuffer = \"%.*s\", expected %s\n",
+       (int)sizeof(classbuffer), classbuffer, expectbuffer);
+
+    memset(classbufferW, 0x55, sizeof(classbufferW));
+    classlen = sizeof(subkey_class);
+    ret = RegQueryInfoKeyW(subkey, classbufferW, &classlen, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "ret = %d\n", ret);
+    ok(classlen == sizeof(subkey_class) - 1, "classlen = %u\n", classlen);
+    memset(expectbufferW, 0x55, sizeof(expectbufferW));
+    lstrcpyW(expectbufferW, subkey_classW);
+    ok(!memcmp(classbufferW, expectbufferW, sizeof(classbufferW)),
+       "classbufferW = %s, expected %s\n",
+       wine_dbgstr_wn(classbufferW, sizeof(classbufferW) / sizeof(WCHAR)), wine_dbgstr_w(expectbufferW));
+
+    RegDeleteKeyA(subsubkey, "");
+    RegCloseKey(subsubkey);
+    RegDeleteKeyA(subkey, "");
+    RegCloseKey(subkey);
+}
+
 static void test_string_termination(void)
 {
     HKEY subkey;
@@ -1653,8 +2145,7 @@ static void test_reg_delete_tree(void)
     ret = RegQueryValueA(subkey, NULL, buffer, &size);
     ok(ret == ERROR_SUCCESS,
         "Default value of subkey is not present\n");
-    ok(!lstrlenA(buffer),
-        "Expected length 0 got length %u(%s)\n", lstrlenA(buffer), buffer);
+    ok(!buffer[0], "Expected length 0 got length %u(%s)\n", lstrlenA(buffer), buffer);
     size = MAX_PATH;
     ok(RegQueryValueA(subkey, "value", buffer, &size),
         "Value is still present\n");
@@ -1668,7 +2159,7 @@ static void test_rw_order(void)
 {
     HKEY hKey;
     DWORD dw = 0;
-    static char keyname[] = "test_rw_order";
+    static const char keyname[] = "test_rw_order";
     char value_buf[2];
     DWORD values, value_len, value_name_max_len;
     LSTATUS ret;
@@ -1707,7 +2198,7 @@ static void test_rw_order(void)
     ok(!RegEnumValueA(hKey, 3, value_buf, &value_len, NULL, NULL, NULL, NULL), "RegEnumValueA failed\n");
     todo_wine ok(strcmp(value_buf, "B") == 0, "Expected name \"B\", got %s\n", value_buf);
 
-    ok(!RegDeleteKey(HKEY_CURRENT_USER, keyname), "Failed to delete key\n");
+    ok(!RegDeleteKeyA(HKEY_CURRENT_USER, keyname), "Failed to delete key\n");
 }
 
 static void test_symlinks(void)
@@ -1825,10 +2316,10 @@ static void test_symlinks(void)
                            KEY_ALL_ACCESS, NULL, &key, NULL );
     ok( err == ERROR_ALREADY_EXISTS, "RegCreateKeyEx wrong error %u\n", err );
 
-    err = RegDeleteKey( hkey_main, "target" );
+    err = RegDeleteKeyA( hkey_main, "target" );
     ok( err == ERROR_SUCCESS, "RegDeleteKey failed error %u\n", err );
 
-    err = RegDeleteKey( hkey_main, "link" );
+    err = RegDeleteKeyA( hkey_main, "link" );
     ok( err == ERROR_FILE_NOT_FOUND, "RegDeleteKey wrong error %u\n", err );
 
     status = pNtDeleteKey( link );
@@ -1867,8 +2358,9 @@ static void _check_key_value( int line, HANDLE root, const char *name, DWORD fla
 static void test_redirection(void)
 {
     DWORD err, type, dw, len;
-    HKEY key, root32, root64, key32, key64;
+    HKEY key, root32, root64, key32, key64, native, op_key;
     BOOL is_vista = FALSE;
+    REGSAM opposite = (sizeof(void*) == 8 ? KEY_WOW64_32KEY : KEY_WOW64_64KEY);
 
     if (ptr_size != 64)
     {
@@ -1974,6 +2466,27 @@ static void test_redirection(void)
         check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
         RegCloseKey( key );
     }
+    else
+    {
+        err = RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software", 0, NULL, 0,
+                               KEY_WOW64_64KEY | KEY_ALL_ACCESS, NULL, &key, NULL );
+        ok( err == ERROR_SUCCESS, "RegCreateKeyExA failed: %u\n", err );
+        check_key_value( key, "Wine\\Winetest", 0, 64 );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 64 );
+        dw = get_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY );
+        todo_wine ok( dw == 32, "wrong value %u\n", dw );
+        check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
+        RegCloseKey( key );
+
+        err = RegCreateKeyExA( HKEY_LOCAL_MACHINE, "Software", 0, NULL, 0,
+                               KEY_WOW64_32KEY | KEY_ALL_ACCESS, NULL, &key, NULL );
+        ok( err == ERROR_SUCCESS, "RegCreateKeyExA failed: %u\n", err );
+        check_key_value( key, "Wine\\Winetest", 0, 32 );
+        dw = get_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY );
+        ok( dw == 32 || broken(dw == 64) /* vista */, "wrong value %u\n", dw );
+        check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
+        RegCloseKey( key );
+    }
 
     check_key_value( HKEY_LOCAL_MACHINE, "Software\\Wine\\Winetest", 0, ptr_size );
     check_key_value( HKEY_LOCAL_MACHINE, "Software\\Wow6432Node\\Wine\\Winetest", 0, 32 );
@@ -2102,6 +2615,39 @@ static void test_redirection(void)
     RegCloseKey( key64 );
     RegCloseKey( root32 );
     RegCloseKey( root64 );
+
+    /* open key in native bit mode */
+    err = RegOpenKeyExA(HKEY_CLASSES_ROOT, "Interface", 0, KEY_ALL_ACCESS, &native);
+    ok(err == ERROR_SUCCESS, "got %i\n", err);
+
+    pRegDeleteKeyExA(native, "AWineTest", 0, 0);
+
+    /* write subkey in opposite bit mode */
+    err = RegOpenKeyExA(HKEY_CLASSES_ROOT, "Interface", 0, KEY_ALL_ACCESS | opposite, &op_key);
+    ok(err == ERROR_SUCCESS, "got %i\n", err);
+
+    err = RegCreateKeyExA(op_key, "AWineTest", 0, NULL, 0, KEY_ALL_ACCESS | opposite,
+            NULL, &key, NULL);
+    ok(err == ERROR_SUCCESS || err == ERROR_ACCESS_DENIED, "got %i\n", err);
+    if(err != ERROR_SUCCESS){
+        win_skip("Can't write to registry\n");
+        RegCloseKey(op_key);
+        RegCloseKey(native);
+        return;
+    }
+    RegCloseKey(key);
+
+    /* verify subkey is not present in native mode */
+    err = RegOpenKeyExA(native, "AWineTest", 0, KEY_ALL_ACCESS, &key);
+    ok(err == ERROR_FILE_NOT_FOUND ||
+            broken(err == ERROR_SUCCESS), /* before Win7, HKCR is reflected instead of redirected */
+            "got %i\n", err);
+
+    err = pRegDeleteKeyExA(op_key, "AWineTest", opposite, 0);
+    ok(err == ERROR_SUCCESS, "got %i\n", err);
+
+    RegCloseKey(op_key);
+    RegCloseKey(native);
 }
 
 static void test_classesroot(void)
@@ -2133,7 +2679,6 @@ static void test_classesroot(void)
     todo_wine ok(res == ERROR_SUCCESS ||
                  broken(res == ERROR_FILE_NOT_FOUND /* WinNT */),
                  "test key not found in hkcr: %d\n", res);
-    todo_wine ok(IS_HKCR(hkcr), "hkcr mask not set in %p\n", hkcr);
     if (res)
     {
         skip("HKCR key merging not supported\n");
@@ -2142,6 +2687,8 @@ static void test_classesroot(void)
         return;
     }
 
+    todo_wine ok(IS_HKCR(hkcr), "hkcr mask not set in %p\n", hkcr);
+
     /* set a value in user's classes */
     res = RegSetValueExA(hkey, "val1", 0, REG_SZ, (const BYTE *)"user", sizeof("user"));
     ok(res == ERROR_SUCCESS, "RegSetValueExA failed: %d, GLE=%x\n", res, GetLastError());
@@ -2364,7 +2911,7 @@ static void test_classesroot(void)
     RegCloseKey( hklmsub1 );
 
     /* delete subkey1 from hkcr (should point at user's classes) */
-    res = RegDeleteKey(hkcr, "subkey1");
+    res = RegDeleteKeyA(hkcr, "subkey1");
     ok(res == ERROR_SUCCESS, "RegDeleteKey failed: %d\n", res);
 
     /* confirm key was removed in hkey but not hklm */
@@ -2375,7 +2922,7 @@ static void test_classesroot(void)
     ok(!IS_HKCR(hklmsub1), "hkcr mask set in %p\n", hklmsub1);
 
     /* delete subkey1 from hkcr again (which should now point at hklm) */
-    res = RegDeleteKey(hkcr, "subkey1");
+    res = RegDeleteKeyA(hkcr, "subkey1");
     ok(res == ERROR_SUCCESS, "RegDeleteKey failed: %d\n", res);
 
     /* confirm hkey was removed in hklm */
@@ -2586,7 +3133,8 @@ static void test_classesroot_mask(void)
 
     res = RegOpenKeyA( HKEY_CLASSES_ROOT, "CLSID", &hkey );
     ok(res == ERROR_SUCCESS, "RegOpenKeyA failed: %d\n", res);
-    todo_wine ok(IS_HKCR(hkey), "hkcr mask not set in %p\n", hkey);
+    todo_wine ok(IS_HKCR(hkey) || broken(!IS_HKCR(hkey)) /* WinNT */,
+                 "hkcr mask not set in %p\n", hkey);
     RegCloseKey( hkey );
 
     res = RegOpenKeyA( HKEY_CURRENT_USER, "Software", &hkey );
@@ -2683,12 +3231,63 @@ static void test_delete_value(void)
        "expect ERROR_FILE_NOT_FOUND, got %i\n", res);
 }
 
+static void test_delete_key_value(void)
+{
+    HKEY subkey;
+    LONG ret;
+
+    if (!pRegDeleteKeyValueA)
+    {
+        win_skip("RegDeleteKeyValue is not available.\n");
+        return;
+    }
+
+    ret = pRegDeleteKeyValueA(NULL, NULL, NULL);
+    ok(ret == ERROR_INVALID_HANDLE, "got %d\n", ret);
+
+    ret = pRegDeleteKeyValueA(hkey_main, NULL, NULL);
+    ok(ret == ERROR_FILE_NOT_FOUND, "got %d\n", ret);
+
+    ret = RegSetValueExA(hkey_main, "test", 0, REG_SZ, (const BYTE*)"value", 6);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+    ret = RegQueryValueExA(hkey_main, "test", NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+    /* NULL subkey name means delete from open key */
+    ret = pRegDeleteKeyValueA(hkey_main, NULL, "test");
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+    ret = RegQueryValueExA(hkey_main, "test", NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_FILE_NOT_FOUND, "got %d\n", ret);
+
+    /* now with real subkey */
+    ret = RegCreateKeyExA(hkey_main, "Subkey1", 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &subkey, NULL);
+    ok(!ret, "failed with error %d\n", ret);
+
+    ret = RegSetValueExA(subkey, "test", 0, REG_SZ, (const BYTE*)"value", 6);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+    ret = RegQueryValueExA(subkey, "test", NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+    ret = pRegDeleteKeyValueA(hkey_main, "Subkey1", "test");
+    ok(ret == ERROR_SUCCESS, "got %d\n", ret);
+
+    ret = RegQueryValueExA(subkey, "test", NULL, NULL, NULL, NULL);
+    ok(ret == ERROR_FILE_NOT_FOUND, "got %d\n", ret);
+
+    RegDeleteKeyA(subkey, "");
+    RegCloseKey(subkey);
+}
+
 START_TEST(registry)
 {
     /* Load pointers for functions that are not available in all Windows versions */
     InitFunctionPtrs();
 
     setup_main_key();
+    check_user_privs();
     test_set_value();
     create_test_entries();
     test_enum_value();
@@ -2699,6 +3298,7 @@ START_TEST(registry)
     test_reg_close_key();
     test_reg_delete_key();
     test_reg_query_value();
+    test_reg_query_info();
     test_string_termination();
     test_symlinks();
     test_redirection();
@@ -2706,22 +3306,15 @@ START_TEST(registry)
     test_classesroot_enum();
     test_classesroot_mask();
 
-    /* SaveKey/LoadKey require the SE_BACKUP_NAME privilege to be set */
-    if (set_privileges(SE_BACKUP_NAME, TRUE) &&
-        set_privileges(SE_RESTORE_NAME, TRUE))
-    {
-        test_reg_save_key();
-        test_reg_load_key();
-        test_reg_unload_key();
-
-        set_privileges(SE_BACKUP_NAME, FALSE);
-        set_privileges(SE_RESTORE_NAME, FALSE);
-    }
+    test_reg_save_key();
+    test_reg_load_key();
+    test_reg_unload_key();
 
     test_reg_delete_tree();
     test_rw_order();
     test_deleted_key();
     test_delete_value();
+    test_delete_key_value();
 
     /* cleanup */
     delete_key( hkey_main );