1 /*
2  * comctl32 MRU unit tests
3  *
4  * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
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 #include <stdarg.h>
21 
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wingdi.h"
25 #include "winuser.h"
26 #include "winnls.h"
27 #include "winreg.h"
28 #include "commctrl.h"
29 #include "shlwapi.h"
30 
31 #include "wine/heap.h"
32 #include "wine/test.h"
33 
34 /* Keys for testing MRU functions */
35 #define REG_TEST_BASEKEYA    "Software\\Wine"
36 #define REG_TEST_BASESUBKEYA "Test"
37 #define REG_TEST_KEYA    REG_TEST_BASEKEYA "\\" REG_TEST_BASESUBKEYA
38 #define REG_TEST_SUBKEYA "MRUTest"
39 #define REG_TEST_FULLKEY REG_TEST_KEYA "\\" REG_TEST_SUBKEYA
40 
41 /* Undocumented MRU functions */
42 typedef struct tagMRUINFOA
43 {
44     DWORD   cbSize;
45     UINT    uMax;
46     UINT    fFlags;
47     HKEY    hKey;
48     LPCSTR  lpszSubKey;
49     int (CALLBACK *lpfnCompare)(LPCSTR, LPCSTR);
50 } MRUINFOA;
51 
52 typedef struct tagMRUINFOW
53 {
54     DWORD   cbSize;
55     UINT    uMax;
56     UINT    fFlags;
57     HKEY    hKey;
58     LPCWSTR lpszSubKey;
59     int (CALLBACK *lpfnCompare)(LPCWSTR, LPCWSTR);
60 } MRUINFOW;
61 
62 #define MRU_STRING     0  /* this one's invented */
63 #define MRU_BINARY     1
64 #define MRU_CACHEWRITE 2
65 
66 #define LIST_SIZE 3 /* Max entries for each mru */
67 
68 static HMODULE hComctl32;
69 static HANDLE (WINAPI *pCreateMRUListA)(MRUINFOA*);
70 static void   (WINAPI *pFreeMRUList)(HANDLE);
71 static INT    (WINAPI *pAddMRUStringA)(HANDLE,LPCSTR);
72 static INT    (WINAPI *pEnumMRUListA)(HANDLE,INT,LPVOID,DWORD);
73 static INT    (WINAPI *pEnumMRUListW)(HANDLE,INT,LPVOID,DWORD);
74 static HANDLE (WINAPI *pCreateMRUListLazyA)(MRUINFOA*, DWORD, DWORD, DWORD);
75 static HANDLE (WINAPI *pCreateMRUListLazyW)(MRUINFOW*, DWORD, DWORD, DWORD);
76 static INT    (WINAPI *pFindMRUData)(HANDLE, LPCVOID, DWORD, LPINT);
77 static INT    (WINAPI *pAddMRUData)(HANDLE, LPCVOID, DWORD);
78 static HANDLE (WINAPI *pCreateMRUListW)(MRUINFOW*);
79 
80 static void init_functions(void)
81 {
82     hComctl32 = LoadLibraryA("comctl32.dll");
83 
84 #define X2(f, ord) p##f = (void*)GetProcAddress(hComctl32, (const char *)ord);
85     X2(CreateMRUListA, 151);
86     X2(FreeMRUList, 152);
87     X2(AddMRUStringA, 153);
88     X2(EnumMRUListA, 154);
89     X2(CreateMRUListLazyA, 157);
90     X2(AddMRUData, 167);
91     X2(FindMRUData, 169);
92     X2(CreateMRUListW, 400);
93     X2(EnumMRUListW, 403);
94     X2(CreateMRUListLazyW, 404);
95 #undef X2
96 }
97 
98 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
99 static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
100 {
101     LONG ret;
102     DWORD dwMaxSubkeyLen, dwMaxValueLen;
103     DWORD dwMaxLen, dwSize;
104     CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
105     HKEY hSubKey = hKey;
106 
107     if(lpszSubKey)
108     {
109         ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
110         if (ret) return ret;
111     }
112 
113     /* Get highest length for keys, values */
114     ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
115             &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
116     if (ret) goto cleanup;
117 
118     dwMaxSubkeyLen++;
119     dwMaxValueLen++;
120     dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
121     if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
122     {
123         /* Name too big: alloc a buffer for it */
124         if (!(lpszName = heap_alloc(dwMaxLen * sizeof(CHAR))))
125         {
126             ret = ERROR_NOT_ENOUGH_MEMORY;
127             goto cleanup;
128         }
129     }
130 
131 
132     /* Recursively delete all the subkeys */
133     while (TRUE)
134     {
135         dwSize = dwMaxLen;
136         if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
137                           NULL, NULL, NULL)) break;
138 
139         ret = mru_RegDeleteTreeA(hSubKey, lpszName);
140         if (ret) goto cleanup;
141     }
142 
143     if (lpszSubKey)
144         ret = RegDeleteKeyA(hKey, lpszSubKey);
145     else
146         while (TRUE)
147         {
148             dwSize = dwMaxLen;
149             if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
150                   NULL, NULL, NULL, NULL)) break;
151 
152             ret = RegDeleteValueA(hKey, lpszName);
153             if (ret) goto cleanup;
154         }
155 
156 cleanup:
157     /* Free buffer if allocated */
158     if (lpszName != szNameBuf)
159         heap_free(lpszName);
160     if(lpszSubKey)
161         RegCloseKey(hSubKey);
162     return ret;
163 }
164 
165 static BOOL create_reg_entries(void)
166 {
167     HKEY hKey = NULL;
168 
169     ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
170        "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
171     if (!hKey) return FALSE;
172     RegCloseKey(hKey);
173     return TRUE;
174 }
175 
176 static void delete_reg_entries(void)
177 {
178     HKEY hKey;
179 
180     if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
181                       &hKey))
182         return;
183     mru_RegDeleteTreeA(hKey, REG_TEST_BASESUBKEYA);
184     RegCloseKey(hKey);
185 }
186 
187 static void check_reg_entries(const char *mrulist, const char**items)
188 {
189     char buff[128];
190     HKEY hKey = NULL;
191     DWORD type, size, ret;
192     unsigned int i;
193 
194     ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
195        "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
196     if (!hKey) return;
197 
198     type = REG_SZ;
199     size = sizeof(buff);
200     buff[0] = '\0';
201     ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);
202 
203     ok(!ret && buff[0], "Checking MRU: got %d from RegQueryValueExW\n", ret);
204     if(ret || !buff[0]) return;
205 
206     ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
207        mrulist, buff);
208     if(strcmp(buff, mrulist)) return;
209 
210     for (i = 0; i < strlen(mrulist); i++)
211     {
212         char name[2];
213         name[0] = mrulist[i];
214         name[1] = '\0';
215         type = REG_SZ;
216         size = sizeof(buff);
217         buff[0] = '\0';
218         ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
219         ok(!ret && buff[0],
220            "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
221            i, mrulist[i], ret);
222         if(ret || !buff[0]) return;
223         ok(!strcmp(buff, items[mrulist[i]-'a']),
224            "Checking MRU item %d ('%c'): expected \"%s\", got \"%s\"\n",
225            i, mrulist[i], buff, items[mrulist[i] - 'a']);
226     }
227 }
228 
229 static int CALLBACK cmp_mru_strA(LPCSTR data1, LPCSTR data2)
230 {
231     return lstrcmpiA(data1, data2);
232 }
233 
234 static void test_MRUListA(void)
235 {
236     const char *checks[LIST_SIZE+1];
237     MRUINFOA infoA;
238     HANDLE hMRU;
239     HKEY hKey;
240     INT iRet;
241 
242     if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUListA)
243     {
244         win_skip("MRU entry points not found\n");
245         return;
246     }
247 
248     if (0)
249     {
250     /* Create (NULL) - crashes native */
251     hMRU = pCreateMRUListA(NULL);
252     }
253 
254     /* size too small */
255     infoA.cbSize = sizeof(infoA) - 2;
256     infoA.uMax = LIST_SIZE;
257     infoA.fFlags = MRU_STRING;
258     infoA.hKey = NULL;
259     infoA.lpszSubKey = REG_TEST_SUBKEYA;
260     infoA.lpfnCompare = cmp_mru_strA;
261 
262     SetLastError(0);
263     hMRU = pCreateMRUListA(&infoA);
264     ok (!hMRU && !GetLastError(),
265         "CreateMRUListA(too small) expected NULL,0 got %p,%d\n",
266         hMRU, GetLastError());
267 
268     /* size too big */
269     infoA.cbSize = sizeof(infoA) + 2;
270     infoA.uMax = LIST_SIZE;
271     infoA.fFlags = MRU_STRING;
272     infoA.hKey = NULL;
273     infoA.lpszSubKey = REG_TEST_SUBKEYA;
274     infoA.lpfnCompare = cmp_mru_strA;
275 
276     SetLastError(0);
277     hMRU = pCreateMRUListA(&infoA);
278     ok (!hMRU && !GetLastError(),
279         "CreateMRUListA(too big) expected NULL,0 got %p,%d\n",
280         hMRU, GetLastError());
281 
282     /* NULL hKey */
283     infoA.cbSize = sizeof(infoA);
284     infoA.uMax = LIST_SIZE;
285     infoA.fFlags = MRU_STRING;
286     infoA.hKey = NULL;
287     infoA.lpszSubKey = REG_TEST_SUBKEYA;
288     infoA.lpfnCompare = cmp_mru_strA;
289 
290     SetLastError(0);
291     hMRU = pCreateMRUListA(&infoA);
292     ok (!hMRU && !GetLastError(),
293         "CreateMRUListA(NULL key) expected NULL,0 got %p,%d\n",
294         hMRU, GetLastError());
295 
296     /* NULL subkey name */
297     infoA.cbSize = sizeof(infoA);
298     infoA.uMax = LIST_SIZE;
299     infoA.fFlags = MRU_STRING;
300     infoA.hKey = NULL;
301     infoA.lpszSubKey = NULL;
302     infoA.lpfnCompare = cmp_mru_strA;
303 
304     SetLastError(0);
305     hMRU = pCreateMRUListA(&infoA);
306     ok (!hMRU && !GetLastError(),
307         "CreateMRUListA(NULL name) expected NULL,0 got %p,%d\n",
308         hMRU, GetLastError());
309 
310     /* Create a string MRU */
311     ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
312        "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
313     if (!hKey) return;
314 
315     infoA.cbSize = sizeof(infoA);
316     infoA.uMax = LIST_SIZE;
317     infoA.fFlags = MRU_STRING;
318     infoA.hKey = hKey;
319     infoA.lpszSubKey = REG_TEST_SUBKEYA;
320     infoA.lpfnCompare = cmp_mru_strA;
321 
322     hMRU = pCreateMRUListA(&infoA);
323     ok(hMRU && !GetLastError(),
324        "CreateMRUListA(string) expected non-NULL,0 got %p,%d\n",
325        hMRU, GetLastError());
326 
327     if (hMRU)
328     {
329         char buffer[255];
330         checks[0] = "Test 1";
331         checks[1] = "Test 2";
332         checks[2] = "Test 3";
333         checks[3] = "Test 4";
334 
335         /* Add (NULL list) */
336         SetLastError(0);
337         iRet = pAddMRUStringA(NULL, checks[0]);
338         ok(iRet == -1 && !GetLastError(),
339            "AddMRUStringA(NULL list) expected -1,0 got %d,%d\n",
340            iRet, GetLastError());
341 
342         /* Add (NULL string) */
343         if (0)
344         {
345 	/* Some native versions crash when passed NULL or fail to SetLastError()  */
346         SetLastError(0);
347         iRet = pAddMRUStringA(hMRU, NULL);
348         ok(iRet == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
349            "AddMRUStringA(NULL str) expected 0,ERROR_INVALID_PARAMETER got %d,%d\n",
350            iRet, GetLastError());
351         }
352 
353         /* Add 3 strings. Check the registry is correct after each add */
354         SetLastError(0);
355         iRet = pAddMRUStringA(hMRU, checks[0]);
356         ok(iRet == 0 && !GetLastError(),
357            "AddMRUStringA(1) expected 0,0 got %d,%d\n",
358            iRet, GetLastError());
359         check_reg_entries("a", checks);
360 
361         SetLastError(0);
362         iRet = pAddMRUStringA(hMRU, checks[1]);
363         ok(iRet == 1 && !GetLastError(),
364            "AddMRUStringA(2) expected 1,0 got %d,%d\n",
365            iRet, GetLastError());
366         check_reg_entries("ba", checks);
367 
368         SetLastError(0);
369         iRet = pAddMRUStringA(hMRU, checks[2]);
370         ok(iRet == 2 && !GetLastError(),
371            "AddMRUStringA(2) expected 2,0 got %d,%d\n",
372            iRet, GetLastError());
373         check_reg_entries("cba", checks);
374 
375         /* Add a duplicate of the 2nd string - it should move to the front,
376          * but keep the same index in the registry.
377          */
378         SetLastError(0);
379         iRet = pAddMRUStringA(hMRU, checks[1]);
380         ok(iRet == 1 && !GetLastError(),
381            "AddMRUStringA(re-add 1) expected 1,0 got %d,%d\n",
382            iRet, GetLastError());
383         check_reg_entries("bca", checks);
384 
385         /* Add a new string - replaces the oldest string + moves to the front */
386         SetLastError(0);
387         iRet = pAddMRUStringA(hMRU, checks[3]);
388         ok(iRet == 0 && !GetLastError(),
389            "AddMRUStringA(add new) expected 0,0 got %d,%d\n",
390            iRet, GetLastError());
391         checks[0] = checks[3];
392         check_reg_entries("abc", checks);
393 
394         /* NULL buffer = get list size */
395         iRet = pEnumMRUListA(hMRU, 0, NULL, 0);
396         ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
397 
398         /* negative item pos = get list size */
399         iRet = pEnumMRUListA(hMRU, -1, NULL, 0);
400         ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
401 
402         /* negative item pos = get list size */
403         iRet = pEnumMRUListA(hMRU, -5, NULL, 0);
404         ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
405 
406         /* negative item pos = get list size */
407         iRet = pEnumMRUListA(hMRU, -1, buffer, 255);
408         ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
409 
410         /* negative item pos = get list size */
411         iRet = pEnumMRUListA(hMRU, -5, buffer, 255);
412         ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
413 
414         /* check entry 0 */
415         buffer[0] = 0;
416         iRet = pEnumMRUListA(hMRU, 0, buffer, 255);
417         ok(iRet == lstrlenA(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[3]), iRet);
418         ok(strcmp(buffer, checks[3]) == 0, "EnumMRUList expected %s, got %s\n", checks[3], buffer);
419 
420         /* check entry 0 with a too small buffer */
421         buffer[0] = 0;   /* overwritten with 'T' */
422         buffer[1] = 'A'; /* overwritten with 0   */
423         buffer[2] = 'A'; /* unchanged */
424         buffer[3] = 0;   /* unchanged */
425         iRet = pEnumMRUListA(hMRU, 0, buffer, 2);
426         ok(iRet == lstrlenA(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[3]), iRet);
427         ok(strcmp(buffer, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer);
428         /* make sure space after buffer has old values */
429         ok(buffer[2] == 'A', "EnumMRUList expected %02x, got %02x\n", 'A', buffer[2]);
430 
431         /* check entry 1 */
432         buffer[0] = 0;
433         iRet = pEnumMRUListA(hMRU, 1, buffer, 255);
434         ok(iRet == lstrlenA(checks[1]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[1]), iRet);
435         ok(strcmp(buffer, checks[1]) == 0, "EnumMRUList expected %s, got %s\n", checks[1], buffer);
436 
437         /* check entry 2 */
438         buffer[0] = 0;
439         iRet = pEnumMRUListA(hMRU, 2, buffer, 255);
440         ok(iRet == lstrlenA(checks[2]), "EnumMRUList expected %d, got %d\n", lstrlenA(checks[2]), iRet);
441         ok(strcmp(buffer, checks[2]) == 0, "EnumMRUList expected %s, got %s\n", checks[2], buffer);
442 
443         /* check out of bounds entry 3 */
444         strcpy(buffer, "dummy");
445         iRet = pEnumMRUListA(hMRU, 3, buffer, 255);
446         ok(iRet == -1, "EnumMRUList expected %d, got %d\n", -1, iRet);
447         ok(strcmp(buffer, "dummy") == 0, "EnumMRUList expected unchanged buffer %s, got %s\n", "dummy", buffer);
448 
449         /* Finished with this MRU */
450         pFreeMRUList(hMRU);
451     }
452 
453     /* FreeMRUList(NULL) crashes on Win98 OSR0 */
454 }
455 
456 typedef struct {
457     MRUINFOA mruA;
458     BOOL ret;
459 } create_lazya_t;
460 
461 static const create_lazya_t create_lazyA[] = {
462     {{ sizeof(MRUINFOA) + 1, 0, 0, HKEY_CURRENT_USER, NULL, NULL }, FALSE },
463     {{ sizeof(MRUINFOA) - 1, 0, 0, HKEY_CURRENT_USER, NULL, NULL }, FALSE },
464     {{ sizeof(MRUINFOA) + 1, 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE },
465     {{ sizeof(MRUINFOA) - 1, 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE },
466     {{ sizeof(MRUINFOA), 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE },
467     {{ sizeof(MRUINFOA), 0, 0, HKEY_CURRENT_USER, NULL, NULL }, FALSE },
468     {{ sizeof(MRUINFOA), 0, 0, NULL, "WineTest", NULL }, FALSE },
469     {{ 0, 0, 0, NULL, "WineTest", NULL }, FALSE },
470     {{ 0, 0, 0, HKEY_CURRENT_USER, "WineTest", NULL }, TRUE }
471 };
472 
473 static void test_CreateMRUListLazyA(void)
474 {
475     int i;
476 
477     if (!pCreateMRUListLazyA || !pFreeMRUList)
478     {
479         win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
480         return;
481     }
482 
483     for (i = 0; i < sizeof(create_lazyA)/sizeof(create_lazya_t); i++)
484     {
485         const create_lazya_t *ptr = &create_lazyA[i];
486         HANDLE hMRU;
487 
488         hMRU = pCreateMRUListLazyA((MRUINFOA*)&ptr->mruA, 0, 0, 0);
489         if (ptr->ret)
490         {
491             ok(hMRU != NULL, "%d: got %p\n", i, hMRU);
492             pFreeMRUList(hMRU);
493         }
494         else
495             ok(hMRU == NULL, "%d: got %p\n", i, hMRU);
496     }
497 }
498 
499 static void test_EnumMRUList(void)
500 {
501     if (!pEnumMRUListA || !pEnumMRUListW)
502     {
503         win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
504         return;
505     }
506 
507     /* NULL handle */
508     if (0)
509     {
510         /* crashes on NT4, passed on Win2k, XP, 2k3, Vista, 2k8 */
511         pEnumMRUListA(NULL, 0, NULL, 0);
512         pEnumMRUListW(NULL, 0, NULL, 0);
513     }
514 }
515 
516 static void test_FindMRUData(void)
517 {
518     INT iRet;
519 
520     if (!pFindMRUData)
521     {
522         win_skip("FindMRUData entry point not found\n");
523         return;
524     }
525 
526     /* NULL handle */
527     iRet = pFindMRUData(NULL, NULL, 0, NULL);
528     ok(iRet == -1, "FindMRUData expected -1, got %d\n", iRet);
529 }
530 
531 static void test_AddMRUData(void)
532 {
533     INT iRet;
534 
535     if (!pAddMRUData)
536     {
537         win_skip("AddMRUData entry point not found\n");
538         return;
539     }
540 
541     /* NULL handle */
542     iRet = pFindMRUData(NULL, NULL, 0, NULL);
543     ok(iRet == -1, "AddMRUData expected -1, got %d\n", iRet);
544 }
545 
546 static void test_CreateMRUListW(void)
547 {
548     static const WCHAR mrutestW[] = {'M','R','U','T','e','s','t',0};
549     MRUINFOW infoW;
550     void *named;
551     HKEY hKey;
552     HANDLE hMru;
553 
554     if (!pCreateMRUListW)
555     {
556         win_skip("CreateMRUListW entry point not found\n");
557         return;
558     }
559 
560     /* exported by name too on recent versions */
561     named = GetProcAddress(hComctl32, "CreateMRUListW");
562     if (named)
563         ok(named == pCreateMRUListW, "got %p, expected %p\n", named, pCreateMRUListW);
564 
565     ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
566        "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
567 
568     infoW.cbSize = sizeof(infoW);
569     infoW.uMax = 1;
570     infoW.fFlags = 0;
571     infoW.lpszSubKey = mrutestW;
572     infoW.hKey = hKey;
573     infoW.lpfnCompare = NULL;
574 
575     hMru = pCreateMRUListW(&infoW);
576     ok(hMru != NULL, "got %p\n", hMru);
577     pFreeMRUList(hMru);
578 
579     /* smaller size */
580     infoW.cbSize = sizeof(infoW) - 1;
581     infoW.uMax = 1;
582     infoW.fFlags = 0;
583     infoW.lpszSubKey = mrutestW;
584     infoW.hKey = hKey;
585     infoW.lpfnCompare = NULL;
586 
587     hMru = pCreateMRUListW(&infoW);
588     ok(hMru != NULL, "got %p\n", hMru);
589     pFreeMRUList(hMru);
590 
591     /* increased size */
592     infoW.cbSize = sizeof(infoW) + 1;
593     infoW.uMax = 1;
594     infoW.fFlags = 0;
595     infoW.lpszSubKey = mrutestW;
596     infoW.hKey = hKey;
597     infoW.lpfnCompare = NULL;
598 
599     hMru = pCreateMRUListW(&infoW);
600     ok(hMru != NULL, "got %p\n", hMru);
601     pFreeMRUList(hMru);
602 
603     /* zero size */
604     infoW.cbSize = 0;
605     infoW.uMax = 1;
606     infoW.fFlags = 0;
607     infoW.lpszSubKey = mrutestW;
608     infoW.hKey = hKey;
609     infoW.lpfnCompare = NULL;
610 
611     hMru = pCreateMRUListW(&infoW);
612     ok(hMru != NULL, "got %p\n", hMru);
613     pFreeMRUList(hMru);
614 
615     /* NULL hKey */
616     infoW.cbSize = sizeof(infoW);
617     infoW.uMax = 1;
618     infoW.fFlags = 0;
619     infoW.lpszSubKey = mrutestW;
620     infoW.hKey = NULL;
621     infoW.lpfnCompare = NULL;
622 
623     hMru = pCreateMRUListW(&infoW);
624     ok(hMru == NULL, "got %p\n", hMru);
625 
626     RegCloseKey(hKey);
627 }
628 
629 static void test_CreateMRUListLazyW(void)
630 {
631     static const WCHAR mrutestW[] = {'M','R','U','T','e','s','t',0};
632     MRUINFOW infoW;
633     void *named;
634     HKEY hKey;
635     HANDLE hMru;
636 
637     if (!pCreateMRUListLazyW)
638     {
639         win_skip("CreateMRUListLazyW entry point not found\n");
640         return;
641     }
642 
643     /* check that it's not exported by name */
644     named = GetProcAddress(hComctl32, "CreateMRUListLazyW");
645     ok(named == NULL, "got %p\n", named);
646 
647     ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
648        "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
649 
650     infoW.cbSize = sizeof(infoW);
651     infoW.uMax = 1;
652     infoW.fFlags = 0;
653     infoW.lpszSubKey = mrutestW;
654     infoW.hKey = hKey;
655     infoW.lpfnCompare = NULL;
656 
657     hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
658     ok(hMru != NULL, "got %p\n", hMru);
659     pFreeMRUList(hMru);
660 
661     /* smaller size */
662     infoW.cbSize = sizeof(infoW) - 1;
663     infoW.uMax = 1;
664     infoW.fFlags = 0;
665     infoW.lpszSubKey = mrutestW;
666     infoW.hKey = hKey;
667     infoW.lpfnCompare = NULL;
668 
669     hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
670     ok(hMru != NULL, "got %p\n", hMru);
671     pFreeMRUList(hMru);
672 
673     /* increased size */
674     infoW.cbSize = sizeof(infoW) + 1;
675     infoW.uMax = 1;
676     infoW.fFlags = 0;
677     infoW.lpszSubKey = mrutestW;
678     infoW.hKey = hKey;
679     infoW.lpfnCompare = NULL;
680 
681     hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
682     ok(hMru != NULL, "got %p\n", hMru);
683     pFreeMRUList(hMru);
684 
685     /* zero size */
686     infoW.cbSize = 0;
687     infoW.uMax = 1;
688     infoW.fFlags = 0;
689     infoW.lpszSubKey = mrutestW;
690     infoW.hKey = hKey;
691     infoW.lpfnCompare = NULL;
692 
693     hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
694     ok(hMru != NULL, "got %p\n", hMru);
695     pFreeMRUList(hMru);
696 
697     /* NULL hKey */
698     infoW.cbSize = sizeof(infoW);
699     infoW.uMax = 1;
700     infoW.fFlags = 0;
701     infoW.lpszSubKey = mrutestW;
702     infoW.hKey = NULL;
703     infoW.lpfnCompare = NULL;
704 
705     hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
706     ok(hMru == NULL, "got %p\n", hMru);
707 
708     RegCloseKey(hKey);
709 }
710 
711 START_TEST(mru)
712 {
713     delete_reg_entries();
714     if (!create_reg_entries())
715         return;
716 
717     init_functions();
718 
719     test_MRUListA();
720     test_CreateMRUListLazyA();
721     test_CreateMRUListLazyW();
722     test_EnumMRUList();
723     test_FindMRUData();
724     test_AddMRUData();
725     test_CreateMRUListW();
726 
727     delete_reg_entries();
728 }
729