comctl32/mru: In EnumMRUListA, ensure that the string is NULL terminated and the...
[wine] / dlls / comctl32 / tests / mru.c
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/test.h"
32
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
39
40 /* Undocumented MRU structures & functions */
41 typedef struct tagCREATEMRULISTA
42 {
43     DWORD   cbSize;
44     DWORD   nMaxItems;
45     DWORD   dwFlags;
46     HKEY    hKey;
47     LPCSTR  lpszSubKey;
48     PROC    lpfnCompare;
49 } CREATEMRULISTA, *LPCREATEMRULISTA;
50
51 #define MRUF_STRING_LIST  0
52 #define MRUF_BINARY_LIST  1
53 #define MRUF_DELAYED_SAVE 2
54
55 #define LIST_SIZE 3 /* Max entries for each mru */
56
57 static CREATEMRULISTA mruA =
58 {
59     sizeof(CREATEMRULISTA),
60     LIST_SIZE,
61     0,
62     NULL,
63     REG_TEST_SUBKEYA,
64     NULL
65 };
66
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);
76 /*
77 static INT    (WINAPI *pFindMRUStringA)(HANDLE,LPCSTR,LPINT);
78 */
79
80
81 static void InitPointers(void)
82 {
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);
91 }
92
93 /* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
94 static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
95 {
96     LONG ret;
97     DWORD dwMaxSubkeyLen, dwMaxValueLen;
98     DWORD dwMaxLen, dwSize;
99     CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
100     HKEY hSubKey = hKey;
101
102     if(lpszSubKey)
103     {
104         ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
105         if (ret) return ret;
106     }
107
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;
112
113     dwMaxSubkeyLen++;
114     dwMaxValueLen++;
115     dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
116     if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
117     {
118         /* Name too big: alloc a buffer for it */
119         if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(CHAR))))
120         {
121             ret = ERROR_NOT_ENOUGH_MEMORY;
122             goto cleanup;
123         }
124     }
125
126
127     /* Recursively delete all the subkeys */
128     while (TRUE)
129     {
130         dwSize = dwMaxLen;
131         if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
132                           NULL, NULL, NULL)) break;
133
134         ret = mru_RegDeleteTreeA(hSubKey, lpszName);
135         if (ret) goto cleanup;
136     }
137
138     if (lpszSubKey)
139         ret = RegDeleteKeyA(hKey, lpszSubKey);
140     else
141         while (TRUE)
142         {
143             dwSize = dwMaxLen;
144             if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
145                   NULL, NULL, NULL, NULL)) break;
146
147             ret = RegDeleteValueA(hKey, lpszName);
148             if (ret) goto cleanup;
149         }
150
151 cleanup:
152     /* Free buffer if allocated */
153     if (lpszName != szNameBuf)
154         HeapFree( GetProcessHeap(), 0, lpszName);
155     if(lpszSubKey)
156         RegCloseKey(hSubKey);
157     return ret;
158 }
159
160 static BOOL create_reg_entries(void)
161 {
162     HKEY hKey = NULL;
163
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;
167     RegCloseKey(hKey);
168     return TRUE;
169 }
170
171 static void delete_reg_entries(void)
172 {
173     HKEY hKey;
174
175     if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
176                       &hKey))
177         return;
178     mru_RegDeleteTreeA(hKey, REG_TEST_BASESUBKEYA);
179     RegCloseKey(hKey);
180 }
181
182 static void check_reg_entries(const char *mrulist, const char**items)
183 {
184     char buff[128];
185     HKEY hKey = NULL;
186     DWORD type, size, ret;
187     unsigned int i;
188
189     ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
190        "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
191     if (!hKey) return;
192
193     type = REG_SZ;
194     size = sizeof(buff);
195     buff[0] = '\0';
196     ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);
197
198     ok(!ret && buff[0], "Checking MRU: got %d from RegQueryValueExW\n", ret);
199     if(ret || !buff[0]) return;
200
201     ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
202        mrulist, buff);
203     if(strcmp(buff, mrulist)) return;
204
205     for (i = 0; i < strlen(mrulist); i++)
206     {
207         char name[2];
208         name[0] = mrulist[i];
209         name[1] = '\0';
210         type = REG_SZ;
211         size = sizeof(buff);
212         buff[0] = '\0';
213         ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
214         ok(!ret && buff[0],
215            "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
216            i, mrulist[i], ret);
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']);
221     }
222 }
223
224 static INT CALLBACK cmp_mru_strA(LPCVOID data1, LPCVOID data2)
225 {
226     return lstrcmpiA(data1, data2);
227 }
228
229 static HANDLE create_mruA(HKEY hKey, DWORD flags, PROC cmp)
230 {
231     mruA.dwFlags = flags;
232     mruA.lpfnCompare = cmp;
233     mruA.hKey = hKey;
234
235     SetLastError(0);
236     return pCreateMRUListA(&mruA);
237 }
238
239 static void test_MRUListA(void)
240 {
241     const char *checks[LIST_SIZE+1];
242     HANDLE hMRU;
243     HKEY hKey;
244     INT iRet;
245
246     if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUList)
247     {
248         skip("MRU entry points not found\n");
249         return;
250     }
251
252     if (0)
253     {
254     /* Create (NULL) - crashes native */
255     hMRU = pCreateMRUListA(NULL);
256     }
257
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);
265
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);
273
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());
279
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;
287
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);
291     if (!hKey)
292         return;
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());
297
298     if (hMRU)
299     {
300         char buffer[255];
301         checks[0] = "Test 1";
302         checks[1] = "Test 2";
303         checks[2] = "Test 3";
304         checks[3] = "Test 4";
305
306         /* Add (NULL list) */
307         SetLastError(0);
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());
312
313         /* Add (NULL string) */
314         if (0)
315         {
316         /* Some native versions crash when passed NULL or fail to SetLastError()  */
317         SetLastError(0);
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());
322         }
323
324         /* Add 3 strings. Check the registry is correct after each add */
325         SetLastError(0);
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);
331
332         SetLastError(0);
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);
338
339         SetLastError(0);
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);
345
346         /* Add a duplicate of the 2nd string - it should move to the front,
347          * but keep the same index in the registry.
348          */
349         SetLastError(0);
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);
355
356         /* Add a new string - replaces the oldest string + moves to the front */
357         SetLastError(0);
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);
364
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);
368
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);
372
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);
376
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);
380
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);
384
385         /* check entry 0 */
386         buffer[0] = 0;
387         iRet = pEnumMRUList(hMRU, 0, buffer, 255);
388         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);
390
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         ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
398         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]);
401
402         /* check entry 1 */
403         buffer[0] = 0;
404         iRet = pEnumMRUList(hMRU, 1, buffer, 255);
405         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);
407
408         /* check entry 2 */
409         buffer[0] = 0;
410         iRet = pEnumMRUList(hMRU, 2, buffer, 255);
411         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);
413
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);
419
420         /* Finished with this MRU */
421         pFreeMRUList(hMRU);
422     }
423
424     /* FreeMRUList(NULL) crashes on Win98 OSR0 */
425 }
426
427 static void test_CreateMRUListLazyA(void)
428 {
429     HANDLE hMRU;
430     HKEY hKey;
431     CREATEMRULISTA listA = { 0 };
432
433     if (!pCreateMRUListLazyA || !pFreeMRUList)
434     {
435         win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
436         return;
437     }
438
439     /* wrong size */
440     listA.cbSize = sizeof(listA) + 1;
441     hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
442     ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
443     listA.cbSize = 4;
444     hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
445     ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
446     /* NULL hKey */
447     listA.cbSize = sizeof(listA);
448     listA.hKey = NULL;
449     hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
450     ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
451     /* NULL subkey */
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);
455     listA.hKey = hKey;
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);
461 }
462
463 static void test_EnumMRUList(void)
464 {
465     if (!pEnumMRUList || !pEnumMRUListW)
466     {
467         win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
468         return;
469     }
470
471     /* NULL handle */
472     if (0)
473     {
474         INT iRet;
475
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);
479     }
480 }
481
482 static void test_FindMRUData(void)
483 {
484     INT iRet;
485
486     if (!pFindMRUData)
487     {
488         win_skip("FindMRUData entry point not found\n");
489         return;
490     }
491
492     /* NULL handle */
493     iRet = pFindMRUData(NULL, NULL, 0, NULL);
494     ok(iRet == -1, "FindMRUData expected -1, got %d\n", iRet);
495 }
496
497 static void test_AddMRUData(void)
498 {
499     INT iRet;
500
501     if (!pAddMRUData)
502     {
503         win_skip("AddMRUData entry point not found\n");
504         return;
505     }
506
507     /* NULL handle */
508     iRet = pFindMRUData(NULL, NULL, 0, NULL);
509     ok(iRet == -1, "AddMRUData expected -1, got %d\n", iRet);
510 }
511
512 START_TEST(mru)
513 {
514     hComctl32 = GetModuleHandleA("comctl32.dll");
515
516     delete_reg_entries();
517     if (!create_reg_entries())
518         return;
519
520     InitPointers();
521
522     test_MRUListA();
523     test_CreateMRUListLazyA();
524     test_EnumMRUList();
525     test_FindMRUData();
526     test_AddMRUData();
527
528     delete_reg_entries();
529 }