mru.c 21.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * comctl32 MRU unit tests
 *
 * Copyright (C) 2004 Jon Griffiths <jon_p_griffiths@yahoo.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
 */
#include <stdarg.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "winreg.h"
#include "commctrl.h"
#include "shlwapi.h"

#include "wine/test.h"

/* Keys for testing MRU functions */
#define REG_TEST_BASEKEYA    "Software\\Wine"
#define REG_TEST_BASESUBKEYA "Test"
#define REG_TEST_KEYA    REG_TEST_BASEKEYA "\\" REG_TEST_BASESUBKEYA
#define REG_TEST_SUBKEYA "MRUTest"
#define REG_TEST_FULLKEY REG_TEST_KEYA "\\" REG_TEST_SUBKEYA

40 41
/* Undocumented MRU functions */
typedef struct tagMRUINFOA
42 43
{
    DWORD   cbSize;
44 45
    UINT    uMax;
    UINT    fFlags;
46 47 48
    HKEY    hKey;
    LPCSTR  lpszSubKey;
    PROC    lpfnCompare;
49
} MRUINFOA;
50

51 52 53 54 55 56 57 58 59 60
typedef struct tagMRUINFOW
{
    DWORD   cbSize;
    UINT    uMax;
    UINT    fFlags;
    HKEY    hKey;
    LPCWSTR lpszSubKey;
    PROC    lpfnCompare;
} MRUINFOW;

61 62 63
#define MRU_STRING     0  /* this one's invented */
#define MRU_BINARY     1
#define MRU_CACHEWRITE 2
64 65 66 67

#define LIST_SIZE 3 /* Max entries for each mru */

static HMODULE hComctl32;
68
static HANDLE (WINAPI *pCreateMRUListA)(MRUINFOA*);
69 70
static void   (WINAPI *pFreeMRUList)(HANDLE);
static INT    (WINAPI *pAddMRUStringA)(HANDLE,LPCSTR);
71
static INT    (WINAPI *pEnumMRUListA)(HANDLE,INT,LPVOID,DWORD);
72
static INT    (WINAPI *pEnumMRUListW)(HANDLE,INT,LPVOID,DWORD);
73
static HANDLE (WINAPI *pCreateMRUListLazyA)(MRUINFOA*, DWORD, DWORD, DWORD);
74
static HANDLE (WINAPI *pCreateMRUListLazyW)(MRUINFOW*, DWORD, DWORD, DWORD);
75 76
static INT    (WINAPI *pFindMRUData)(HANDLE, LPCVOID, DWORD, LPINT);
static INT    (WINAPI *pAddMRUData)(HANDLE, LPCVOID, DWORD);
77
static HANDLE (WINAPI *pCreateMRUListW)(MRUINFOW*);
78

79 80 81 82 83
static void InitPointers(void)
{
    pCreateMRUListA = (void*)GetProcAddress(hComctl32,(LPCSTR)151);
    pFreeMRUList    = (void*)GetProcAddress(hComctl32,(LPCSTR)152);
    pAddMRUStringA  = (void*)GetProcAddress(hComctl32,(LPCSTR)153);
84
    pEnumMRUListA   = (void*)GetProcAddress(hComctl32,(LPCSTR)154);
85 86 87
    pCreateMRUListLazyA = (void*)GetProcAddress(hComctl32,(LPCSTR)157);
    pAddMRUData     = (void*)GetProcAddress(hComctl32,(LPCSTR)167);
    pFindMRUData    = (void*)GetProcAddress(hComctl32,(LPCSTR)169);
88
    pCreateMRUListW = (void*)GetProcAddress(hComctl32,(LPCSTR)400);
89
    pEnumMRUListW   = (void*)GetProcAddress(hComctl32,(LPCSTR)403);
90
    pCreateMRUListLazyW = (void*)GetProcAddress(hComctl32,(LPCSTR)404);
91 92
}

93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159
/* Based on RegDeleteTreeW from dlls/advapi32/registry.c */
static LSTATUS mru_RegDeleteTreeA(HKEY hKey, LPCSTR lpszSubKey)
{
    LONG ret;
    DWORD dwMaxSubkeyLen, dwMaxValueLen;
    DWORD dwMaxLen, dwSize;
    CHAR szNameBuf[MAX_PATH], *lpszName = szNameBuf;
    HKEY hSubKey = hKey;

    if(lpszSubKey)
    {
        ret = RegOpenKeyExA(hKey, lpszSubKey, 0, KEY_READ, &hSubKey);
        if (ret) return ret;
    }

    /* Get highest length for keys, values */
    ret = RegQueryInfoKeyA(hSubKey, NULL, NULL, NULL, NULL,
            &dwMaxSubkeyLen, NULL, NULL, &dwMaxValueLen, NULL, NULL, NULL);
    if (ret) goto cleanup;

    dwMaxSubkeyLen++;
    dwMaxValueLen++;
    dwMaxLen = max(dwMaxSubkeyLen, dwMaxValueLen);
    if (dwMaxLen > sizeof(szNameBuf)/sizeof(CHAR))
    {
        /* Name too big: alloc a buffer for it */
        if (!(lpszName = HeapAlloc( GetProcessHeap(), 0, dwMaxLen*sizeof(CHAR))))
        {
            ret = ERROR_NOT_ENOUGH_MEMORY;
            goto cleanup;
        }
    }


    /* Recursively delete all the subkeys */
    while (TRUE)
    {
        dwSize = dwMaxLen;
        if (RegEnumKeyExA(hSubKey, 0, lpszName, &dwSize, NULL,
                          NULL, NULL, NULL)) break;

        ret = mru_RegDeleteTreeA(hSubKey, lpszName);
        if (ret) goto cleanup;
    }

    if (lpszSubKey)
        ret = RegDeleteKeyA(hKey, lpszSubKey);
    else
        while (TRUE)
        {
            dwSize = dwMaxLen;
            if (RegEnumValueA(hKey, 0, lpszName, &dwSize,
                  NULL, NULL, NULL, NULL)) break;

            ret = RegDeleteValueA(hKey, lpszName);
            if (ret) goto cleanup;
        }

cleanup:
    /* Free buffer if allocated */
    if (lpszName != szNameBuf)
        HeapFree( GetProcessHeap(), 0, lpszName);
    if(lpszSubKey)
        RegCloseKey(hSubKey);
    return ret;
}

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
static BOOL create_reg_entries(void)
{
    HKEY hKey = NULL;

    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
    if (!hKey) return FALSE;
    RegCloseKey(hKey);
    return TRUE;
}

static void delete_reg_entries(void)
{
    HKEY hKey;

    if (RegOpenKeyExA(HKEY_CURRENT_USER, REG_TEST_BASEKEYA, 0, KEY_ALL_ACCESS,
                      &hKey))
        return;
178
    mru_RegDeleteTreeA(hKey, REG_TEST_BASESUBKEYA);
179 180 181 182 183 184 185 186
    RegCloseKey(hKey);
}

static void check_reg_entries(const char *mrulist, const char**items)
{
    char buff[128];
    HKEY hKey = NULL;
    DWORD type, size, ret;
187
    unsigned int i;
188 189 190 191 192 193 194 195 196 197

    ok(!RegOpenKeyA(HKEY_CURRENT_USER, REG_TEST_FULLKEY, &hKey),
       "Couldn't open test key \"%s\"\n", REG_TEST_FULLKEY);
    if (!hKey) return;

    type = REG_SZ;
    size = sizeof(buff);
    buff[0] = '\0';
    ret = RegQueryValueExA(hKey, "MRUList", NULL, &type, (LPBYTE)buff, &size);

198
    ok(!ret && buff[0], "Checking MRU: got %d from RegQueryValueExW\n", ret);
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    if(ret || !buff[0]) return;

    ok(strcmp(buff, mrulist) == 0, "Checking MRU: Expected list %s, got %s\n",
       mrulist, buff);
    if(strcmp(buff, mrulist)) return;

    for (i = 0; i < strlen(mrulist); i++)
    {
        char name[2];
        name[0] = mrulist[i];
        name[1] = '\0';
        type = REG_SZ;
        size = sizeof(buff);
        buff[0] = '\0';
        ret = RegQueryValueExA(hKey, name, NULL, &type, (LPBYTE)buff, &size);
        ok(!ret && buff[0],
215
           "Checking MRU item %d ('%c'): got %d from RegQueryValueExW\n",
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
           i, mrulist[i], ret);
        if(ret || !buff[0]) return;
        ok(!strcmp(buff, items[mrulist[i]-'a']),
           "Checking MRU item %d ('%c'): expected \"%s\", got \"%s\"\n",
           i, mrulist[i], buff, items[mrulist[i] - 'a']);
    }
}

static INT CALLBACK cmp_mru_strA(LPCVOID data1, LPCVOID data2)
{
    return lstrcmpiA(data1, data2);
}

static void test_MRUListA(void)
{
    const char *checks[LIST_SIZE+1];
232
    MRUINFOA infoA;
233 234 235 236
    HANDLE hMRU;
    HKEY hKey;
    INT iRet;

237
    if (!pCreateMRUListA || !pFreeMRUList || !pAddMRUStringA || !pEnumMRUListA)
238 239
    {
        skip("MRU entry points not found\n");
240
        return;
241
    }
242

243 244 245
    if (0)
    {
    /* Create (NULL) - crashes native */
246
    hMRU = pCreateMRUListA(NULL);
247
    }
248

249 250 251 252 253 254 255 256 257 258
    /* size too small */
    infoA.cbSize = sizeof(infoA) - 2;
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = (PROC)cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
259
    ok (!hMRU && !GetLastError(),
260
        "CreateMRUListA(too small) expected NULL,0 got %p,%d\n",
261 262
        hMRU, GetLastError());

263 264 265 266 267 268 269 270 271 272
    /* size too big */
    infoA.cbSize = sizeof(infoA) + 2;
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = (PROC)cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
273
    ok (!hMRU && !GetLastError(),
274
        "CreateMRUListA(too big) expected NULL,0 got %p,%d\n",
275 276
        hMRU, GetLastError());

277 278 279 280 281 282 283 284 285 286
    /* NULL hKey */
    infoA.cbSize = sizeof(infoA);
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = (PROC)cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
287
    ok (!hMRU && !GetLastError(),
288
        "CreateMRUListA(NULL key) expected NULL,0 got %p,%d\n",
289 290
        hMRU, GetLastError());

291 292 293 294 295 296 297 298 299 300
    /* NULL subkey name */
    infoA.cbSize = sizeof(infoA);
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = NULL;
    infoA.lpszSubKey = NULL;
    infoA.lpfnCompare = (PROC)cmp_mru_strA;

    SetLastError(0);
    hMRU = pCreateMRUListA(&infoA);
301
    ok (!hMRU && !GetLastError(),
302
        "CreateMRUListA(NULL name) expected NULL,0 got %p,%d\n",
303 304 305 306 307
        hMRU, GetLastError());

    /* Create a string MRU */
    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
308 309 310 311 312 313 314 315 316 317
    if (!hKey) return;

    infoA.cbSize = sizeof(infoA);
    infoA.uMax = LIST_SIZE;
    infoA.fFlags = MRU_STRING;
    infoA.hKey = hKey;
    infoA.lpszSubKey = REG_TEST_SUBKEYA;
    infoA.lpfnCompare = (PROC)cmp_mru_strA;

    hMRU = pCreateMRUListA(&infoA);
318
    ok(hMRU && !GetLastError(),
319
       "CreateMRUListA(string) expected non-NULL,0 got %p,%d\n",
320 321 322 323
       hMRU, GetLastError());

    if (hMRU)
    {
324
        char buffer[255];
325 326 327 328 329 330 331 332 333
        checks[0] = "Test 1";
        checks[1] = "Test 2";
        checks[2] = "Test 3";
        checks[3] = "Test 4";

        /* Add (NULL list) */
        SetLastError(0);
        iRet = pAddMRUStringA(NULL, checks[0]);
        ok(iRet == -1 && !GetLastError(),
334
           "AddMRUStringA(NULL list) expected -1,0 got %d,%d\n",
335 336 337
           iRet, GetLastError());

        /* Add (NULL string) */
338 339
        if (0)
        {
340
	/* Some native versions crash when passed NULL or fail to SetLastError()  */
341 342 343
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, NULL);
        ok(iRet == 0 && GetLastError() == ERROR_INVALID_PARAMETER,
344
           "AddMRUStringA(NULL str) expected 0,ERROR_INVALID_PARAMETER got %d,%d\n",
345
           iRet, GetLastError());
346
        }
347 348 349 350 351

        /* Add 3 strings. Check the registry is correct after each add */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[0]);
        ok(iRet == 0 && !GetLastError(),
352
           "AddMRUStringA(1) expected 0,0 got %d,%d\n",
353 354 355 356 357 358
           iRet, GetLastError());
        check_reg_entries("a", checks);

        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[1]);
        ok(iRet == 1 && !GetLastError(),
359
           "AddMRUStringA(2) expected 1,0 got %d,%d\n",
360 361 362 363 364 365
           iRet, GetLastError());
        check_reg_entries("ba", checks);

        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[2]);
        ok(iRet == 2 && !GetLastError(),
366
           "AddMRUStringA(2) expected 2,0 got %d,%d\n",
367 368 369 370 371 372 373 374 375
           iRet, GetLastError());
        check_reg_entries("cba", checks);

        /* Add a duplicate of the 2nd string - it should move to the front,
         * but keep the same index in the registry.
         */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[1]);
        ok(iRet == 1 && !GetLastError(),
376
           "AddMRUStringA(re-add 1) expected 1,0 got %d,%d\n",
377 378 379 380 381 382 383
           iRet, GetLastError());
        check_reg_entries("bca", checks);

        /* Add a new string - replaces the oldest string + moves to the front */
        SetLastError(0);
        iRet = pAddMRUStringA(hMRU, checks[3]);
        ok(iRet == 0 && !GetLastError(),
384
           "AddMRUStringA(add new) expected 0,0 got %d,%d\n",
385 386 387 388
           iRet, GetLastError());
        checks[0] = checks[3];
        check_reg_entries("abc", checks);

389
        /* NULL buffer = get list size */
390
        iRet = pEnumMRUListA(hMRU, 0, NULL, 0);
391
        ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
392 393

        /* negative item pos = get list size */
394
        iRet = pEnumMRUListA(hMRU, -1, NULL, 0);
395
        ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
396 397

        /* negative item pos = get list size */
398
        iRet = pEnumMRUListA(hMRU, -5, NULL, 0);
399
        ok(iRet == 3 || iRet == -1 /* Vista */, "EnumMRUList expected %d or -1, got %d\n", LIST_SIZE, iRet);
400 401

        /* negative item pos = get list size */
402
        iRet = pEnumMRUListA(hMRU, -1, buffer, 255);
403 404 405
        ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);

        /* negative item pos = get list size */
406
        iRet = pEnumMRUListA(hMRU, -5, buffer, 255);
407 408 409 410
        ok(iRet == 3, "EnumMRUList expected %d, got %d\n", LIST_SIZE, iRet);

        /* check entry 0 */
        buffer[0] = 0;
411
        iRet = pEnumMRUListA(hMRU, 0, buffer, 255);
412
        ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
413 414 415 416 417 418 419
        ok(strcmp(buffer, checks[3]) == 0, "EnumMRUList expected %s, got %s\n", checks[3], buffer);

        /* check entry 0 with a too small buffer */
        buffer[0] = 0;   /* overwritten with 'T' */
        buffer[1] = 'A'; /* overwritten with 0   */
        buffer[2] = 'A'; /* unchanged */
        buffer[3] = 0;   /* unchanged */
420
        iRet = pEnumMRUListA(hMRU, 0, buffer, 2);
421 422
        ok(iRet == lstrlen(checks[3]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[3]), iRet);
        ok(strcmp(buffer, "T") == 0, "EnumMRUList expected %s, got %s\n", "T", buffer);
423 424 425 426 427
        /* make sure space after buffer has old values */
        ok(buffer[2] == 'A', "EnumMRUList expected %02x, got %02x\n", 'A', buffer[2]);

        /* check entry 1 */
        buffer[0] = 0;
428
        iRet = pEnumMRUListA(hMRU, 1, buffer, 255);
429
        ok(iRet == lstrlen(checks[1]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[1]), iRet);
430 431 432 433
        ok(strcmp(buffer, checks[1]) == 0, "EnumMRUList expected %s, got %s\n", checks[1], buffer);

        /* check entry 2 */
        buffer[0] = 0;
434
        iRet = pEnumMRUListA(hMRU, 2, buffer, 255);
435
        ok(iRet == lstrlen(checks[2]), "EnumMRUList expected %d, got %d\n", lstrlen(checks[2]), iRet);
436 437 438 439
        ok(strcmp(buffer, checks[2]) == 0, "EnumMRUList expected %s, got %s\n", checks[2], buffer);

        /* check out of bounds entry 3 */
        strcpy(buffer, "dummy");
440
        iRet = pEnumMRUListA(hMRU, 3, buffer, 255);
441 442 443
        ok(iRet == -1, "EnumMRUList expected %d, got %d\n", -1, iRet);
        ok(strcmp(buffer, "dummy") == 0, "EnumMRUList expected unchanged buffer %s, got %s\n", "dummy", buffer);

444 445 446 447
        /* Finished with this MRU */
        pFreeMRUList(hMRU);
    }

448
    /* FreeMRUList(NULL) crashes on Win98 OSR0 */
449 450
}

451 452 453 454
static void test_CreateMRUListLazyA(void)
{
    HANDLE hMRU;
    HKEY hKey;
455
    MRUINFOA listA = { 0 };
456

457
    if (!pCreateMRUListLazyA || !pFreeMRUList)
458
    {
459
        win_skip("CreateMRUListLazyA or FreeMRUList entry points not found\n");
460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
        return;
    }

    /* wrong size */
    listA.cbSize = sizeof(listA) + 1;
    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
    ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
    listA.cbSize = 4;
    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
    ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
    /* NULL hKey */
    listA.cbSize = sizeof(listA);
    listA.hKey = NULL;
    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
    ok(hMRU == NULL, "Expected NULL handle, got %p\n", hMRU);
    /* NULL subkey */
    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);
    listA.cbSize = sizeof(listA);
    listA.hKey = hKey;
    listA.lpszSubKey = NULL;
    hMRU = pCreateMRUListLazyA(&listA, 0, 0, 0);
482 483 484
    ok(hMRU == NULL || broken(hMRU != NULL), /* Win9x */
       "Expected NULL handle, got %p\n", hMRU);
    if (hMRU) pFreeMRUList(hMRU);
485 486 487 488
}

static void test_EnumMRUList(void)
{
489
    if (!pEnumMRUListA || !pEnumMRUListW)
490 491 492 493 494 495 496 497 498
    {
        win_skip("EnumMRUListA/EnumMRUListW entry point not found\n");
        return;
    }

    /* NULL handle */
    if (0)
    {
        /* crashes on NT4, passed on Win2k, XP, 2k3, Vista, 2k8 */
499
        pEnumMRUListA(NULL, 0, NULL, 0);
500
        pEnumMRUListW(NULL, 0, NULL, 0);
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
    }
}

static void test_FindMRUData(void)
{
    INT iRet;

    if (!pFindMRUData)
    {
        win_skip("FindMRUData entry point not found\n");
        return;
    }

    /* NULL handle */
    iRet = pFindMRUData(NULL, NULL, 0, NULL);
    ok(iRet == -1, "FindMRUData expected -1, got %d\n", iRet);
}

static void test_AddMRUData(void)
{
    INT iRet;

    if (!pAddMRUData)
    {
        win_skip("AddMRUData entry point not found\n");
        return;
    }

    /* NULL handle */
    iRet = pFindMRUData(NULL, NULL, 0, NULL);
    ok(iRet == -1, "AddMRUData expected -1, got %d\n", iRet);
}

534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
static void test_CreateMRUListW(void)
{
    static const WCHAR mrutestW[] = {'M','R','U','T','e','s','t',0};
    MRUINFOW infoW;
    void *named;
    HKEY hKey;
    HANDLE hMru;

    if (!pCreateMRUListW)
    {
        win_skip("CreateMRUListW entry point not found\n");
        return;
    }

    /* exported by name too on recent versions */
    named = GetProcAddress(hComctl32, "CreateMRUListW");
    if (named)
        ok(named == pCreateMRUListW, "got %p, expected %p\n", named, pCreateMRUListW);

    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);

    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* smaller size */
    infoW.cbSize = sizeof(infoW) - 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
576
    ok(hMru != NULL, "got %p\n", hMru);
577 578 579 580 581 582 583 584 585 586 587
    pFreeMRUList(hMru);

    /* increased size */
    infoW.cbSize = sizeof(infoW) + 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
588
    ok(hMru != NULL, "got %p\n", hMru);
589 590 591 592 593 594 595 596 597 598 599
    pFreeMRUList(hMru);

    /* zero size */
    infoW.cbSize = 0;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
600
    ok(hMru != NULL, "got %p\n", hMru);
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657
    pFreeMRUList(hMru);

    /* NULL hKey */
    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = NULL;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListW(&infoW);
    ok(hMru == NULL, "got %p\n", hMru);

    RegCloseKey(hKey);
}

static void test_CreateMRUListLazyW(void)
{
    static const WCHAR mrutestW[] = {'M','R','U','T','e','s','t',0};
    MRUINFOW infoW;
    void *named;
    HKEY hKey;
    HANDLE hMru;

    if (!pCreateMRUListLazyW)
    {
        win_skip("CreateMRUListLazyW entry point not found\n");
        return;
    }

    /* check that it's not exported by name */
    named = GetProcAddress(hComctl32, "CreateMRUListLazyW");
    ok(named == NULL, "got %p\n", named);

    ok(!RegCreateKeyA(HKEY_CURRENT_USER, REG_TEST_KEYA, &hKey),
       "Couldn't create test key \"%s\"\n", REG_TEST_KEYA);

    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru != NULL, "got %p\n", hMru);
    pFreeMRUList(hMru);

    /* smaller size */
    infoW.cbSize = sizeof(infoW) - 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
658
    ok(hMru != NULL, "got %p\n", hMru);
659 660 661 662 663 664 665 666 667 668 669
    pFreeMRUList(hMru);

    /* increased size */
    infoW.cbSize = sizeof(infoW) + 1;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
670
    ok(hMru != NULL, "got %p\n", hMru);
671 672 673 674 675 676 677 678 679 680 681
    pFreeMRUList(hMru);

    /* zero size */
    infoW.cbSize = 0;
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = hKey;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
682
    ok(hMru != NULL, "got %p\n", hMru);
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
    pFreeMRUList(hMru);

    /* NULL hKey */
    infoW.cbSize = sizeof(infoW);
    infoW.uMax = 1;
    infoW.fFlags = 0;
    infoW.lpszSubKey = mrutestW;
    infoW.hKey = NULL;
    infoW.lpfnCompare = NULL;

    hMru = pCreateMRUListLazyW(&infoW, 0, 0, 0);
    ok(hMru == NULL, "got %p\n", hMru);

    RegCloseKey(hKey);
}

699 700 701 702 703 704 705 706
START_TEST(mru)
{
    hComctl32 = GetModuleHandleA("comctl32.dll");

    delete_reg_entries();
    if (!create_reg_entries())
        return;

707 708
    InitPointers();

709
    test_MRUListA();
710
    test_CreateMRUListLazyA();
711
    test_CreateMRUListLazyW();
712 713 714
    test_EnumMRUList();
    test_FindMRUData();
    test_AddMRUData();
715
    test_CreateMRUListW();
716 717 718

    delete_reg_entries();
}