1 /* Unit test suite for Rtl* Registry API functions
2 *
3 * Copyright 2003 Thomas Mertes
4 * Copyright 2005 Brad DeMorrow
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 *
20 * NOTE: I don't test every RelativeTo value because it would be redundant, all calls go through
21 * helper function RTL_GetKeyHandle().--Brad DeMorrow
22 *
23 */
24
25 #include "ntdll_test.h"
26 #include "winternl.h"
27 #include "stdio.h"
28 #include "winnt.h"
29 #include "winnls.h"
30 #include "stdlib.h"
31
32 /* A test string */
33 static const WCHAR stringW[] = {'s', 't', 'r', 'i', 'n', 'g', 'W', 0};
34 /* A size, in bytes, short enough to cause truncation of the above */
35 #define STR_TRUNC_SIZE (sizeof(stringW)-2*sizeof(*stringW))
36
37 #ifndef __WINE_WINTERNL_H
38
39 /* RtlQueryRegistryValues structs and defines */
40 #define RTL_REGISTRY_ABSOLUTE 0
41 #define RTL_REGISTRY_SERVICES 1
42 #define RTL_REGISTRY_CONTROL 2
43 #define RTL_REGISTRY_WINDOWS_NT 3
44 #define RTL_REGISTRY_DEVICEMAP 4
45 #define RTL_REGISTRY_USER 5
46
47 #define RTL_REGISTRY_HANDLE 0x40000000
48 #define RTL_REGISTRY_OPTIONAL 0x80000000
49
50 #define RTL_QUERY_REGISTRY_SUBKEY 0x00000001
51 #define RTL_QUERY_REGISTRY_TOPKEY 0x00000002
52 #define RTL_QUERY_REGISTRY_REQUIRED 0x00000004
53 #define RTL_QUERY_REGISTRY_NOVALUE 0x00000008
54 #define RTL_QUERY_REGISTRY_NOEXPAND 0x00000010
55 #define RTL_QUERY_REGISTRY_DIRECT 0x00000020
56 #define RTL_QUERY_REGISTRY_DELETE 0x00000040
57
58 typedef NTSTATUS (WINAPI *PRTL_QUERY_REGISTRY_ROUTINE)( PCWSTR ValueName,
59 ULONG ValueType,
60 PVOID ValueData,
61 ULONG ValueLength,
62 PVOID Context,
63 PVOID EntryContext);
64
65 typedef struct _RTL_QUERY_REGISTRY_TABLE {
66 PRTL_QUERY_REGISTRY_ROUTINE QueryRoutine;
67 ULONG Flags;
68 PWSTR Name;
69 PVOID EntryContext;
70 ULONG DefaultType;
71 PVOID DefaultData;
72 ULONG DefaultLength;
73 } RTL_QUERY_REGISTRY_TABLE, *PRTL_QUERY_REGISTRY_TABLE;
74
75 typedef struct _KEY_VALUE_BASIC_INFORMATION {
76 ULONG TitleIndex;
77 ULONG Type;
78 ULONG NameLength;
79 WCHAR Name[1];
80 } KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;
81
82 typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
83 ULONG TitleIndex;
84 ULONG Type;
85 ULONG DataLength;
86 UCHAR Data[1];
87 } KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;
88
89 typedef struct _KEY_VALUE_FULL_INFORMATION {
90 ULONG TitleIndex;
91 ULONG Type;
92 ULONG DataOffset;
93 ULONG DataLength;
94 ULONG NameLength;
95 WCHAR Name[1];
96 } KEY_VALUE_FULL_INFORMATION, *PKEY_VALUE_FULL_INFORMATION;
97
98 typedef enum _KEY_VALUE_INFORMATION_CLASS {
99 KeyValueBasicInformation,
100 KeyValueFullInformation,
101 KeyValuePartialInformation,
102 KeyValueFullInformationAlign64,
103 KeyValuePartialInformationAlign64
104 } KEY_VALUE_INFORMATION_CLASS;
105
106 #define InitializeObjectAttributes(p,n,a,r,s) \
107 do { \
108 (p)->Length = sizeof(OBJECT_ATTRIBUTES); \
109 (p)->RootDirectory = r; \
110 (p)->Attributes = a; \
111 (p)->ObjectName = n; \
112 (p)->SecurityDescriptor = s; \
113 (p)->SecurityQualityOfService = NULL; \
114 } while (0)
115
116 #endif
117
118 static BOOLEAN (WINAPI * pRtlCreateUnicodeStringFromAsciiz)(PUNICODE_STRING, LPCSTR);
119 static void (WINAPI * pRtlInitUnicodeString)(PUNICODE_STRING,PCWSTR);
120 static NTSTATUS (WINAPI * pRtlFreeUnicodeString)(PUNICODE_STRING);
121 static NTSTATUS (WINAPI * pNtDeleteValueKey)(IN HANDLE, IN PUNICODE_STRING);
122 static NTSTATUS (WINAPI * pRtlQueryRegistryValues)(IN ULONG, IN PCWSTR,IN PRTL_QUERY_REGISTRY_TABLE, IN PVOID,IN PVOID);
123 static NTSTATUS (WINAPI * pRtlCheckRegistryKey)(IN ULONG,IN PWSTR);
124 static NTSTATUS (WINAPI * pRtlOpenCurrentUser)(IN ACCESS_MASK, PHANDLE);
125 static NTSTATUS (WINAPI * pNtOpenKey)(PHANDLE, IN ACCESS_MASK, IN POBJECT_ATTRIBUTES);
126 static NTSTATUS (WINAPI * pNtOpenKeyEx)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, ULONG);
127 static NTSTATUS (WINAPI * pNtClose)(IN HANDLE);
128 static NTSTATUS (WINAPI * pNtFlushKey)(HANDLE);
129 static NTSTATUS (WINAPI * pNtDeleteKey)(HANDLE);
130 static NTSTATUS (WINAPI * pNtCreateKey)( PHANDLE retkey, ACCESS_MASK access, const OBJECT_ATTRIBUTES *attr,
131 ULONG TitleIndex, const UNICODE_STRING *class, ULONG options,
132 PULONG dispos );
133 static NTSTATUS (WINAPI * pNtQueryKey)(HANDLE,KEY_INFORMATION_CLASS,PVOID,ULONG,PULONG);
134 static NTSTATUS (WINAPI * pNtQueryLicenseValue)(const UNICODE_STRING *,ULONG *,PVOID,ULONG,ULONG *);
135 static NTSTATUS (WINAPI * pNtQueryValueKey)(HANDLE,const UNICODE_STRING *,KEY_VALUE_INFORMATION_CLASS,void *,DWORD,DWORD *);
136 static NTSTATUS (WINAPI * pNtSetValueKey)(HANDLE, const PUNICODE_STRING, ULONG,
137 ULONG, const void*, ULONG );
138 static NTSTATUS (WINAPI * pNtQueryInformationProcess)(HANDLE,PROCESSINFOCLASS,PVOID,ULONG,PULONG);
139 static NTSTATUS (WINAPI * pRtlFormatCurrentUserKeyPath)(PUNICODE_STRING);
140 static LONG (WINAPI * pRtlCompareUnicodeString)(const PUNICODE_STRING,const PUNICODE_STRING,BOOLEAN);
141 static BOOLEAN (WINAPI * pRtlCreateUnicodeString)(PUNICODE_STRING, LPCWSTR);
142 static LPVOID (WINAPI * pRtlReAllocateHeap)(IN PVOID, IN ULONG, IN PVOID, IN ULONG);
143 static NTSTATUS (WINAPI * pRtlAppendUnicodeToString)(PUNICODE_STRING, PCWSTR);
144 static NTSTATUS (WINAPI * pRtlUnicodeStringToAnsiString)(PSTRING, PUNICODE_STRING, BOOL);
145 static NTSTATUS (WINAPI * pRtlFreeHeap)(PVOID, ULONG, PVOID);
146 static LPVOID (WINAPI * pRtlAllocateHeap)(PVOID,ULONG,ULONG);
147 static NTSTATUS (WINAPI * pRtlZeroMemory)(PVOID, ULONG);
148 static NTSTATUS (WINAPI * pRtlpNtQueryValueKey)(HANDLE,ULONG*,PBYTE,DWORD*,void *);
149 static NTSTATUS (WINAPI * pNtNotifyChangeKey)(HANDLE,HANDLE,PIO_APC_ROUTINE,PVOID,PIO_STATUS_BLOCK,ULONG,BOOLEAN,PVOID,ULONG,BOOLEAN);
150 static NTSTATUS (WINAPI * pNtNotifyChangeMultipleKeys)(HANDLE,ULONG,OBJECT_ATTRIBUTES*,HANDLE,PIO_APC_ROUTINE,
151 void*,IO_STATUS_BLOCK*,ULONG,BOOLEAN,void*,ULONG,BOOLEAN);
152 static NTSTATUS (WINAPI * pNtWaitForSingleObject)(HANDLE,BOOLEAN,const LARGE_INTEGER*);
153
154 static HMODULE hntdll = 0;
155 static int CurrentTest = 0;
156 static UNICODE_STRING winetestpath;
157
158 #define NTDLL_GET_PROC(func) \
159 p ## func = (void*)GetProcAddress(hntdll, #func); \
160 if(!p ## func) { \
161 trace("GetProcAddress(%s) failed\n", #func); \
162 FreeLibrary(hntdll); \
163 return FALSE; \
164 }
165
InitFunctionPtrs(void)166 static BOOL InitFunctionPtrs(void)
167 {
168 hntdll = LoadLibraryA("ntdll.dll");
169 if(!hntdll) {
170 trace("Could not load ntdll.dll\n");
171 return FALSE;
172 }
173 NTDLL_GET_PROC(RtlInitUnicodeString)
174 NTDLL_GET_PROC(RtlCreateUnicodeStringFromAsciiz)
175 NTDLL_GET_PROC(RtlCreateUnicodeString)
176 NTDLL_GET_PROC(RtlFreeUnicodeString)
177 NTDLL_GET_PROC(RtlQueryRegistryValues)
178 NTDLL_GET_PROC(RtlCheckRegistryKey)
179 NTDLL_GET_PROC(RtlOpenCurrentUser)
180 NTDLL_GET_PROC(NtClose)
181 NTDLL_GET_PROC(NtDeleteValueKey)
182 NTDLL_GET_PROC(NtCreateKey)
183 NTDLL_GET_PROC(NtFlushKey)
184 NTDLL_GET_PROC(NtDeleteKey)
185 NTDLL_GET_PROC(NtQueryKey)
186 NTDLL_GET_PROC(NtQueryValueKey)
187 NTDLL_GET_PROC(NtQueryInformationProcess)
188 NTDLL_GET_PROC(NtSetValueKey)
189 NTDLL_GET_PROC(NtOpenKey)
190 NTDLL_GET_PROC(NtNotifyChangeKey)
191 NTDLL_GET_PROC(RtlFormatCurrentUserKeyPath)
192 NTDLL_GET_PROC(RtlCompareUnicodeString)
193 NTDLL_GET_PROC(RtlReAllocateHeap)
194 NTDLL_GET_PROC(RtlAppendUnicodeToString)
195 NTDLL_GET_PROC(RtlUnicodeStringToAnsiString)
196 NTDLL_GET_PROC(RtlFreeHeap)
197 NTDLL_GET_PROC(RtlAllocateHeap)
198 NTDLL_GET_PROC(RtlZeroMemory)
199 NTDLL_GET_PROC(RtlpNtQueryValueKey)
200 NTDLL_GET_PROC(RtlOpenCurrentUser)
201 NTDLL_GET_PROC(NtWaitForSingleObject)
202
203 /* optional functions */
204 pNtQueryLicenseValue = (void *)GetProcAddress(hntdll, "NtQueryLicenseValue");
205 pNtOpenKeyEx = (void *)GetProcAddress(hntdll, "NtOpenKeyEx");
206 pNtNotifyChangeMultipleKeys = (void *)GetProcAddress(hntdll, "NtNotifyChangeMultipleKeys");
207
208 return TRUE;
209 }
210 #undef NTDLL_GET_PROC
211
QueryRoutine(IN PCWSTR ValueName,IN ULONG ValueType,IN PVOID ValueData,IN ULONG ValueLength,IN PVOID Context,IN PVOID EntryContext)212 static NTSTATUS WINAPI QueryRoutine (IN PCWSTR ValueName, IN ULONG ValueType, IN PVOID ValueData,
213 IN ULONG ValueLength, IN PVOID Context, IN PVOID EntryContext)
214 {
215 NTSTATUS ret = STATUS_SUCCESS;
216
217 trace("**Test %d**\n", CurrentTest);
218 trace("ValueName: %s\n", wine_dbgstr_w(ValueName));
219
220 switch(ValueType)
221 {
222 case REG_NONE:
223 trace("ValueType: REG_NONE\n");
224 trace("ValueData: %p\n", ValueData);
225 break;
226
227 case REG_BINARY:
228 trace("ValueType: REG_BINARY\n");
229 trace("ValueData: %p\n", ValueData);
230 break;
231
232 case REG_SZ:
233 trace("ValueType: REG_SZ\n");
234 trace("ValueData: %s\n", (char*)ValueData);
235 break;
236
237 case REG_MULTI_SZ:
238 trace("ValueType: REG_MULTI_SZ\n");
239 trace("ValueData: %s\n", (char*)ValueData);
240 break;
241
242 case REG_EXPAND_SZ:
243 trace("ValueType: REG_EXPAND_SZ\n");
244 trace("ValueData: %s\n", (char*)ValueData);
245 break;
246
247 case REG_DWORD:
248 trace("ValueType: REG_DWORD\n");
249 trace("ValueData: %p\n", ValueData);
250 break;
251 };
252 trace("ValueLength: %d\n", (int)ValueLength);
253
254 if(CurrentTest == 0)
255 ok(1, "\n"); /*checks that QueryRoutine is called*/
256 if(CurrentTest > 7)
257 ok(!1, "Invalid Test Specified!\n");
258
259 CurrentTest++;
260
261 return ret;
262 }
263
test_RtlQueryRegistryValues(void)264 static void test_RtlQueryRegistryValues(void)
265 {
266
267 /*
268 ******************************
269 * QueryTable Flags *
270 ******************************
271 *RTL_QUERY_REGISTRY_SUBKEY * Name is the name of a subkey relative to Path
272 *RTL_QUERY_REGISTRY_TOPKEY * Resets location to original RelativeTo and Path
273 *RTL_QUERY_REGISTRY_REQUIRED * Key required. returns STATUS_OBJECT_NAME_NOT_FOUND if not present
274 *RTL_QUERY_REGISTRY_NOVALUE * We just want a call-back
275 *RTL_QUERY_REGISTRY_NOEXPAND * Don't expand the variables!
276 *RTL_QUERY_REGISTRY_DIRECT * Results of query will be stored in EntryContext(QueryRoutine ignored)
277 *RTL_QUERY_REGISTRY_DELETE * Delete value key after query
278 ******************************
279
280
281 **Test layout(numbered according to CurrentTest value)**
282 0)NOVALUE Just make sure call-back works
283 1)Null Name See if QueryRoutine is called for every value in current key
284 2)SUBKEY See if we can use SUBKEY to change the current path on the fly
285 3)REQUIRED Test for value that's not there
286 4)NOEXPAND See if it will return multiple strings(no expand should split strings up)
287 5)DIRECT Make it store data directly in EntryContext and not call QueryRoutine
288 6)DefaultType Test return values when key isn't present
289 7)DefaultValue Test Default Value returned with key isn't present(and no REQUIRED flag set)
290 8)DefaultLength Test Default Length with DefaultType = REG_SZ
291 9)DefaultLength Test Default Length with DefaultType = REG_MULTI_SZ
292 10)DefaultLength Test Default Length with DefaultType = REG_EXPAND_SZ
293 11)DefaultData Test whether DefaultData is used while DefaultType = REG_NONE(shouldn't be)
294 12)Delete Try to delete value key
295
296 */
297 NTSTATUS status;
298 ULONG RelativeTo;
299
300 PRTL_QUERY_REGISTRY_TABLE QueryTable = NULL;
301 RelativeTo = RTL_REGISTRY_ABSOLUTE;/*Only using absolute - no need to test all relativeto variables*/
302
303 QueryTable = pRtlAllocateHeap(GetProcessHeap(), 0, sizeof(RTL_QUERY_REGISTRY_TABLE)*26);
304
305 pRtlZeroMemory( QueryTable, sizeof(RTL_QUERY_REGISTRY_TABLE) * 26);
306
307 QueryTable[0].QueryRoutine = QueryRoutine;
308 QueryTable[0].Flags = RTL_QUERY_REGISTRY_NOVALUE;
309 QueryTable[0].Name = NULL;
310 QueryTable[0].EntryContext = NULL;
311 QueryTable[0].DefaultType = REG_BINARY;
312 QueryTable[0].DefaultData = NULL;
313 QueryTable[0].DefaultLength = 100;
314
315 QueryTable[1].QueryRoutine = QueryRoutine;
316 QueryTable[1].Flags = 0;
317 QueryTable[1].Name = NULL;
318 QueryTable[1].EntryContext = 0;
319 QueryTable[1].DefaultType = REG_NONE;
320 QueryTable[1].DefaultData = NULL;
321 QueryTable[1].DefaultLength = 0;
322
323 QueryTable[2].QueryRoutine = NULL;
324 QueryTable[2].Flags = 0;
325 QueryTable[2].Name = NULL;
326 QueryTable[2].EntryContext = 0;
327 QueryTable[2].DefaultType = REG_NONE;
328 QueryTable[2].DefaultData = NULL;
329 QueryTable[2].DefaultLength = 0;
330
331 status = pRtlQueryRegistryValues(RelativeTo, winetestpath.Buffer, QueryTable, 0, 0);
332 ok(status == STATUS_SUCCESS, "RtlQueryRegistryValues return: 0x%08x\n", status);
333
334 pRtlFreeHeap(GetProcessHeap(), 0, QueryTable);
335 }
336
test_NtOpenKey(void)337 static void test_NtOpenKey(void)
338 {
339 HANDLE key;
340 NTSTATUS status;
341 OBJECT_ATTRIBUTES attr;
342 ACCESS_MASK am = KEY_READ;
343 UNICODE_STRING str;
344
345 /* All NULL */
346 status = pNtOpenKey(NULL, 0, NULL);
347 ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
348
349 /* NULL attributes */
350 status = pNtOpenKey(&key, 0, NULL);
351 ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
352 "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
353
354 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
355
356 /* NULL key */
357 status = pNtOpenKey(NULL, am, &attr);
358 ok(status == STATUS_ACCESS_VIOLATION, "Expected STATUS_ACCESS_VIOLATION, got: 0x%08x\n", status);
359
360 /* Length > sizeof(OBJECT_ATTRIBUTES) */
361 attr.Length *= 2;
362 status = pNtOpenKey(&key, am, &attr);
363 ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
364
365 /* Zero accessmask */
366 attr.Length = sizeof(attr);
367 key = (HANDLE)0xdeadbeef;
368 status = pNtOpenKey(&key, 0, &attr);
369 todo_wine
370 ok(status == STATUS_ACCESS_DENIED, "Expected STATUS_ACCESS_DENIED, got: 0x%08x\n", status);
371 todo_wine
372 ok(!key, "key = %p\n", key);
373 if (status == STATUS_SUCCESS) NtClose(key);
374
375 /* Calling without parent key requres full registry path. */
376 pRtlCreateUnicodeStringFromAsciiz( &str, "Machine" );
377 InitializeObjectAttributes(&attr, &str, 0, 0, 0);
378 key = (HANDLE)0xdeadbeef;
379 status = pNtOpenKey(&key, KEY_READ, &attr);
380 todo_wine ok(status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey Failed: 0x%08x\n", status);
381 todo_wine
382 ok(!key, "key = %p\n", key);
383 pRtlFreeUnicodeString( &str );
384
385 /* Open is case sensitive unless OBJ_CASE_INSENSITIVE is specified. */
386 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\Machine" );
387 status = pNtOpenKey(&key, KEY_READ, &attr);
388 todo_wine ok(status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey Failed: 0x%08x\n", status);
389
390 attr.Attributes = OBJ_CASE_INSENSITIVE;
391 status = pNtOpenKey(&key, KEY_READ, &attr);
392 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
393 pNtClose(key);
394 pRtlFreeUnicodeString( &str );
395
396 pRtlCreateUnicodeStringFromAsciiz( &str, "" );
397 status = pNtOpenKey(&key, KEY_READ, &attr);
398 todo_wine
399 ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey failed: 0x%08x\n", status );
400 pRtlFreeUnicodeString( &str );
401
402 pRtlCreateUnicodeStringFromAsciiz( &str, "\\" );
403 status = pNtOpenKey(&key, KEY_READ, &attr);
404 todo_wine
405 ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenKey failed: 0x%08x\n", status );
406 pRtlFreeUnicodeString( &str );
407
408 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry" );
409 status = pNtOpenKey(&key, KEY_READ, &attr);
410 todo_wine
411 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
412 pNtClose( key );
413 pRtlFreeUnicodeString( &str );
414
415 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\" );
416 status = pNtOpenKey(&key, KEY_READ, &attr);
417 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
418 pNtClose( key );
419 pRtlFreeUnicodeString( &str );
420
421 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar" );
422 status = pNtOpenKey(&key, KEY_READ, &attr);
423 todo_wine
424 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey failed: 0x%08x\n", status );
425 pRtlFreeUnicodeString( &str );
426
427 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar\\Machine" );
428 status = pNtOpenKey(&key, KEY_READ, &attr);
429 todo_wine
430 ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey failed: 0x%08x\n", status );
431 pRtlFreeUnicodeString( &str );
432
433 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Machine\\Software\\Classes" );
434 status = pNtOpenKey(&key, KEY_READ, &attr);
435 todo_wine
436 ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtOpenKey failed: 0x%08x\n", status );
437 pRtlFreeUnicodeString( &str );
438
439 pRtlCreateUnicodeStringFromAsciiz( &str, "Machine\\Software\\Classes" );
440 status = pNtOpenKey(&key, KEY_READ, &attr);
441 todo_wine
442 ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtOpenKey failed: 0x%08x\n", status );
443 pRtlFreeUnicodeString( &str );
444
445 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Device\\Null" );
446 status = pNtOpenKey(&key, KEY_READ, &attr);
447 todo_wine
448 ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtOpenKey failed: 0x%08x\n", status );
449 pRtlFreeUnicodeString( &str );
450
451 if (!pNtOpenKeyEx)
452 {
453 win_skip("NtOpenKeyEx not available\n");
454 return;
455 }
456
457 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
458 status = pNtOpenKeyEx(&key, KEY_WRITE|KEY_READ, &attr, 0);
459 ok(status == STATUS_SUCCESS, "NtOpenKeyEx Failed: 0x%08x\n", status);
460
461 pNtClose(key);
462 }
463
test_NtCreateKey(void)464 static void test_NtCreateKey(void)
465 {
466 /*Create WineTest*/
467 OBJECT_ATTRIBUTES attr;
468 HANDLE key, subkey;
469 ACCESS_MASK am = GENERIC_ALL;
470 NTSTATUS status;
471 UNICODE_STRING str;
472
473 /* All NULL */
474 status = pNtCreateKey(NULL, 0, NULL, 0, 0, 0, 0);
475 ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
476 "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
477
478 /* Only the key */
479 status = pNtCreateKey(&key, 0, NULL, 0, 0, 0, 0);
480 ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
481 "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
482
483 /* Only accessmask */
484 status = pNtCreateKey(NULL, am, NULL, 0, 0, 0, 0);
485 ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_INVALID_PARAMETER,
486 "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
487
488 /* Key and accessmask */
489 status = pNtCreateKey(&key, am, NULL, 0, 0, 0, 0);
490 ok(status == STATUS_ACCESS_VIOLATION /* W2K3/XP/W2K */ || status == STATUS_INVALID_PARAMETER /* NT4 */,
491 "Expected STATUS_ACCESS_VIOLATION or STATUS_INVALID_PARAMETER(NT4), got: 0x%08x\n", status);
492
493 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
494
495 /* Only attributes */
496 status = pNtCreateKey(NULL, 0, &attr, 0, 0, 0, 0);
497 ok(status == STATUS_ACCESS_VIOLATION || status == STATUS_ACCESS_DENIED /* Win7 */,
498 "Expected STATUS_ACCESS_VIOLATION or STATUS_ACCESS_DENIED, got: 0x%08x\n", status);
499
500 /* Length > sizeof(OBJECT_ATTRIBUTES) */
501 attr.Length *= 2;
502 status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
503 ok(status == STATUS_INVALID_PARAMETER, "Expected STATUS_INVALID_PARAMETER, got: 0x%08x\n", status);
504
505 attr.Length = sizeof(attr);
506 status = pNtCreateKey(&key, am, &attr, 0, 0, 0, 0);
507 ok(status == STATUS_SUCCESS, "NtCreateKey Failed: 0x%08x\n", status);
508
509 attr.RootDirectory = key;
510 attr.ObjectName = &str;
511
512 pRtlCreateUnicodeStringFromAsciiz( &str, "test\\sub\\key" );
513 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
514 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
515 pRtlFreeUnicodeString( &str );
516
517 pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey" );
518 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
519 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
520 pRtlFreeUnicodeString( &str );
521
522 pRtlCreateUnicodeStringFromAsciiz( &str, "test\\subkey\\" );
523 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
524 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
525 pRtlFreeUnicodeString( &str );
526
527 pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey\\" );
528 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
529 ok( status == STATUS_SUCCESS || broken(status == STATUS_OBJECT_NAME_NOT_FOUND), /* nt4 */
530 "NtCreateKey failed: 0x%08x\n", status );
531 if (status == STATUS_SUCCESS)
532 {
533 pNtDeleteKey( subkey );
534 pNtClose( subkey );
535 }
536 pRtlFreeUnicodeString( &str );
537
538 pRtlCreateUnicodeStringFromAsciiz( &str, "test_subkey" );
539 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
540 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
541 pRtlFreeUnicodeString( &str );
542 pNtDeleteKey( subkey );
543 pNtClose( subkey );
544
545 attr.RootDirectory = 0;
546 attr.Attributes = OBJ_CASE_INSENSITIVE;
547
548 pRtlCreateUnicodeStringFromAsciiz( &str, "" );
549 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
550 todo_wine
551 ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateKey failed: 0x%08x\n", status );
552 pRtlFreeUnicodeString( &str );
553
554 pRtlCreateUnicodeStringFromAsciiz( &str, "\\" );
555 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
556 todo_wine
557 ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateKey failed: 0x%08x\n", status );
558 pRtlFreeUnicodeString( &str );
559
560 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry" );
561 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
562 todo_wine
563 ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
564 "NtCreateKey failed: 0x%08x\n", status );
565 if (!status) pNtClose( subkey );
566 pRtlFreeUnicodeString( &str );
567
568 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\" );
569 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
570 ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
571 "NtCreateKey failed: 0x%08x\n", status );
572 if (!status) pNtClose( subkey );
573 pRtlFreeUnicodeString( &str );
574
575 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar" );
576 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
577 todo_wine
578 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
579 pRtlFreeUnicodeString( &str );
580
581 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Foobar\\Machine" );
582 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
583 todo_wine
584 ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
585 pRtlFreeUnicodeString( &str );
586
587 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Machine\\Software\\Classes" );
588 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
589 todo_wine
590 ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
591 pRtlFreeUnicodeString( &str );
592
593 pRtlCreateUnicodeStringFromAsciiz( &str, "Machine\\Software\\Classes" );
594 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
595 todo_wine
596 ok( status == STATUS_OBJECT_PATH_SYNTAX_BAD, "NtCreateKey failed: 0x%08x\n", status );
597 pRtlFreeUnicodeString( &str );
598
599 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Device\\Null" );
600 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
601 todo_wine
602 ok( status == STATUS_OBJECT_TYPE_MISMATCH, "NtCreateKey failed: 0x%08x\n", status );
603 pRtlFreeUnicodeString( &str );
604
605 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\Machine\\Software\\Classes" );
606 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
607 ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
608 "NtCreateKey failed: 0x%08x\n", status );
609 if (!status) pNtClose( subkey );
610 pRtlFreeUnicodeString( &str );
611
612 /* the REGISTRY part is case-sensitive unless OBJ_CASE_INSENSITIVE is specified */
613 attr.Attributes = 0;
614 pRtlCreateUnicodeStringFromAsciiz( &str, "\\Registry\\Machine\\Software\\Classes" );
615 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
616 todo_wine
617 ok( status == STATUS_OBJECT_PATH_NOT_FOUND, "NtCreateKey failed: 0x%08x\n", status );
618 pRtlFreeUnicodeString( &str );
619
620 pRtlCreateUnicodeStringFromAsciiz( &str, "\\REGISTRY\\Machine\\Software\\Classes" );
621 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
622 ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
623 "NtCreateKey failed: 0x%08x\n", status );
624 if (!status) pNtClose( subkey );
625 pRtlFreeUnicodeString( &str );
626
627 pRtlCreateUnicodeStringFromAsciiz( &str, "\\REGISTRY\\MACHINE\\SOFTWARE\\CLASSES" );
628 status = pNtCreateKey( &subkey, am, &attr, 0, 0, 0, 0 );
629 ok( status == STATUS_SUCCESS || status == STATUS_ACCESS_DENIED,
630 "NtCreateKey failed: 0x%08x\n", status );
631 if (!status) pNtClose( subkey );
632 pRtlFreeUnicodeString( &str );
633
634 pNtClose(key);
635 }
636
test_NtSetValueKey(void)637 static void test_NtSetValueKey(void)
638 {
639 HANDLE key;
640 NTSTATUS status;
641 OBJECT_ATTRIBUTES attr;
642 ACCESS_MASK am = KEY_WRITE;
643 UNICODE_STRING ValName;
644 DWORD data = 711;
645
646 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
647 status = pNtOpenKey(&key, am, &attr);
648 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
649
650 pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
651 status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &data, sizeof(data));
652 ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
653 pRtlFreeUnicodeString(&ValName);
654
655 pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
656 status = pNtSetValueKey(key, &ValName, 0, REG_SZ, (VOID*)stringW, STR_TRUNC_SIZE);
657 ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
658 pRtlFreeUnicodeString(&ValName);
659
660 pNtClose(key);
661 }
662
test_RtlOpenCurrentUser(void)663 static void test_RtlOpenCurrentUser(void)
664 {
665 NTSTATUS status;
666 HANDLE handle;
667 status=pRtlOpenCurrentUser(KEY_READ, &handle);
668 ok(status == STATUS_SUCCESS, "RtlOpenCurrentUser Failed: 0x%08x\n", status);
669 pNtClose(handle);
670 }
671
test_RtlCheckRegistryKey(void)672 static void test_RtlCheckRegistryKey(void)
673 {
674 NTSTATUS status;
675
676 status = pRtlCheckRegistryKey(RTL_REGISTRY_ABSOLUTE, winetestpath.Buffer);
677 ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE: 0x%08x\n", status);
678
679 status = pRtlCheckRegistryKey((RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL), winetestpath.Buffer);
680 ok(status == STATUS_SUCCESS, "RtlCheckRegistryKey with RTL_REGISTRY_ABSOLUTE and RTL_REGISTRY_OPTIONAL: 0x%08x\n", status);
681 }
682
test_NtFlushKey(void)683 static void test_NtFlushKey(void)
684 {
685 NTSTATUS status;
686 HANDLE hkey;
687 OBJECT_ATTRIBUTES attr;
688 ACCESS_MASK am = KEY_ALL_ACCESS;
689
690 status = pNtFlushKey(NULL);
691 ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
692
693 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
694 pNtOpenKey(&hkey, am, &attr);
695
696 status = pNtFlushKey(hkey);
697 ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status);
698
699 pNtClose(hkey);
700 }
701
test_NtQueryValueKey(void)702 static void test_NtQueryValueKey(void)
703 {
704 HANDLE key;
705 NTSTATUS status;
706 OBJECT_ATTRIBUTES attr;
707 UNICODE_STRING ValName;
708 KEY_VALUE_BASIC_INFORMATION *basic_info;
709 KEY_VALUE_PARTIAL_INFORMATION *partial_info, pi;
710 KEY_VALUE_FULL_INFORMATION *full_info;
711 DWORD len, expected;
712
713 pRtlCreateUnicodeStringFromAsciiz(&ValName, "deletetest");
714
715 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
716 status = pNtOpenKey(&key, KEY_READ|KEY_SET_VALUE, &attr);
717 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
718
719 len = FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[0]);
720 basic_info = HeapAlloc(GetProcessHeap(), 0, len);
721 status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len);
722 ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
723 ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex);
724 ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type);
725 ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength);
726 ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len);
727
728 basic_info = HeapReAlloc(GetProcessHeap(), 0, basic_info, len);
729 status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, basic_info, len, &len);
730 ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
731 ok(basic_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", basic_info->TitleIndex);
732 ok(basic_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", basic_info->Type);
733 ok(basic_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", basic_info->NameLength);
734 ok(len == FIELD_OFFSET(KEY_VALUE_BASIC_INFORMATION, Name[basic_info->NameLength/sizeof(WCHAR)]), "NtQueryValueKey returned wrong len %d\n", len);
735 ok(!memcmp(basic_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n");
736 HeapFree(GetProcessHeap(), 0, basic_info);
737
738 len = FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[0]);
739 partial_info = HeapAlloc(GetProcessHeap(), 0, len);
740 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
741 ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
742 ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
743 ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
744 ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
745 ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len);
746
747 partial_info = HeapReAlloc(GetProcessHeap(), 0, partial_info, len);
748 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
749 ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
750 ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
751 ok(partial_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
752 ok(partial_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
753 ok(len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data[partial_info->DataLength]), "NtQueryValueKey returned wrong len %d\n", len);
754 ok(*(DWORD *)partial_info->Data == 711, "incorrect Data returned: 0x%x\n", *(DWORD *)partial_info->Data);
755 HeapFree(GetProcessHeap(), 0, partial_info);
756
757 len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]);
758 full_info = HeapAlloc(GetProcessHeap(), 0, len);
759 status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len);
760 ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey should have returned STATUS_BUFFER_OVERFLOW instead of 0x%08x\n", status);
761 ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex);
762 ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type);
763 ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength);
764 ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength);
765 ok(len == FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength,
766 "NtQueryValueKey returned wrong len %d\n", len);
767 len = FIELD_OFFSET(KEY_VALUE_FULL_INFORMATION, Name[0]) + full_info->DataLength + full_info->NameLength;
768
769 full_info = HeapReAlloc(GetProcessHeap(), 0, full_info, len);
770 status = pNtQueryValueKey(key, &ValName, KeyValueFullInformation, full_info, len, &len);
771 ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
772 ok(full_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", full_info->TitleIndex);
773 ok(full_info->Type == REG_DWORD, "NtQueryValueKey returned wrong Type %d\n", full_info->Type);
774 ok(full_info->DataLength == 4, "NtQueryValueKey returned wrong DataLength %d\n", full_info->DataLength);
775 ok(full_info->NameLength == 20, "NtQueryValueKey returned wrong NameLength %d\n", full_info->NameLength);
776 ok(!memcmp(full_info->Name, ValName.Buffer, ValName.Length), "incorrect Name returned\n");
777 ok(*(DWORD *)((char *)full_info + full_info->DataOffset) == 711, "incorrect Data returned: 0x%x\n",
778 *(DWORD *)((char *)full_info + full_info->DataOffset));
779 HeapFree(GetProcessHeap(), 0, full_info);
780
781 pRtlFreeUnicodeString(&ValName);
782 pRtlCreateUnicodeStringFromAsciiz(&ValName, "stringtest");
783
784 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, NULL, 0, &len);
785 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey should have returned STATUS_BUFFER_TOO_SMALL instead of 0x%08x\n", status);
786 partial_info = HeapAlloc(GetProcessHeap(), 0, len+1);
787 memset(partial_info, 0xbd, len+1);
788 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, len, &len);
789 ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
790 ok(partial_info->TitleIndex == 0, "NtQueryValueKey returned wrong TitleIndex %d\n", partial_info->TitleIndex);
791 ok(partial_info->Type == REG_SZ, "NtQueryValueKey returned wrong Type %d\n", partial_info->Type);
792 ok(partial_info->DataLength == STR_TRUNC_SIZE, "NtQueryValueKey returned wrong DataLength %d\n", partial_info->DataLength);
793 ok(!memcmp(partial_info->Data, stringW, STR_TRUNC_SIZE), "incorrect Data returned\n");
794 ok(*(partial_info->Data+STR_TRUNC_SIZE) == 0xbd, "string overflowed %02x\n", *(partial_info->Data+STR_TRUNC_SIZE));
795
796 expected = len;
797 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 0, &len);
798 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
799 ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
800 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, 1, &len);
801 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
802 ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
803 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data) - 1, &len);
804 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryValueKey wrong status 0x%08x\n", status);
805 ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
806 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, partial_info, FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data), &len);
807 ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryValueKey wrong status 0x%08x\n", status);
808 ok(len == expected, "NtQueryValueKey wrong len %u\n", len);
809
810 HeapFree(GetProcessHeap(), 0, partial_info);
811 pRtlFreeUnicodeString(&ValName);
812
813 pRtlCreateUnicodeStringFromAsciiz(&ValName, "custtest");
814 status = pNtSetValueKey(key, &ValName, 0, 0xff00ff00, NULL, 0);
815 ok(status == STATUS_SUCCESS, "NtSetValueKey Failed: 0x%08x\n", status);
816
817 status = pNtQueryValueKey(key, &ValName, KeyValuePartialInformation, &pi, sizeof(pi), &len);
818 ok(status == STATUS_SUCCESS, "NtQueryValueKey should have returned STATUS_SUCCESS instead of 0x%08x\n", status);
819 ok(pi.Type == 0xff00ff00, "Type=%x\n", pi.Type);
820 ok(pi.DataLength == 0, "DataLength=%u\n", pi.DataLength);
821 pRtlFreeUnicodeString(&ValName);
822
823 pNtClose(key);
824 }
825
test_NtDeleteKey(void)826 static void test_NtDeleteKey(void)
827 {
828 NTSTATUS status;
829 HANDLE hkey;
830 OBJECT_ATTRIBUTES attr;
831 ACCESS_MASK am = KEY_ALL_ACCESS;
832
833 status = pNtDeleteKey(NULL);
834 ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
835
836 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
837 status = pNtOpenKey(&hkey, am, &attr);
838 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
839
840 status = pNtDeleteKey(hkey);
841 ok(status == STATUS_SUCCESS, "NtDeleteKey Failed: 0x%08x\n", status);
842 }
843
test_NtQueryLicenseKey(void)844 static void test_NtQueryLicenseKey(void)
845 {
846 static const WCHAR emptyW[] = {'E','M','P','T','Y',0};
847 UNICODE_STRING name;
848 WORD buffer[32];
849 NTSTATUS status;
850 ULONG type, len;
851 DWORD value;
852
853 if (!pNtQueryLicenseValue)
854 {
855 win_skip("NtQueryLicenseValue not found, skipping tests\n");
856 return;
857 }
858
859 type = 0xdead;
860 len = 0xbeef;
861 memset(&name, 0, sizeof(name));
862 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len);
863 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
864 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
865 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
866
867 /* test with empty key */
868 pRtlCreateUnicodeStringFromAsciiz(&name, "");
869
870 type = 0xdead;
871 len = 0xbeef;
872 status = pNtQueryLicenseValue(NULL, &type, buffer, sizeof(buffer), &len);
873 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
874 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
875 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
876
877 type = 0xdead;
878 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), NULL);
879 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
880 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
881
882 len = 0xbeef;
883 status = pNtQueryLicenseValue(&name, NULL, buffer, sizeof(buffer), &len);
884 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
885 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
886
887 type = 0xdead;
888 len = 0xbeef;
889 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len);
890 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
891 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
892 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
893
894 pRtlFreeUnicodeString(&name);
895
896 /* test with nonexistent licence key */
897 pRtlCreateUnicodeStringFromAsciiz(&name, "Nonexistent-License-Value");
898
899 type = 0xdead;
900 len = 0xbeef;
901 status = pNtQueryLicenseValue(NULL, &type, buffer, sizeof(buffer), &len);
902 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
903 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
904 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
905
906 type = 0xdead;
907 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), NULL);
908 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
909 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
910
911 len = 0xbeef;
912 status = pNtQueryLicenseValue(&name, NULL, buffer, sizeof(buffer), &len);
913 ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryLicenseValue returned %08x, expected STATUS_OBJECT_NAME_NOT_FOUND\n", status);
914 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
915
916 type = 0xdead;
917 len = 0xbeef;
918 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len);
919 ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryLicenseValue unexpected succeeded\n");
920 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
921 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
922
923 pRtlFreeUnicodeString(&name);
924
925 /* test with REG_SZ license key */
926 pRtlCreateUnicodeStringFromAsciiz(&name, "Kernel-MUI-Language-Allowed");
927
928 type = 0xdead;
929 len = 0xbeef;
930 status = pNtQueryLicenseValue(NULL, &type, buffer, sizeof(buffer), &len);
931 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
932 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
933 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
934
935 type = 0xdead;
936 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), NULL);
937 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
938 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
939
940 type = 0xdead;
941 len = 0;
942 status = pNtQueryLicenseValue(&name, &type, buffer, 0, &len);
943 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status);
944 ok(type == REG_SZ, "expected type = REG_SZ, got %u\n", type);
945 ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len);
946
947 len = 0;
948 status = pNtQueryLicenseValue(&name, NULL, buffer, 0, &len);
949 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status);
950 ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len);
951
952 type = 0xdead;
953 len = 0;
954 memset(buffer, 0x11, sizeof(buffer));
955 status = pNtQueryLicenseValue(&name, &type, buffer, sizeof(buffer), &len);
956 ok(status == STATUS_SUCCESS, "NtQueryLicenseValue returned %08x, expected STATUS_SUCCESS\n", status);
957 ok(type == REG_SZ, "expected type = REG_SZ, got %u\n", type);
958 ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len);
959 ok(!memcmp(buffer, emptyW, sizeof(emptyW)), "unexpected buffer content\n");
960
961 type = 0xdead;
962 len = 0;
963 memset(buffer, 0x11, sizeof(buffer));
964 status = pNtQueryLicenseValue(&name, &type, buffer, 2, &len);
965 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status);
966 ok(type == REG_SZ, "expected type REG_SZ, got %u\n", type);
967 ok(len == sizeof(emptyW), "expected len = %u, got %u\n", (DWORD)sizeof(emptyW), len);
968 ok(buffer[0] == 0x1111, "expected buffer[0] = 0x1111, got %u\n", buffer[0]);
969
970 pRtlFreeUnicodeString(&name);
971
972 /* test with REG_DWORD license key */
973 pRtlCreateUnicodeStringFromAsciiz(&name, "Kernel-MUI-Number-Allowed");
974
975 type = 0xdead;
976 len = 0xbeef;
977 status = pNtQueryLicenseValue(NULL, &type, &value, sizeof(value), &len);
978 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
979 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
980 ok(len == 0xbeef, "expected unmodified value for len, got %u\n", len);
981
982 type = 0xdead;
983 status = pNtQueryLicenseValue(&name, &type, &value, sizeof(value), NULL);
984 ok(status == STATUS_INVALID_PARAMETER, "NtQueryLicenseValue returned %08x, expected STATUS_INVALID_PARAMETER\n", status);
985 ok(type == 0xdead, "expected unmodified value for type, got %u\n", type);
986
987 type = 0xdead;
988 len = 0;
989 status = pNtQueryLicenseValue(&name, &type, &value, 0, &len);
990 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status);
991 ok(type == REG_DWORD, "expected type = REG_DWORD, got %u\n", type);
992 ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len);
993
994 len = 0;
995 status = pNtQueryLicenseValue(&name, NULL, &value, 0, &len);
996 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status);
997 ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len);
998
999 type = 0xdead;
1000 len = 0;
1001 value = 0xdeadbeef;
1002 status = pNtQueryLicenseValue(&name, &type, &value, sizeof(value), &len);
1003 ok(status == STATUS_SUCCESS, "NtQueryLicenseValue returned %08x, expected STATUS_SUCCESS\n", status);
1004 ok(type == REG_DWORD, "expected type = REG_DWORD, got %u\n", type);
1005 ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len);
1006 ok(value != 0xdeadbeef, "expected value != 0xdeadbeef\n");
1007
1008 type = 0xdead;
1009 len = 0;
1010 status = pNtQueryLicenseValue(&name, &type, &value, 2, &len);
1011 ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryLicenseValue returned %08x, expected STATUS_BUFFER_TOO_SMALL\n", status);
1012 ok(type == REG_DWORD, "expected type REG_DWORD, got %u\n", type);
1013 ok(len == sizeof(value), "expected len = %u, got %u\n", (DWORD)sizeof(value), len);
1014
1015 pRtlFreeUnicodeString(&name);
1016 }
1017
test_RtlpNtQueryValueKey(void)1018 static void test_RtlpNtQueryValueKey(void)
1019 {
1020 NTSTATUS status;
1021
1022 status = pRtlpNtQueryValueKey(NULL, NULL, NULL, NULL, NULL);
1023 ok(status == STATUS_INVALID_HANDLE, "Expected STATUS_INVALID_HANDLE, got: 0x%08x\n", status);
1024 }
1025
test_symlinks(void)1026 static void test_symlinks(void)
1027 {
1028 static const WCHAR linkW[] = {'l','i','n','k',0};
1029 static const WCHAR valueW[] = {'v','a','l','u','e',0};
1030 static const WCHAR symlinkW[] = {'S','y','m','b','o','l','i','c','L','i','n','k','V','a','l','u','e',0};
1031 static const WCHAR targetW[] = {'\\','t','a','r','g','e','t',0};
1032 static UNICODE_STRING null_str;
1033 char buffer[1024];
1034 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
1035 WCHAR *target;
1036 UNICODE_STRING symlink_str, link_str, target_str, value_str;
1037 HANDLE root, key, link;
1038 OBJECT_ATTRIBUTES attr;
1039 NTSTATUS status;
1040 DWORD target_len, len, dw;
1041
1042 pRtlInitUnicodeString( &link_str, linkW );
1043 pRtlInitUnicodeString( &symlink_str, symlinkW );
1044 pRtlInitUnicodeString( &target_str, targetW + 1 );
1045 pRtlInitUnicodeString( &value_str, valueW );
1046
1047 target_len = winetestpath.Length + sizeof(targetW);
1048 target = pRtlAllocateHeap( GetProcessHeap(), 0, target_len + sizeof(targetW) /*for loop test*/ );
1049 memcpy( target, winetestpath.Buffer, winetestpath.Length );
1050 memcpy( target + winetestpath.Length/sizeof(WCHAR), targetW, sizeof(targetW) );
1051
1052 attr.Length = sizeof(attr);
1053 attr.RootDirectory = 0;
1054 attr.Attributes = 0;
1055 attr.ObjectName = &winetestpath;
1056 attr.SecurityDescriptor = NULL;
1057 attr.SecurityQualityOfService = NULL;
1058
1059 status = pNtCreateKey( &root, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1060 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1061
1062 attr.RootDirectory = root;
1063 attr.ObjectName = &link_str;
1064 status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
1065 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1066
1067 /* REG_SZ is not allowed */
1068 status = pNtSetValueKey( link, &symlink_str, 0, REG_SZ, target, target_len );
1069 ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
1070 status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
1071 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1072 /* other values are not allowed */
1073 status = pNtSetValueKey( link, &link_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
1074 ok( status == STATUS_ACCESS_DENIED, "NtSetValueKey wrong status 0x%08x\n", status );
1075
1076 /* try opening the target through the link */
1077
1078 attr.ObjectName = &link_str;
1079 key = (HANDLE)0xdeadbeef;
1080 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1081 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
1082 ok( !key, "key = %p\n", key );
1083
1084 attr.ObjectName = &target_str;
1085 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1086 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1087
1088 dw = 0xbeef;
1089 status = pNtSetValueKey( key, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
1090 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1091 pNtClose( key );
1092
1093 attr.ObjectName = &link_str;
1094 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1095 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1096
1097 len = sizeof(buffer);
1098 status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
1099 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1100 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
1101
1102 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1103 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
1104
1105 /* REG_LINK can be created in non-link keys */
1106 status = pNtSetValueKey( key, &symlink_str, 0, REG_LINK, target, target_len - sizeof(WCHAR) );
1107 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1108 len = sizeof(buffer);
1109 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1110 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1111 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1112 "wrong len %u\n", len );
1113 status = pNtDeleteValueKey( key, &symlink_str );
1114 ok( status == STATUS_SUCCESS, "NtDeleteValueKey failed: 0x%08x\n", status );
1115
1116 pNtClose( key );
1117
1118 attr.Attributes = 0;
1119 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1120 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1121
1122 len = sizeof(buffer);
1123 status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
1124 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1125 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + sizeof(DWORD), "wrong len %u\n", len );
1126
1127 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1128 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
1129 pNtClose( key );
1130
1131 /* now open the symlink itself */
1132
1133 attr.RootDirectory = root;
1134 attr.Attributes = OBJ_OPENLINK;
1135 attr.ObjectName = &link_str;
1136 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1137 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1138
1139 len = sizeof(buffer);
1140 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1141 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1142 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1143 "wrong len %u\n", len );
1144 pNtClose( key );
1145
1146 if (pNtOpenKeyEx)
1147 {
1148 /* REG_OPTION_OPEN_LINK flag doesn't matter */
1149 status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK );
1150 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1151
1152 len = sizeof(buffer);
1153 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1154 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1155 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1156 "wrong len %u\n", len );
1157 pNtClose( key );
1158
1159 status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, 0 );
1160 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1161
1162 len = sizeof(buffer);
1163 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1164 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1165 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1166 "wrong len %u\n", len );
1167 pNtClose( key );
1168
1169 attr.Attributes = 0;
1170 status = pNtOpenKeyEx( &key, KEY_ALL_ACCESS, &attr, REG_OPTION_OPEN_LINK );
1171 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1172
1173 len = sizeof(buffer);
1174 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1175 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
1176 pNtClose( key );
1177 }
1178
1179 attr.Attributes = OBJ_OPENLINK;
1180 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1181 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1182 len = sizeof(buffer);
1183 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1184 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1185 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1186 "wrong len %u\n", len );
1187 pNtClose( key );
1188
1189 /* delete target and create by NtCreateKey on link */
1190 attr.ObjectName = &target_str;
1191 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1192 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1193 status = pNtDeleteKey( key );
1194 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1195 pNtClose( key );
1196
1197 attr.ObjectName = &link_str;
1198 attr.Attributes = 0;
1199 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1200 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
1201
1202 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1203 todo_wine ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1204 pNtClose( key );
1205 if (status) /* can be removed once todo_wine above is fixed */
1206 {
1207 attr.ObjectName = &target_str;
1208 attr.Attributes = OBJ_OPENLINK;
1209 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1210 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1211 pNtClose( key );
1212 }
1213
1214 attr.ObjectName = &target_str;
1215 attr.Attributes = OBJ_OPENLINK;
1216 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1217 ok( status == STATUS_SUCCESS, "NtOpenKey wrong status 0x%08x\n", status );
1218
1219 if (0) /* crashes the Windows kernel on some Vista systems */
1220 {
1221 /* reopen the link from itself */
1222
1223 attr.RootDirectory = link;
1224 attr.Attributes = OBJ_OPENLINK;
1225 attr.ObjectName = &null_str;
1226 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1227 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1228 len = sizeof(buffer);
1229 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1230 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1231 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1232 "wrong len %u\n", len );
1233 pNtClose( key );
1234
1235 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1236 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1237 len = sizeof(buffer);
1238 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1239 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1240 ok( len == FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION,Data) + target_len - sizeof(WCHAR),
1241 "wrong len %u\n", len );
1242 pNtClose( key );
1243 }
1244
1245 if (0) /* crashes the Windows kernel in most versions */
1246 {
1247 attr.RootDirectory = link;
1248 attr.Attributes = 0;
1249 attr.ObjectName = &null_str;
1250 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1251 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1252 len = sizeof(buffer);
1253 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1254 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
1255 pNtClose( key );
1256
1257 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1258 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1259 len = sizeof(buffer);
1260 status = pNtQueryValueKey( key, &symlink_str, KeyValuePartialInformation, info, len, &len );
1261 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey failed: 0x%08x\n", status );
1262 pNtClose( key );
1263 }
1264
1265 /* target with terminating null doesn't work */
1266 status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, target, target_len );
1267 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1268 attr.RootDirectory = root;
1269 attr.Attributes = 0;
1270 attr.ObjectName = &link_str;
1271 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1272 ok( status == STATUS_OBJECT_NAME_NOT_FOUND, "NtOpenKey wrong status 0x%08x\n", status );
1273
1274 /* relative symlink, works only on win2k */
1275 status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK, targetW+1, sizeof(targetW)-2*sizeof(WCHAR) );
1276 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1277 attr.ObjectName = &link_str;
1278 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1279 ok( status == STATUS_SUCCESS || status == STATUS_OBJECT_NAME_NOT_FOUND,
1280 "NtOpenKey wrong status 0x%08x\n", status );
1281
1282 key = (HKEY)0xdeadbeef;
1283 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, NULL );
1284 ok( status == STATUS_OBJECT_NAME_COLLISION, "NtCreateKey failed: 0x%08x\n", status );
1285 ok( !key, "key = %p\n", key );
1286
1287 status = pNtDeleteKey( link );
1288 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1289 pNtClose( link );
1290
1291 attr.ObjectName = &target_str;
1292 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1293 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1294 status = pNtDeleteKey( key );
1295 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1296 pNtClose( key );
1297
1298 /* symlink loop */
1299
1300 status = pNtCreateKey( &link, KEY_ALL_ACCESS, &attr, 0, 0, REG_OPTION_CREATE_LINK, 0 );
1301 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1302 memcpy( target + target_len/sizeof(WCHAR) - 1, targetW, sizeof(targetW) );
1303 status = pNtSetValueKey( link, &symlink_str, 0, REG_LINK,
1304 target, target_len + sizeof(targetW) - sizeof(WCHAR) );
1305 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1306
1307 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1308 ok( status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NAME_TOO_LONG,
1309 "NtOpenKey failed: 0x%08x\n", status );
1310
1311 attr.Attributes = OBJ_OPENLINK;
1312 status = pNtOpenKey( &key, KEY_ALL_ACCESS, &attr );
1313 ok( status == STATUS_SUCCESS, "NtOpenKey failed: 0x%08x\n", status );
1314 pNtClose( key );
1315
1316 status = pNtDeleteKey( link );
1317 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1318 pNtClose( link );
1319
1320 status = pNtDeleteKey( root );
1321 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1322 pNtClose( root );
1323
1324 pRtlFreeHeap(GetProcessHeap(), 0, target);
1325 }
1326
1327 static WCHAR valueW[] = {'v','a','l','u','e'};
1328 static UNICODE_STRING value_str = { sizeof(valueW), sizeof(valueW), valueW };
1329 static const DWORD ptr_size = 8 * sizeof(void*);
1330
get_key_value(HANDLE root,const char * name,DWORD flags)1331 static DWORD get_key_value( HANDLE root, const char *name, DWORD flags )
1332 {
1333 char tmp[32];
1334 NTSTATUS status;
1335 OBJECT_ATTRIBUTES attr;
1336 UNICODE_STRING str;
1337 HANDLE key;
1338 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)tmp;
1339 DWORD dw, len = sizeof(tmp);
1340
1341 attr.Length = sizeof(attr);
1342 attr.RootDirectory = root;
1343 attr.Attributes = OBJ_CASE_INSENSITIVE;
1344 attr.ObjectName = &str;
1345 attr.SecurityDescriptor = NULL;
1346 attr.SecurityQualityOfService = NULL;
1347 pRtlCreateUnicodeStringFromAsciiz( &str, name );
1348
1349 status = pNtCreateKey( &key, flags | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1350 if (status == STATUS_OBJECT_NAME_NOT_FOUND) return 0;
1351 ok( status == STATUS_SUCCESS, "%08x: NtCreateKey failed: 0x%08x\n", flags, status );
1352
1353 status = pNtQueryValueKey( key, &value_str, KeyValuePartialInformation, info, len, &len );
1354 if (status == STATUS_OBJECT_NAME_NOT_FOUND)
1355 dw = 0;
1356 else
1357 {
1358 ok( status == STATUS_SUCCESS, "%08x: NtQueryValueKey failed: 0x%08x\n", flags, status );
1359 dw = *(DWORD *)info->Data;
1360 }
1361 pNtClose( key );
1362 pRtlFreeUnicodeString( &str );
1363 return dw;
1364 }
1365
_check_key_value(int line,HANDLE root,const char * name,DWORD flags,DWORD expect)1366 static void _check_key_value( int line, HANDLE root, const char *name, DWORD flags, DWORD expect )
1367 {
1368 DWORD dw = get_key_value( root, name, flags );
1369 ok_(__FILE__,line)( dw == expect, "%08x: wrong value %u/%u\n", flags, dw, expect );
1370 }
1371 #define check_key_value(root,name,flags,expect) _check_key_value( __LINE__, root, name, flags, expect )
1372
test_redirection(void)1373 static void test_redirection(void)
1374 {
1375 static const WCHAR softwareW[] = {'\\','R','e','g','i','s','t','r','y','\\',
1376 'M','a','c','h','i','n','e','\\',
1377 'S','o','f','t','w','a','r','e',0};
1378 static const WCHAR wownodeW[] = {'\\','R','e','g','i','s','t','r','y','\\',
1379 'M','a','c','h','i','n','e','\\',
1380 'S','o','f','t','w','a','r','e','\\',
1381 'W','o','w','6','4','3','2','N','o','d','e',0};
1382 static const WCHAR wine64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
1383 'M','a','c','h','i','n','e','\\',
1384 'S','o','f','t','w','a','r','e','\\',
1385 'W','i','n','e',0};
1386 static const WCHAR wine32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
1387 'M','a','c','h','i','n','e','\\',
1388 'S','o','f','t','w','a','r','e','\\',
1389 'W','o','w','6','4','3','2','N','o','d','e','\\',
1390 'W','i','n','e',0};
1391 static const WCHAR key64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
1392 'M','a','c','h','i','n','e','\\',
1393 'S','o','f','t','w','a','r','e','\\',
1394 'W','i','n','e','\\','W','i','n','e','t','e','s','t',0};
1395 static const WCHAR key32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
1396 'M','a','c','h','i','n','e','\\',
1397 'S','o','f','t','w','a','r','e','\\',
1398 'W','o','w','6','4','3','2','N','o','d','e','\\',
1399 'W','i','n','e','\\', 'W','i','n','e','t','e','s','t',0};
1400 static const WCHAR classes64W[] = {'\\','R','e','g','i','s','t','r','y','\\',
1401 'M','a','c','h','i','n','e','\\',
1402 'S','o','f','t','w','a','r','e','\\',
1403 'C','l','a','s','s','e','s','\\',
1404 'W','i','n','e',0};
1405 static const WCHAR classes32W[] = {'\\','R','e','g','i','s','t','r','y','\\',
1406 'M','a','c','h','i','n','e','\\',
1407 'S','o','f','t','w','a','r','e','\\',
1408 'C','l','a','s','s','e','s','\\',
1409 'W','o','w','6','4','3','2','N','o','d','e','\\',
1410 'W','i','n','e',0};
1411 NTSTATUS status;
1412 OBJECT_ATTRIBUTES attr;
1413 UNICODE_STRING str;
1414 char buffer[1024];
1415 KEY_VALUE_PARTIAL_INFORMATION *info = (KEY_VALUE_PARTIAL_INFORMATION *)buffer;
1416 DWORD dw, len;
1417 HANDLE key, root32, root64, key32, key64;
1418 BOOL is_vista = FALSE;
1419
1420 if (ptr_size != 64)
1421 {
1422 ULONG is_wow64, len;
1423 if (pNtQueryInformationProcess( GetCurrentProcess(), ProcessWow64Information,
1424 &is_wow64, sizeof(is_wow64), &len ) ||
1425 !is_wow64)
1426 {
1427 trace( "Not on Wow64, no redirection\n" );
1428 return;
1429 }
1430 }
1431
1432 attr.Length = sizeof(attr);
1433 attr.RootDirectory = 0;
1434 attr.Attributes = OBJ_CASE_INSENSITIVE;
1435 attr.ObjectName = &str;
1436 attr.SecurityDescriptor = NULL;
1437 attr.SecurityQualityOfService = NULL;
1438
1439 pRtlInitUnicodeString( &str, wine64W );
1440 status = pNtCreateKey( &root64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1441 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1442
1443 pRtlInitUnicodeString( &str, wine32W );
1444 status = pNtCreateKey( &root32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1445 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1446
1447 pRtlInitUnicodeString( &str, key64W );
1448 status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1449 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1450
1451 pRtlInitUnicodeString( &str, key32W );
1452 status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1453 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1454
1455 dw = 64;
1456 status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
1457 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1458
1459 dw = 32;
1460 status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
1461 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1462
1463 len = sizeof(buffer);
1464 status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len );
1465 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1466 dw = *(DWORD *)info->Data;
1467 ok( dw == 32, "wrong value %u\n", dw );
1468
1469 len = sizeof(buffer);
1470 status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len );
1471 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1472 dw = *(DWORD *)info->Data;
1473 ok( dw == 64, "wrong value %u\n", dw );
1474
1475 pRtlInitUnicodeString( &str, softwareW );
1476 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1477 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1478
1479 if (ptr_size == 32)
1480 {
1481 /* the Vista mechanism allows opening Wow6432Node from a 32-bit key too */
1482 /* the new (and simpler) Win7 mechanism doesn't */
1483 if (get_key_value( key, "Wow6432Node\\Wine\\Winetest", 0 ) == 32)
1484 {
1485 trace( "using Vista-style Wow6432Node handling\n" );
1486 is_vista = TRUE;
1487 }
1488 check_key_value( key, "Wine\\Winetest", 0, 32 );
1489 check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1490 check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1491 check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 );
1492 check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 );
1493 check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
1494 }
1495 else
1496 {
1497 check_key_value( key, "Wine\\Winetest", 0, 64 );
1498 check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
1499 }
1500 pNtClose( key );
1501
1502 if (ptr_size == 32)
1503 {
1504 status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1505 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1506 dw = get_key_value( key, "Wine\\Winetest", 0 );
1507 ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
1508 check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, 64 );
1509 check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1510 check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, 32 );
1511 dw = get_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY );
1512 ok( dw == 32 || broken(dw == 64) /* xp64 */, "wrong value %u\n", dw );
1513 check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1514 pNtClose( key );
1515
1516 status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1517 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1518 check_key_value( key, "Wine\\Winetest", 0, 32 );
1519 check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1520 check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1521 check_key_value( key, "Wow6432Node\\Wine\\Winetest", 0, is_vista ? 32 : 0 );
1522 check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 0 );
1523 check_key_value( key, "Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, is_vista ? 32 : 0 );
1524 pNtClose( key );
1525 }
1526
1527 check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", 0, ptr_size );
1528 check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", 0, 32 );
1529 if (ptr_size == 64)
1530 {
1531 /* KEY_WOW64 flags have no effect on 64-bit */
1532 check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 );
1533 check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 64 );
1534 check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, 32 );
1535 check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1536 }
1537 else
1538 {
1539 check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_64KEY, 64 );
1540 check_key_value( 0, "\\Registry\\Machine\\Software\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1541 check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1542 check_key_value( 0, "\\Registry\\Machine\\Software\\Wow6432Node\\Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1543 }
1544
1545 pRtlInitUnicodeString( &str, wownodeW );
1546 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1547 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1548 check_key_value( key, "Wine\\Winetest", 0, 32 );
1549 check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, (ptr_size == 64) ? 32 : (is_vista ? 64 : 32) );
1550 check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1551 pNtClose( key );
1552
1553 if (ptr_size == 32)
1554 {
1555 status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1556 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1557 dw = get_key_value( key, "Wine\\Winetest", 0 );
1558 ok( dw == (is_vista ? 64 : 32) || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
1559 check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1560 check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1561 pNtClose( key );
1562
1563 status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1564 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1565 check_key_value( key, "Wine\\Winetest", 0, 32 );
1566 check_key_value( key, "Wine\\Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1567 check_key_value( key, "Wine\\Winetest", KEY_WOW64_32KEY, 32 );
1568 pNtClose( key );
1569 }
1570
1571 pRtlInitUnicodeString( &str, wine32W );
1572 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1573 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1574 check_key_value( key, "Winetest", 0, 32 );
1575 check_key_value( key, "Winetest", KEY_WOW64_64KEY, (ptr_size == 32 && is_vista) ? 64 : 32 );
1576 check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
1577 pNtClose( key );
1578
1579 if (ptr_size == 32)
1580 {
1581 status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1582 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1583 dw = get_key_value( key, "Winetest", 0 );
1584 ok( dw == 32 || (is_vista && dw == 64), "wrong value %u\n", dw );
1585 check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1586 check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
1587 pNtClose( key );
1588
1589 status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1590 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1591 check_key_value( key, "Winetest", 0, 32 );
1592 check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1593 check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
1594 pNtClose( key );
1595 }
1596
1597 pRtlInitUnicodeString( &str, wine64W );
1598 status = pNtCreateKey( &key, KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1599 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1600 check_key_value( key, "Winetest", 0, ptr_size );
1601 check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : ptr_size );
1602 check_key_value( key, "Winetest", KEY_WOW64_32KEY, ptr_size );
1603 pNtClose( key );
1604
1605 if (ptr_size == 32)
1606 {
1607 status = pNtCreateKey( &key, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1608 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1609 dw = get_key_value( key, "Winetest", 0 );
1610 ok( dw == 64 || broken(dw == 32) /* xp64 */, "wrong value %u\n", dw );
1611 check_key_value( key, "Winetest", KEY_WOW64_64KEY, 64 );
1612 dw = get_key_value( key, "Winetest", KEY_WOW64_32KEY );
1613 todo_wine ok( dw == 32, "wrong value %u\n", dw );
1614 pNtClose( key );
1615
1616 status = pNtCreateKey( &key, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1617 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1618 check_key_value( key, "Winetest", 0, 32 );
1619 check_key_value( key, "Winetest", KEY_WOW64_64KEY, is_vista ? 64 : 32 );
1620 check_key_value( key, "Winetest", KEY_WOW64_32KEY, 32 );
1621 pNtClose( key );
1622 }
1623
1624 status = pNtDeleteKey( key32 );
1625 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1626 pNtClose( key32 );
1627
1628 status = pNtDeleteKey( key64 );
1629 ok( status == STATUS_SUCCESS, "NtDeleteKey failed: 0x%08x\n", status );
1630 pNtClose( key64 );
1631
1632 pNtDeleteKey( root32 );
1633 pNtClose( root32 );
1634 pNtDeleteKey( root64 );
1635 pNtClose( root64 );
1636
1637 /* Software\Classes is shared/reflected so behavior is different */
1638
1639 pRtlInitUnicodeString( &str, classes64W );
1640 status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1641 if (status == STATUS_ACCESS_DENIED)
1642 {
1643 skip("Not authorized to modify the Classes key\n");
1644 return;
1645 }
1646 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1647
1648 pRtlInitUnicodeString( &str, classes32W );
1649 status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1650 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1651
1652 dw = 64;
1653 status = pNtSetValueKey( key64, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
1654 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1655 pNtClose( key64 );
1656
1657 dw = 32;
1658 status = pNtSetValueKey( key32, &value_str, 0, REG_DWORD, &dw, sizeof(dw) );
1659 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1660 pNtClose( key32 );
1661
1662 pRtlInitUnicodeString( &str, classes64W );
1663 status = pNtCreateKey( &key64, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1664 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1665 len = sizeof(buffer);
1666 status = pNtQueryValueKey( key64, &value_str, KeyValuePartialInformation, info, len, &len );
1667 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1668 dw = *(DWORD *)info->Data;
1669 ok( dw == ptr_size, "wrong value %u\n", dw );
1670
1671 pRtlInitUnicodeString( &str, classes32W );
1672 status = pNtCreateKey( &key32, KEY_WOW64_32KEY | KEY_ALL_ACCESS, &attr, 0, 0, 0, 0 );
1673 ok( status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status );
1674 len = sizeof(buffer);
1675 status = pNtQueryValueKey( key32, &value_str, KeyValuePartialInformation, info, len, &len );
1676 ok( status == STATUS_SUCCESS, "NtQueryValueKey failed: 0x%08x\n", status );
1677 dw = *(DWORD *)info->Data;
1678 ok( dw == 32, "wrong value %u\n", dw );
1679
1680 pNtDeleteKey( key32 );
1681 pNtClose( key32 );
1682 pNtDeleteKey( key64 );
1683 pNtClose( key64 );
1684 }
1685
test_long_value_name(void)1686 static void test_long_value_name(void)
1687 {
1688 HANDLE key;
1689 NTSTATUS status, expected;
1690 OBJECT_ATTRIBUTES attr;
1691 UNICODE_STRING ValName;
1692 DWORD i;
1693
1694 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
1695 status = pNtOpenKey(&key, KEY_WRITE|KEY_READ, &attr);
1696 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
1697
1698 ValName.MaximumLength = 0xfffc;
1699 ValName.Length = ValName.MaximumLength - sizeof(WCHAR);
1700 ValName.Buffer = HeapAlloc(GetProcessHeap(), 0, ValName.MaximumLength);
1701 for (i = 0; i < ValName.Length / sizeof(WCHAR); i++)
1702 ValName.Buffer[i] = 'a';
1703 ValName.Buffer[i] = 0;
1704
1705 status = pNtDeleteValueKey(key, &ValName);
1706 ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtDeleteValueKey with nonexistent long value name returned 0x%08x\n", status);
1707 status = pNtSetValueKey(key, &ValName, 0, REG_DWORD, &i, sizeof(i));
1708 ok(status == STATUS_INVALID_PARAMETER || broken(status == STATUS_SUCCESS) /* nt4 */,
1709 "NtSetValueKey with long value name returned 0x%08x\n", status);
1710 expected = (status == STATUS_SUCCESS) ? STATUS_SUCCESS : STATUS_OBJECT_NAME_NOT_FOUND;
1711 status = pNtDeleteValueKey(key, &ValName);
1712 ok(status == expected, "NtDeleteValueKey with long value name returned 0x%08x\n", status);
1713
1714 status = pNtQueryValueKey(key, &ValName, KeyValueBasicInformation, NULL, 0, &i);
1715 ok(status == STATUS_OBJECT_NAME_NOT_FOUND, "NtQueryValueKey with nonexistent long value name returned 0x%08x\n", status);
1716
1717 pRtlFreeUnicodeString(&ValName);
1718 pNtClose(key);
1719 }
1720
test_NtQueryKey(void)1721 static void test_NtQueryKey(void)
1722 {
1723 HANDLE key, subkey, subkey2;
1724 NTSTATUS status;
1725 OBJECT_ATTRIBUTES attr;
1726 ULONG length, len;
1727 KEY_NAME_INFORMATION *info = NULL;
1728 KEY_CACHED_INFORMATION cached_info;
1729 UNICODE_STRING str;
1730 DWORD dw;
1731
1732 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
1733 status = pNtOpenKey(&key, KEY_READ, &attr);
1734 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
1735
1736 status = pNtQueryKey(key, KeyNameInformation, NULL, 0, &length);
1737 if (status == STATUS_INVALID_PARAMETER) {
1738 win_skip("KeyNameInformation is not supported\n");
1739 pNtClose(key);
1740 return;
1741 }
1742 todo_wine ok(status == STATUS_BUFFER_TOO_SMALL, "NtQueryKey Failed: 0x%08x\n", status);
1743 info = HeapAlloc(GetProcessHeap(), 0, length);
1744
1745 /* non-zero buffer size, but insufficient */
1746 status = pNtQueryKey(key, KeyNameInformation, info, sizeof(*info), &len);
1747 ok(status == STATUS_BUFFER_OVERFLOW, "NtQueryKey Failed: 0x%08x\n", status);
1748 ok(length == len, "got %d, expected %d\n", len, length);
1749 ok(info->NameLength == winetestpath.Length, "got %d, expected %d\n",
1750 info->NameLength, winetestpath.Length);
1751
1752 /* correct buffer size */
1753 status = pNtQueryKey(key, KeyNameInformation, info, length, &len);
1754 ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08x\n", status);
1755 ok(length == len, "got %d, expected %d\n", len, length);
1756
1757 str.Buffer = info->Name;
1758 str.Length = info->NameLength;
1759 ok(pRtlCompareUnicodeString(&winetestpath, &str, TRUE) == 0,
1760 "got %s, expected %s\n",
1761 wine_dbgstr_wn(str.Buffer, str.Length/sizeof(WCHAR)),
1762 wine_dbgstr_wn(winetestpath.Buffer, winetestpath.Length/sizeof(WCHAR)));
1763
1764 HeapFree(GetProcessHeap(), 0, info);
1765
1766 attr.RootDirectory = key;
1767 attr.ObjectName = &str;
1768 pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey");
1769 status = pNtCreateKey(&subkey, GENERIC_ALL, &attr, 0, 0, 0, 0);
1770 ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status);
1771 pRtlFreeUnicodeString(&str);
1772
1773 status = pNtQueryKey(subkey, KeyCachedInformation, &cached_info, sizeof(cached_info), &len);
1774 ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08x\n", status);
1775
1776 if (status == STATUS_SUCCESS)
1777 {
1778 ok(len == sizeof(cached_info), "got unexpected length %d\n", len);
1779 ok(cached_info.SubKeys == 0, "cached_info.SubKeys = %u\n", cached_info.SubKeys);
1780 ok(cached_info.MaxNameLen == 0, "cached_info.MaxNameLen = %u\n", cached_info.MaxNameLen);
1781 ok(cached_info.Values == 0, "cached_info.Values = %u\n", cached_info.Values);
1782 ok(cached_info.MaxValueNameLen == 0, "cached_info.MaxValueNameLen = %u\n", cached_info.MaxValueNameLen);
1783 ok(cached_info.MaxValueDataLen == 0, "cached_info.MaxValueDataLen = %u\n", cached_info.MaxValueDataLen);
1784 ok(cached_info.NameLength == 22, "cached_info.NameLength = %u\n", cached_info.NameLength);
1785 }
1786
1787 attr.RootDirectory = subkey;
1788 attr.ObjectName = &str;
1789 pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey2");
1790 status = pNtCreateKey(&subkey2, GENERIC_ALL, &attr, 0, 0, 0, 0);
1791 ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status);
1792 pRtlFreeUnicodeString(&str);
1793
1794 pRtlCreateUnicodeStringFromAsciiz(&str, "val");
1795 dw = 64;
1796 status = pNtSetValueKey( subkey, &str, 0, REG_DWORD, &dw, sizeof(dw) );
1797 ok( status == STATUS_SUCCESS, "NtSetValueKey failed: 0x%08x\n", status );
1798 pRtlFreeUnicodeString(&str);
1799
1800 status = pNtQueryKey(subkey, KeyCachedInformation, &cached_info, sizeof(cached_info), &len);
1801 ok(status == STATUS_SUCCESS, "NtQueryKey Failed: 0x%08x\n", status);
1802
1803 if (status == STATUS_SUCCESS)
1804 {
1805 ok(len == sizeof(cached_info), "got unexpected length %d\n", len);
1806 ok(cached_info.SubKeys == 1, "cached_info.SubKeys = %u\n", cached_info.SubKeys);
1807 ok(cached_info.MaxNameLen == 24, "cached_info.MaxNameLen = %u\n", cached_info.MaxNameLen);
1808 ok(cached_info.Values == 1, "cached_info.Values = %u\n", cached_info.Values);
1809 ok(cached_info.MaxValueNameLen == 6, "cached_info.MaxValueNameLen = %u\n", cached_info.MaxValueNameLen);
1810 ok(cached_info.MaxValueDataLen == 4, "cached_info.MaxValueDataLen = %u\n", cached_info.MaxValueDataLen);
1811 ok(cached_info.NameLength == 22, "cached_info.NameLength = %u\n", cached_info.NameLength);
1812 }
1813
1814 status = pNtDeleteKey(subkey2);
1815 ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status);
1816 status = pNtDeleteKey(subkey);
1817 ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status);
1818
1819 pNtClose(subkey2);
1820 pNtClose(subkey);
1821 pNtClose(key);
1822 }
1823
test_notify(void)1824 static void test_notify(void)
1825 {
1826 OBJECT_ATTRIBUTES attr;
1827 LARGE_INTEGER timeout;
1828 IO_STATUS_BLOCK iosb;
1829 UNICODE_STRING str;
1830 HANDLE key, events[2], subkey;
1831 NTSTATUS status;
1832
1833 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
1834 status = pNtOpenKey(&key, KEY_ALL_ACCESS, &attr);
1835 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
1836
1837 events[0] = CreateEventW(NULL, FALSE, TRUE, NULL);
1838 ok(events[0] != NULL, "CreateEvent failed: %u\n", GetLastError());
1839 events[1] = CreateEventW(NULL, FALSE, TRUE, NULL);
1840 ok(events[1] != NULL, "CreateEvent failed: %u\n", GetLastError());
1841
1842 status = pNtNotifyChangeKey(key, events[0], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE);
1843 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1844 status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE);
1845 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1846
1847 timeout.QuadPart = 0;
1848 status = pNtWaitForSingleObject(events[0], FALSE, &timeout);
1849 ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %x\n", status);
1850 status = pNtWaitForSingleObject(events[1], FALSE, &timeout);
1851 ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %x\n", status);
1852
1853 attr.RootDirectory = key;
1854 attr.ObjectName = &str;
1855
1856 pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey");
1857 status = pNtCreateKey(&subkey, GENERIC_ALL, &attr, 0, 0, 0, 0);
1858 ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status);
1859 pRtlFreeUnicodeString(&str);
1860
1861 status = pNtWaitForSingleObject(events[0], FALSE, &timeout);
1862 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1863 status = pNtWaitForSingleObject(events[1], FALSE, &timeout);
1864 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1865
1866 status = pNtNotifyChangeKey(key, events[0], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE);
1867 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1868 status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE);
1869 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1870
1871 status = pNtDeleteKey(subkey);
1872 ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status);
1873
1874 status = pNtWaitForSingleObject(events[0], FALSE, &timeout);
1875 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1876 status = pNtWaitForSingleObject(events[1], FALSE, &timeout);
1877 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1878
1879 pNtClose(subkey);
1880
1881 status = pNtNotifyChangeKey(key, events[0], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE);
1882 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1883 status = pNtNotifyChangeKey(key, events[1], NULL, NULL, &iosb, 0, FALSE, NULL, 0, TRUE);
1884 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1885
1886 pNtClose(key);
1887
1888 status = pNtWaitForSingleObject(events[0], FALSE, &timeout);
1889 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1890 status = pNtWaitForSingleObject(events[1], FALSE, &timeout);
1891 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1892
1893 if (pNtNotifyChangeMultipleKeys)
1894 {
1895 InitializeObjectAttributes(&attr, &winetestpath, 0, 0, 0);
1896 status = pNtOpenKey(&key, KEY_ALL_ACCESS, &attr);
1897 ok(status == STATUS_SUCCESS, "NtOpenKey Failed: 0x%08x\n", status);
1898
1899 status = pNtNotifyChangeMultipleKeys(key, 0, NULL, events[0], NULL, NULL, &iosb, REG_NOTIFY_CHANGE_NAME, FALSE, NULL, 0, TRUE);
1900 ok(status == STATUS_PENDING, "NtNotifyChangeKey returned %x\n", status);
1901
1902 timeout.QuadPart = 0;
1903 status = pNtWaitForSingleObject(events[0], FALSE, &timeout);
1904 ok(status == STATUS_TIMEOUT, "NtWaitForSingleObject returned %x\n", status);
1905
1906 attr.RootDirectory = key;
1907 attr.ObjectName = &str;
1908 pRtlCreateUnicodeStringFromAsciiz(&str, "test_subkey");
1909 status = pNtCreateKey(&subkey, GENERIC_ALL, &attr, 0, 0, 0, 0);
1910 ok(status == STATUS_SUCCESS, "NtCreateKey failed: 0x%08x\n", status);
1911 pRtlFreeUnicodeString(&str);
1912
1913 status = pNtWaitForSingleObject(events[0], FALSE, &timeout);
1914 ok(status == STATUS_SUCCESS, "NtWaitForSingleObject returned %x\n", status);
1915
1916 status = pNtDeleteKey(subkey);
1917 ok(status == STATUS_SUCCESS, "NtDeleteSubkey failed: %x\n", status);
1918 pNtClose(subkey);
1919 pNtClose(key);
1920 }
1921 else
1922 {
1923 win_skip("NtNotifyChangeMultipleKeys not available\n");
1924 }
1925
1926 pNtClose(events[0]);
1927 pNtClose(events[1]);
1928 }
1929
START_TEST(reg)1930 START_TEST(reg)
1931 {
1932 static const WCHAR winetest[] = {'\\','W','i','n','e','T','e','s','t',0};
1933 if(!InitFunctionPtrs())
1934 return;
1935 pRtlFormatCurrentUserKeyPath(&winetestpath);
1936 winetestpath.Buffer = pRtlReAllocateHeap(GetProcessHeap(), HEAP_ZERO_MEMORY, winetestpath.Buffer,
1937 winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR));
1938 winetestpath.MaximumLength = winetestpath.MaximumLength + sizeof(winetest)*sizeof(WCHAR);
1939
1940 pRtlAppendUnicodeToString(&winetestpath, winetest);
1941
1942 test_NtCreateKey();
1943 test_NtOpenKey();
1944 test_NtSetValueKey();
1945 test_RtlCheckRegistryKey();
1946 test_RtlOpenCurrentUser();
1947 test_RtlQueryRegistryValues();
1948 test_RtlpNtQueryValueKey();
1949 test_NtFlushKey();
1950 test_NtQueryKey();
1951 test_NtQueryLicenseKey();
1952 test_NtQueryValueKey();
1953 test_long_value_name();
1954 test_notify();
1955 test_NtDeleteKey();
1956 test_symlinks();
1957 test_redirection();
1958
1959 pRtlFreeUnicodeString(&winetestpath);
1960
1961 FreeLibrary(hntdll);
1962 }
1963