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