2 * comctl32 MRU unit tests
4 * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
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.
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.
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
31 #include "wine/heap.h"
32 #include "wine/test.h"
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
41 /* Undocumented MRU functions */
42 typedef struct tagMRUINFOA
49 int (CALLBACK
*lpfnCompare
)(LPCSTR
, LPCSTR
);
52 typedef struct tagMRUINFOW
59 int (CALLBACK
*lpfnCompare
)(LPCWSTR
, LPCWSTR
);
62 #define MRU_STRING 0 /* this one's invented */
64 #define MRU_CACHEWRITE 2
66 #define LIST_SIZE 3 /* Max entries for each mru */
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
*);
80 static void init_functions(void)
82 hComctl32
= LoadLibraryA("comctl32.dll");
84 #define X2(f, ord) p##f = (void*)GetProcAddress(hComctl32, (const char *)ord);
85 X2(CreateMRUListA
, 151);
87 X2(AddMRUStringA
, 153);
88 X2(EnumMRUListA
, 154);
89 X2(CreateMRUListLazyA
, 157);
92 X2(CreateMRUListW
, 400);
93 X2(EnumMRUListW
, 403);
94 X2(CreateMRUListLazyW
, 404);
98 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
99 static LSTATUS
mru_RegDeleteTreeA(HKEY hKey
, LPCSTR lpszSubKey
)
102 DWORD dwMaxSubkeyLen
, dwMaxValueLen
;
103 DWORD dwMaxLen
, dwSize
;
104 CHAR szNameBuf
[MAX_PATH
], *lpszName
= szNameBuf
;
109 ret
= RegOpenKeyExA(hKey
, lpszSubKey
, 0, KEY_READ
, &hSubKey
);
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
;
120 dwMaxLen
= max(dwMaxSubkeyLen
, dwMaxValueLen
);
121 if (dwMaxLen
> ARRAY_SIZE(szNameBuf
))
123 /* Name too big: alloc a buffer for it */
124 if (!(lpszName
= heap_alloc(dwMaxLen
* sizeof(CHAR
))))
126 ret
= ERROR_NOT_ENOUGH_MEMORY
;
132 /* Recursively delete all the subkeys */
136 if (RegEnumKeyExA(hSubKey
, 0, lpszName
, &dwSize
, NULL
,
137 NULL
, NULL
, NULL
)) break;
139 ret
= mru_RegDeleteTreeA(hSubKey
, lpszName
);
140 if (ret
) goto cleanup
;
144 ret
= RegDeleteKeyA(hKey
, lpszSubKey
);
149 if (RegEnumValueA(hKey
, 0, lpszName
, &dwSize
,
150 NULL
, NULL
, NULL
, NULL
)) break;
152 ret
= RegDeleteValueA(hKey
, lpszName
);
153 if (ret
) goto cleanup
;
157 /* Free buffer if allocated */
158 if (lpszName
!= szNameBuf
)
161 RegCloseKey(hSubKey
);
165 static BOOL
create_reg_entries(void)
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
;
176 static void delete_reg_entries(void)
180 if (RegOpenKeyExA(HKEY_CURRENT_USER
, REG_TEST_BASEKEYA
, 0, KEY_ALL_ACCESS
,
183 mru_RegDeleteTreeA(hKey
, REG_TEST_BASESUBKEYA
);
187 static void check_reg_entries(const char *mrulist
, const char**items
)
191 DWORD type
, size
, ret
;
194 ok(!RegOpenKeyA(HKEY_CURRENT_USER
, REG_TEST_FULLKEY
, &hKey
),
195 "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY
);
201 ret
= RegQueryValueExA(hKey
, "MRUList", NULL
, &type
, (LPBYTE
)buff
, &size
);
203 ok(!ret
&& buff
[0], "Checking MRU: got %d from RegQueryValueExW\n", ret
);
204 if(ret
|| !buff
[0]) return;
206 ok(strcmp(buff
, mrulist
) == 0, "Checking MRU: Expected list %s, got %s\n",
208 if(strcmp(buff
, mrulist
)) return;
210 for (i
= 0; i
< strlen(mrulist
); i
++)
213 name
[0] = mrulist
[i
];
218 ret
= RegQueryValueExA(hKey
, name
, NULL
, &type
, (LPBYTE
)buff
, &size
);
220 "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
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']);
229 static int CALLBACK
cmp_mru_strA(LPCSTR data1
, LPCSTR data2
)
231 return lstrcmpiA(data1
, data2
);
234 static void test_MRUListA(void)
236 const char *checks
[LIST_SIZE
+1];
242 if (!pCreateMRUListA
|| !pFreeMRUList
|| !pAddMRUStringA
|| !pEnumMRUListA
)
244 win_skip("MRU entry points not found\n");
250 /* Create (NULL) - crashes native */
251 hMRU
= pCreateMRUListA(NULL
);
255 infoA
.cbSize
= sizeof(infoA
) - 2;
256 infoA
.uMax
= LIST_SIZE
;
257 infoA
.fFlags
= MRU_STRING
;
259 infoA
.lpszSubKey
= REG_TEST_SUBKEYA
;
260 infoA
.lpfnCompare
= cmp_mru_strA
;
263 hMRU
= pCreateMRUListA(&infoA
);
264 ok (!hMRU
&& !GetLastError(),
265 "CreateMRUListA(too small) expected NULL,0 got %p,%d\n",
266 hMRU
, GetLastError());
269 infoA
.cbSize
= sizeof(infoA
) + 2;
270 infoA
.uMax
= LIST_SIZE
;
271 infoA
.fFlags
= MRU_STRING
;
273 infoA
.lpszSubKey
= REG_TEST_SUBKEYA
;
274 infoA
.lpfnCompare
= cmp_mru_strA
;
277 hMRU
= pCreateMRUListA(&infoA
);
278 ok (!hMRU
&& !GetLastError(),
279 "CreateMRUListA(too big) expected NULL,0 got %p,%d\n",
280 hMRU
, GetLastError());
283 infoA
.cbSize
= sizeof(infoA
);
284 infoA
.uMax
= LIST_SIZE
;
285 infoA
.fFlags
= MRU_STRING
;
287 infoA
.lpszSubKey
= REG_TEST_SUBKEYA
;
288 infoA
.lpfnCompare
= cmp_mru_strA
;
291 hMRU
= pCreateMRUListA(&infoA
);
292 ok (!hMRU
&& !GetLastError(),
293 "CreateMRUListA(NULL key) expected NULL,0 got %p,%d\n",
294 hMRU
, GetLastError());
296 /* NULL subkey name */
297 infoA
.cbSize
= sizeof(infoA
);
298 infoA
.uMax
= LIST_SIZE
;
299 infoA
.fFlags
= MRU_STRING
;
301 infoA
.lpszSubKey
= NULL
;
302 infoA
.lpfnCompare
= cmp_mru_strA
;
305 hMRU
= pCreateMRUListA(&infoA
);
306 ok (!hMRU
&& !GetLastError(),
307 "CreateMRUListA(NULL name) expected NULL,0 got %p,%d\n",
308 hMRU
, GetLastError());
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
);
315 infoA
.cbSize
= sizeof(infoA
);
316 infoA
.uMax
= LIST_SIZE
;
317 infoA
.fFlags
= MRU_STRING
;
319 infoA
.lpszSubKey
= REG_TEST_SUBKEYA
;
320 infoA
.lpfnCompare
= cmp_mru_strA
;
322 hMRU
= pCreateMRUListA(&infoA
);
323 ok(hMRU
&& !GetLastError(),
324 "CreateMRUListA(string) expected non-NULL,0 got %p,%d\n",
325 hMRU
, GetLastError());
330 checks
[0] = "Test 1";
331 checks
[1] = "Test 2";
332 checks
[2] = "Test 3";
333 checks
[3] = "Test 4";
335 /* Add (NULL list) */
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());
342 /* Add (NULL string) */
345 /* Some native versions crash when passed NULL or fail to SetLastError() */
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());
353 /* Add 3 strings. Check the registry is correct after each add */
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
);
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
);
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
);
375 /* Add a duplicate of the 2nd string - it should move to the front,
376 * but keep the same index in the registry.
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
);
385 /* Add a new string - replaces the oldest string + moves to the front */
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
);
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
);
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
);
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
);
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
);
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
);
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
);
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]);
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
);
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
);
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
);
449 /* Finished with this MRU */
453 /* FreeMRUList(NULL) crashes on Win98 OSR0 */
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
}
473 static void test_CreateMRUListLazyA(void)
477 if (!pCreateMRUListLazyA
|| !pFreeMRUList
)
479 win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
483 for (i
= 0; i
< ARRAY_SIZE(create_lazyA
); i
++)
485 const create_lazya_t
*ptr
= &create_lazyA
[i
];
488 hMRU
= pCreateMRUListLazyA((MRUINFOA
*)&ptr
->mruA
, 0, 0, 0);
491 ok(hMRU
!= NULL
, "%d: got %p\n", i
, hMRU
);
495 ok(hMRU
== NULL
, "%d: got %p\n", i
, hMRU
);
499 static void test_EnumMRUList(void)
501 if (!pEnumMRUListA
|| !pEnumMRUListW
)
503 win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
510 /* crashes on NT4, passed on Win2k, XP, 2k3, Vista, 2k8 */
511 pEnumMRUListA(NULL
, 0, NULL
, 0);
512 pEnumMRUListW(NULL
, 0, NULL
, 0);
516 static void test_FindMRUData(void)
522 win_skip("FindMRUData entry point not found\n");
527 iRet
= pFindMRUData(NULL
, NULL
, 0, NULL
);
528 ok(iRet
== -1, "FindMRUData expected -1, got %d\n", iRet
);
531 static void test_AddMRUData(void)
537 win_skip("AddMRUData entry point not found\n");
542 iRet
= pFindMRUData(NULL
, NULL
, 0, NULL
);
543 ok(iRet
== -1, "AddMRUData expected -1, got %d\n", iRet
);
546 static void test_CreateMRUListW(void)
553 if (!pCreateMRUListW
)
555 win_skip("CreateMRUListW entry point not found\n");
559 /* exported by name too on recent versions */
560 named
= GetProcAddress(hComctl32
, "CreateMRUListW");
562 ok(named
== pCreateMRUListW
, "got %p, expected %p\n", named
, pCreateMRUListW
);
564 ok(!RegCreateKeyA(HKEY_CURRENT_USER
, REG_TEST_KEYA
, &hKey
),
565 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA
);
567 infoW
.cbSize
= sizeof(infoW
);
570 infoW
.lpszSubKey
= L
"MRUTest";
572 infoW
.lpfnCompare
= NULL
;
574 hMru
= pCreateMRUListW(&infoW
);
575 ok(hMru
!= NULL
, "got %p\n", hMru
);
579 infoW
.cbSize
= sizeof(infoW
) - 1;
582 infoW
.lpszSubKey
= L
"MRUTest";
584 infoW
.lpfnCompare
= NULL
;
586 hMru
= pCreateMRUListW(&infoW
);
587 ok(hMru
!= NULL
, "got %p\n", hMru
);
591 infoW
.cbSize
= sizeof(infoW
) + 1;
594 infoW
.lpszSubKey
= L
"MRUTest";
596 infoW
.lpfnCompare
= NULL
;
598 hMru
= pCreateMRUListW(&infoW
);
599 ok(hMru
!= NULL
, "got %p\n", hMru
);
606 infoW
.lpszSubKey
= L
"MRUTest";
608 infoW
.lpfnCompare
= NULL
;
610 hMru
= pCreateMRUListW(&infoW
);
611 ok(hMru
!= NULL
, "got %p\n", hMru
);
615 infoW
.cbSize
= sizeof(infoW
);
618 infoW
.lpszSubKey
= L
"MRUTest";
620 infoW
.lpfnCompare
= NULL
;
622 hMru
= pCreateMRUListW(&infoW
);
623 ok(hMru
== NULL
, "got %p\n", hMru
);
628 static void test_CreateMRUListLazyW(void)
635 if (!pCreateMRUListLazyW
)
637 win_skip("CreateMRUListLazyW entry point not found\n");
641 /* check that it's not exported by name */
642 named
= GetProcAddress(hComctl32
, "CreateMRUListLazyW");
643 ok(named
== NULL
, "got %p\n", named
);
645 ok(!RegCreateKeyA(HKEY_CURRENT_USER
, REG_TEST_KEYA
, &hKey
),
646 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA
);
648 infoW
.cbSize
= sizeof(infoW
);
651 infoW
.lpszSubKey
= L
"MRUTest";
653 infoW
.lpfnCompare
= NULL
;
655 hMru
= pCreateMRUListLazyW(&infoW
, 0, 0, 0);
656 ok(hMru
!= NULL
, "got %p\n", hMru
);
660 infoW
.cbSize
= sizeof(infoW
) - 1;
663 infoW
.lpszSubKey
= L
"MRUTest";
665 infoW
.lpfnCompare
= NULL
;
667 hMru
= pCreateMRUListLazyW(&infoW
, 0, 0, 0);
668 ok(hMru
!= NULL
, "got %p\n", hMru
);
672 infoW
.cbSize
= sizeof(infoW
) + 1;
675 infoW
.lpszSubKey
= L
"MRUTest";
677 infoW
.lpfnCompare
= NULL
;
679 hMru
= pCreateMRUListLazyW(&infoW
, 0, 0, 0);
680 ok(hMru
!= NULL
, "got %p\n", hMru
);
687 infoW
.lpszSubKey
= L
"MRUTest";
689 infoW
.lpfnCompare
= NULL
;
691 hMru
= pCreateMRUListLazyW(&infoW
, 0, 0, 0);
692 ok(hMru
!= NULL
, "got %p\n", hMru
);
696 infoW
.cbSize
= sizeof(infoW
);
699 infoW
.lpszSubKey
= L
"MRUTest";
701 infoW
.lpfnCompare
= NULL
;
703 hMru
= pCreateMRUListLazyW(&infoW
, 0, 0, 0);
704 ok(hMru
== NULL
, "got %p\n", hMru
);
711 delete_reg_entries();
712 if (!create_reg_entries())
718 test_CreateMRUListLazyA();
719 test_CreateMRUListLazyW();
723 test_CreateMRUListW();
725 delete_reg_entries();