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/test.h"
33 /* Keys for testing MRU functions */
34 #define REG_TEST_BASEKEYA "Software\\Wine"
35 #define REG_TEST_BASESUBKEYA "Test"
36 #define REG_TEST_KEYA REG_TEST_BASEKEYA "\\" REG_TEST_BASESUBKEYA
37 #define REG_TEST_SUBKEYA "MRUTest"
38 #define REG_TEST_FULLKEY REG_TEST_KEYA "\\" REG_TEST_SUBKEYA
40 /* Undocumented MRU structures & functions */
41 typedef struct tagCREATEMRULISTA
49 } CREATEMRULISTA, *LPCREATEMRULISTA;
51 #define MRUF_STRING_LIST 0
52 #define MRUF_BINARY_LIST 1
53 #define MRUF_DELAYED_SAVE 2
55 #define LIST_SIZE 3 /* Max entries for each mru */
57 static CREATEMRULISTA mruA =
59 sizeof(CREATEMRULISTA),
67 static HMODULE hComctl32;
68 static HANDLE (WINAPI *pCreateMRUListA)(LPCREATEMRULISTA);
69 static void (WINAPI *pFreeMRUList)(HANDLE);
70 static INT (WINAPI *pAddMRUStringA)(HANDLE,LPCSTR);
71 static INT (WINAPI *pEnumMRUList)(HANDLE,INT,LPVOID,DWORD);
72 static INT (WINAPI *pEnumMRUListW)(HANDLE,INT,LPVOID,DWORD);
73 static HANDLE (WINAPI *pCreateMRUListLazyA)(LPCREATEMRULISTA, DWORD, DWORD, DWORD);
74 static INT (WINAPI *pFindMRUData)(HANDLE, LPCVOID, DWORD, LPINT);
75 static INT (WINAPI *pAddMRUData)(HANDLE, LPCVOID, DWORD);
77 static INT (WINAPI *pFindMRUStringA)(HANDLE,LPCSTR,LPINT);
81 static void InitPointers(void)
83 pCreateMRUListA = (void*)GetProcAddress(hComctl32,(LPCSTR)151);
84 pFreeMRUList = (void*)GetProcAddress(hComctl32,(LPCSTR)152);
85 pAddMRUStringA = (void*)GetProcAddress(hComctl32,(LPCSTR)153);
86 pEnumMRUList = (void*)GetProcAddress(hComctl32,(LPCSTR)154);
87 pCreateMRUListLazyA = (void*)GetProcAddress(hComctl32,(LPCSTR)157);
88 pAddMRUData = (void*)GetProcAddress(hComctl32,(LPCSTR)167);
89 pFindMRUData = (void*)GetProcAddress(hComctl32,(LPCSTR)169);
90 pEnumMRUListW = (void*)GetProcAddress(hComctl32,(LPCSTR)403);
93 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
94 static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
97 DWORD dwMaxSubkeyLen, dwMaxValueLen;
98 DWORD dwMaxLen, dwSize;
99 CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
104 ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
108 /* Get highest length for keys, values */
109 ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
110 &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
111 if (ret) goto cleanup;
115 dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
116 if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
118 /* Name too big: alloc a buffer for it */
119 if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(CHAR))))
121 ret = ERROR_NOT_ENOUGH_MEMORY;
127 /* Recursively delete all the subkeys */
131 if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
132 NULL, NULL, NULL)) break;
134 ret = mru_RegDeleteTreeA(hSubKey, lpszName);
135 if (ret) goto cleanup;
139 ret = RegDeleteKeyA(hKey, lpszSubKey);
144 if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
145 NULL, NULL, NULL, NULL)) break;
147 ret = RegDeleteValueA(hKey, lpszName);
148 if (ret) goto cleanup;
152 /* Free buffer if allocated */
153 if (lpszName != szNameBuf)
154 HeapFree( GetProcessHeap(), 0, lpszName);
156 RegCloseKey(hSubKey);
160 static BOOL create_reg_entries(void)
164 ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
165 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
166 if (!hKey) return FALSE;
171 static void delete_reg_entries(void)
175 if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
178 mru_RegDeleteTreeA(hKey, REG_TEST_BASESUBKEYA);
182 static void check_reg_entries(const char *mrulist, const char**items)
186 DWORD type, size, ret;
189 ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
190 "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
196 ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);
198 ok(!ret && buff[0], "Checking MRU: got %d from RegQueryValueExW\n", ret);
199 if(ret || !buff[0]) return;
201 ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
203 if(strcmp(buff, mrulist)) return;
205 for (i = 0; i < strlen(mrulist); i++)
208 name[0] = mrulist[i];
213 ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
215 "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
217 if(ret || !buff[0]) return;
218 ok(!strcmp(buff, items[mrulist[i]-'a']),
219 "Checking MRU item %d ('%c'): expected \"%s\", got \"%s\"\n",
220 i, mrulist[i], buff, items[mrulist[i] - 'a']);
224 static INT CALLBACK cmp_mru_strA(LPCVOID data1, LPCVOID data2)
226 return lstrcmpiA(data1, data2);
229 static HANDLE create_mruA(HKEY hKey, DWORD flags, PROC cmp)
231 mruA.dwFlags = flags;
232 mruA.lpfnCompare = cmp;
236 return pCreateMRUListA(&mruA);
239 static void test_MRUListA(void)
241 const char *checks[LIST_SIZE+1];
246 if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUList)
248 skip("MRU entry points not found\n");
254 /* Create (NULL) - crashes native */
255 hMRU = pCreateMRUListA(NULL);
258 /* Create (size too small) */
259 mruA.cbSize = sizeof(mruA) - 2;
260 hMRU = create_mruA(NULL, MRUF_STRING_LIST, (PROC)cmp_mru_strA);
261 ok (!hMRU && !GetLastError(),
262 "CreateMRUListA(too small) expected NULL,0 got %p,%d\n",
263 hMRU, GetLastError());
264 mruA.cbSize = sizeof(mruA);
266 /* Create (size too big) */
267 mruA.cbSize = sizeof(mruA) + 2;
268 hMRU = create_mruA(NULL, MRUF_STRING_LIST, (PROC)cmp_mru_strA);
269 ok (!hMRU && !GetLastError(),
270 "CreateMRUListA(too big) expected NULL,0 got %p,%d\n",
271 hMRU, GetLastError());
272 mruA.cbSize = sizeof(mruA);
274 /* Create (NULL hKey) */
275 hMRU = create_mruA(NULL, MRUF_STRING_LIST, (PROC)cmp_mru_strA);
276 ok (!hMRU && !GetLastError(),
277 "CreateMRUListA(NULL key) expected NULL,0 got %p,%d\n",
278 hMRU, GetLastError());
280 /* Create (NULL name) */
281 mruA.lpszSubKey = NULL;
282 hMRU = create_mruA(NULL, MRUF_STRING_LIST, (PROC)cmp_mru_strA);
283 ok (!hMRU && !GetLastError(),
284 "CreateMRUListA(NULL name) expected NULL,0 got %p,%d\n",
285 hMRU, GetLastError());
286 mruA.lpszSubKey = REG_TEST_SUBKEYA;
288 /* Create a string MRU */
289 ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
290 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
293 hMRU = create_mruA(hKey, MRUF_STRING_LIST, (PROC)cmp_mru_strA);
294 ok(hMRU && !GetLastError(),
295 "CreateMRUListA(string) expected non-NULL,0 got %p,%d\n",
296 hMRU, GetLastError());
301 checks[0] = "Test 1";
302 checks[1] = "Test 2";
303 checks[2] = "Test 3";
304 checks[3] = "Test 4";
306 /* Add (NULL list) */
308 iRet = pAddMRUStringA(NULL, checks[0]);
309 ok(iRet == -1 && !GetLastError(),
310 "AddMRUStringA(NULL list) expected -1,0 got %d,%d\n",
311 iRet, GetLastError());
313 /* Add (NULL string) */
316 /* Some native versions crash when passed NULL or fail to SetLastError() */
318 iRet = pAddMRUStringA(hMRU, NULL);
319 ok(iRet == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
320 "AddMRUStringA(NULL str) expected 0,ERROR_INVALID_PARAMETER got %d,%d\n",
321 iRet, GetLastError());
324 /* Add 3 strings. Check the registry is correct after each add */
326 iRet = pAddMRUStringA(hMRU, checks[0]);
327 ok(iRet == 0 && !GetLastError(),
328 "AddMRUStringA(1) expected 0,0 got %d,%d\n",
329 iRet, GetLastError());
330 check_reg_entries("a", checks);
333 iRet = pAddMRUStringA(hMRU, checks[1]);
334 ok(iRet == 1 && !GetLastError(),
335 "AddMRUStringA(2) expected 1,0 got %d,%d\n",
336 iRet, GetLastError());
337 check_reg_entries("ba", checks);
340 iRet = pAddMRUStringA(hMRU, checks[2]);
341 ok(iRet == 2 && !GetLastError(),
342 "AddMRUStringA(2) expected 2,0 got %d,%d\n",
343 iRet, GetLastError());
344 check_reg_entries("cba", checks);
346 /* Add a duplicate of the 2nd string - it should move to the front,
347 * but keep the same index in the registry.
350 iRet = pAddMRUStringA(hMRU, checks[1]);
351 ok(iRet == 1 && !GetLastError(),
352 "AddMRUStringA(re-add 1) expected 1,0 got %d,%d\n",
353 iRet, GetLastError());
354 check_reg_entries("bca", checks);
356 /* Add a new string - replaces the oldest string + moves to the front */
358 iRet = pAddMRUStringA(hMRU, checks[3]);
359 ok(iRet == 0 && !GetLastError(),
360 "AddMRUStringA(add new) expected 0,0 got %d,%d\n",
361 iRet, GetLastError());
362 checks[0] = checks[3];
363 check_reg_entries("abc", checks);
365 /* NULL buffer = get list size */
366 iRet = pEnumMRUList(hMRU, 0, NULL, 0);
367 ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
369 /* negative item pos = get list size */
370 iRet = pEnumMRUList(hMRU, -1, NULL, 0);
371 ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
373 /* negative item pos = get list size */
374 iRet = pEnumMRUList(hMRU, -5, NULL, 0);
375 ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
377 /* negative item pos = get list size */
378 iRet = pEnumMRUList(hMRU, -1, buffer, 255);
379 ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
381 /* negative item pos = get list size */
382 iRet = pEnumMRUList(hMRU, -5, buffer, 255);
383 ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);
387 iRet = pEnumMRUList(hMRU, 0, buffer, 255);
388 todo_wine ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
389 ok(strcmp(buffer, checks[3]) == 0, "EnumMRUList expected %s, got %s\n", checks[3], buffer);
391 /* check entry 0 with a too small buffer */
392 buffer[0] = 0; /* overwritten with 'T' */
393 buffer[1] = 'A'; /* overwritten with 0 */
394 buffer[2] = 'A'; /* unchanged */
395 buffer[3] = 0; /* unchanged */
396 iRet = pEnumMRUList(hMRU, 0, buffer, 2);
397 todo_wine ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
398 todo_wine ok(strcmp(buffer, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer);
399 /* make sure space after buffer has old values */
400 ok(buffer[2] == 'A', "EnumMRUList expected %02x, got %02x\n", 'A', buffer[2]);
404 iRet = pEnumMRUList(hMRU, 1, buffer, 255);
405 todo_wine ok(iRet == lstrlen(checks[1]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[1]), iRet);
406 ok(strcmp(buffer, checks[1]) == 0, "EnumMRUList expected %s, got %s\n", checks[1], buffer);
410 iRet = pEnumMRUList(hMRU, 2, buffer, 255);
411 todo_wine ok(iRet == lstrlen(checks[2]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[2]), iRet);
412 ok(strcmp(buffer, checks[2]) == 0, "EnumMRUList expected %s, got %s\n", checks[2], buffer);
414 /* check out of bounds entry 3 */
415 strcpy(buffer, "dummy");
416 iRet = pEnumMRUList(hMRU, 3, buffer, 255);
417 ok(iRet == -1, "EnumMRUList expected %d, got %d\n", -1, iRet);
418 ok(strcmp(buffer, "dummy") == 0, "EnumMRUList expected unchanged buffer %s, got %s\n", "dummy", buffer);
420 /* Finished with this MRU */
424 /* FreeMRUList(NULL) crashes on Win98 OSR0 */
427 static void test_CreateMRUListLazyA(void)
431 CREATEMRULISTA listA = { 0 };
433 if (!pCreateMRUListLazyA || !pFreeMRUList)
435 win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
440 listA.cbSize = sizeof(listA) + 1;
441 hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
442 ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
444 hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
445 ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
447 listA.cbSize = sizeof(listA);
449 hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
450 ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
452 ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
453 "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
454 listA.cbSize = sizeof(listA);
456 listA.lpszSubKey = NULL;
457 hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
458 ok(hMRU == NULL || broken(hMRU != NULL), /* Win9x */
459 "Expected NULL handle, got %p\n", hMRU);
460 if (hMRU) pFreeMRUList(hMRU);
463 static void test_EnumMRUList(void)
465 if (!pEnumMRUList || !pEnumMRUListW)
467 win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
476 /* crashes on NT4, passed on Win2k, XP, 2k3, Vista, 2k8 */
477 iRet = pEnumMRUList(NULL, 0, NULL, 0);
478 iRet = pEnumMRUListW(NULL, 0, NULL, 0);
482 static void test_FindMRUData(void)
488 win_skip("FindMRUData entry point not found\n");
493 iRet = pFindMRUData(NULL, NULL, 0, NULL);
494 ok(iRet == -1, "FindMRUData expected -1, got %d\n", iRet);
497 static void test_AddMRUData(void)
503 win_skip("AddMRUData entry point not found\n");
508 iRet = pFindMRUData(NULL, NULL, 0, NULL);
509 ok(iRet == -1, "AddMRUData expected -1, got %d\n", iRet);
514 hComctl32 = GetModuleHandleA("comctl32.dll");
516 delete_reg_entries();
517 if (!create_reg_entries())
523 test_CreateMRUListLazyA();
528 delete_reg_entries();