shlfolder.c 62.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/*
 * Unit test of the IShellFolder functions.
 *
 * Copyright 2004 Vitaliy Margolen
 *
 * 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
 */

#include <stdarg.h>
#include <stdio.h>

24
#define COBJMACROS
25
#define CONST_VTABLE
26

27 28 29 30 31 32 33 34 35
#include "windef.h"
#include "winbase.h"
#include "wtypes.h"
#include "shellapi.h"


#include "shlguid.h"
#include "shlobj.h"
#include "shobjidl.h"
36
#include "shlwapi.h"
37 38
#include "ocidl.h"
#include "oleauto.h"
39 40 41 42

#include "wine/test.h"


43
static IMalloc *ppM;
44

45
static HRESULT (WINAPI *pSHBindToParent)(LPCITEMIDLIST, REFIID, LPVOID*, LPCITEMIDLIST*);
46 47
static HRESULT (WINAPI *pSHGetFolderPathA)(HWND, int, HANDLE, DWORD, LPSTR);
static HRESULT (WINAPI *pSHGetFolderPathAndSubDirA)(HWND, int, HANDLE, DWORD, LPCSTR, LPSTR);
48
static BOOL (WINAPI *pSHGetPathFromIDListW)(LPCITEMIDLIST,LPWSTR);
49
static BOOL (WINAPI *pSHGetSpecialFolderPathW)(HWND, LPWSTR, int, BOOL);
50
static HRESULT (WINAPI *pStrRetToBufW)(STRRET*,LPCITEMIDLIST,LPWSTR,UINT);
51
static LPITEMIDLIST (WINAPI *pILFindLastID)(LPCITEMIDLIST);
52 53
static void (WINAPI *pILFree)(LPITEMIDLIST);
static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST);
54 55 56

static void init_function_pointers(void)
{
57
    HMODULE hmod;
58
    HRESULT hr;
59

60
    hmod = GetModuleHandleA("shell32.dll");
61
    pSHBindToParent = (void*)GetProcAddress(hmod, "SHBindToParent");
62 63
    pSHGetFolderPathA = (void*)GetProcAddress(hmod, "SHGetFolderPathA");
    pSHGetFolderPathAndSubDirA = (void*)GetProcAddress(hmod, "SHGetFolderPathAndSubDirA");
64
    pSHGetPathFromIDListW = (void*)GetProcAddress(hmod, "SHGetPathFromIDListW");
65 66 67 68
    pSHGetSpecialFolderPathW = (void*)GetProcAddress(hmod, "SHGetSpecialFolderPathW");
    pILFindLastID = (void *)GetProcAddress(hmod, (LPCSTR)16);
    pILFree = (void*)GetProcAddress(hmod, (LPSTR)155);
    pILIsEqual = (void*)GetProcAddress(hmod, (LPSTR)21);
69

70
    hmod = GetModuleHandleA("shlwapi.dll");
71
    pStrRetToBufW = (void*)GetProcAddress(hmod, "StrRetToBufW");
72

73
    hr = SHGetMalloc(&ppM);
74
    ok(hr == S_OK, "SHGetMalloc failed %08x\n", hr);
75 76
}

77 78 79 80 81 82 83 84 85
static void test_ParseDisplayName(void)
{
    HRESULT hr;
    IShellFolder *IDesktopFolder;
    static const char *cNonExistDir1A = "c:\\nonexist_subdir";
    static const char *cNonExistDir2A = "c:\\\\nonexist_subdir";
    DWORD res;
    WCHAR cTestDirW [MAX_PATH] = {0};
    ITEMIDLIST *newPIDL;
86
    BOOL bRes;
87 88 89 90 91 92 93 94 95 96

    hr = SHGetDesktopFolder(&IDesktopFolder);
    if(hr != S_OK) return;

    res = GetFileAttributesA(cNonExistDir1A);
    if(res != INVALID_FILE_ATTRIBUTES) return;

    MultiByteToWideChar(CP_ACP, 0, cNonExistDir1A, -1, cTestDirW, MAX_PATH);
    hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
        NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
97
    ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL), 
98
        "ParseDisplayName returned %08x, expected 80070002 or E_FAIL\n", hr);
99 100 101 102 103 104 105

    res = GetFileAttributesA(cNonExistDir2A);
    if(res != INVALID_FILE_ATTRIBUTES) return;

    MultiByteToWideChar(CP_ACP, 0, cNonExistDir2A, -1, cTestDirW, MAX_PATH);
    hr = IShellFolder_ParseDisplayName(IDesktopFolder, 
        NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
106
    ok((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || (hr == E_FAIL) || (hr == E_INVALIDARG), 
107
        "ParseDisplayName returned %08x, expected 80070002, E_FAIL or E_INVALIDARG\n", hr);
108

109 110 111 112 113 114
    /* I thought that perhaps the DesktopFolder's ParseDisplayName would recognize the
     * path corresponding to CSIDL_PERSONAL and return a CLSID_MyDocuments PIDL. Turns
     * out it doesn't. The magic seems to happen in the file dialogs, then. */
    if (!pSHGetSpecialFolderPathW || !pILFindLastID) goto finished;
    
    bRes = pSHGetSpecialFolderPathW(NULL, cTestDirW, CSIDL_PERSONAL, FALSE);
115
    ok(bRes, "SHGetSpecialFolderPath(CSIDL_PERSONAL) failed! %u\n", GetLastError());
116 117 118
    if (!bRes) goto finished;

    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
119
    ok(SUCCEEDED(hr), "DesktopFolder->ParseDisplayName failed. hr = %08x.\n", hr);
120 121 122 123 124 125 126 127
    if (FAILED(hr)) goto finished;

    ok(pILFindLastID(newPIDL)->mkid.abID[0] == 0x31, "Last pidl should be of type "
       "PT_FOLDER, but is: %02x\n", pILFindLastID(newPIDL)->mkid.abID[0]);
    IMalloc_Free(ppM, newPIDL);
    
finished:
    IShellFolder_Release(IDesktopFolder);
128 129
}

130
/* creates a file with the specified name for tests */
131
static void CreateTestFile(const CHAR *name)
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
{
    HANDLE file;
    DWORD written;

    file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
    if (file != INVALID_HANDLE_VALUE)
    {
	WriteFile(file, name, strlen(name), &written, NULL);
	WriteFile(file, "\n", strlen("\n"), &written, NULL);
	CloseHandle(file);
    }
}


/* initializes the tests */
147
static void CreateFilesFolders(void)
148 149 150 151 152 153 154
{
    CreateDirectoryA(".\\testdir", NULL);
    CreateDirectoryA(".\\testdir\\test.txt", NULL);
    CreateTestFile  (".\\testdir\\test1.txt ");
    CreateTestFile  (".\\testdir\\test2.txt ");
    CreateTestFile  (".\\testdir\\test3.txt ");
    CreateDirectoryA(".\\testdir\\testdir2 ", NULL);
155
    CreateDirectoryA(".\\testdir\\testdir2\\subdir", NULL);
156 157 158
}

/* cleans after tests */
159
static void Cleanup(void)
160 161 162 163 164
{
    DeleteFileA(".\\testdir\\test1.txt");
    DeleteFileA(".\\testdir\\test2.txt");
    DeleteFileA(".\\testdir\\test3.txt");
    RemoveDirectoryA(".\\testdir\\test.txt");
165
    RemoveDirectoryA(".\\testdir\\testdir2\\subdir");
166 167 168 169 170 171
    RemoveDirectoryA(".\\testdir\\testdir2");
    RemoveDirectoryA(".\\testdir");
}


/* perform test */
172
static void test_EnumObjects(IShellFolder *iFolder)
173 174
{
    IEnumIDList *iEnumList;
175
    LPITEMIDLIST newPIDL, idlArr[10];
176 177
    ULONG NumPIDLs;
    int i=0, j;
178
    HRESULT hr;
179 180 181 182

    static const WORD iResults [5][5] =
    {
	{ 0,-1,-1,-1,-1},
183 184 185 186
	{ 1, 0,-1,-1,-1},
	{ 1, 1, 0,-1,-1},
	{ 1, 1, 1, 0,-1},
	{ 1, 1, 1, 1, 0}
187 188
    };

189 190
#define SFGAO_testfor SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR | SFGAO_CAPABILITYMASK
    /* Don't test for SFGAO_HASSUBFOLDER since we return real state and native cached */
191
    static const ULONG attrs[5] =
192
    {
193 194 195 196 197
        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR,
        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
        SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM,
198 199 200
    };

    hr = IShellFolder_EnumObjects(iFolder, NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &iEnumList);
201
    ok(hr == S_OK, "EnumObjects failed %08x\n", hr);
202

203 204
    /* This is to show that, contrary to what is said on MSDN, on IEnumIDList::Next,
     * the filesystem shellfolders return S_OK even if less than 'celt' items are
205 206 207 208 209
     * returned (in contrast to S_FALSE). We have to do it in a loop since WinXP
     * only ever returns a single entry per call. */
    while (IEnumIDList_Next(iEnumList, 10-i, &idlArr[i], &NumPIDLs) == S_OK) 
        i += NumPIDLs;
    ok (i == 5, "i: %d\n", i);
210 211

    hr = IEnumIDList_Release(iEnumList);
212
    ok(hr == S_OK, "IEnumIDList_Release failed %08x\n", hr);
213
    
214 215 216
    /* Sort them first in case of wrong order from system */
    for (i=0;i<5;i++) for (j=0;j<5;j++)
        if ((SHORT)IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]) < 0)
217
	{
218 219 220 221 222 223 224 225
            newPIDL = idlArr[i];
            idlArr[i] = idlArr[j];
            idlArr[j] = newPIDL;
        }
	    
    for (i=0;i<5;i++) for (j=0;j<5;j++)
    {
        hr = IShellFolder_CompareIDs(iFolder, 0, idlArr[i], idlArr[j]);
226
        ok(hr == iResults[i][j], "Got %x expected [%d]-[%d]=%x\n", hr, i, j, iResults[i][j]);
227 228
    }

229

230 231 232
    for (i = 0; i < 5; i++)
    {
        SFGAOF flags;
233 234
        /* Native returns all flags no matter what we ask for */
        flags = SFGAO_CANCOPY;
235
        hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
236
        flags &= SFGAO_testfor;
237 238
        ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
        ok(flags == (attrs[i]), "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
239 240 241 242

        flags = SFGAO_testfor;
        hr = IShellFolder_GetAttributesOf(iFolder, 1, (LPCITEMIDLIST*)(idlArr + i), &flags);
        flags &= SFGAO_testfor;
243 244
        ok(hr == S_OK, "GetAttributesOf returns %08x\n", hr);
        ok(flags == attrs[i], "GetAttributesOf[%i] got %08x, expected %08x\n", i, flags, attrs[i]);
245
    }
246 247 248

    for (i=0;i<5;i++)
        IMalloc_Free(ppM, idlArr[i]);
249 250
}

251
static void test_BindToObject(void)
252 253 254 255 256 257 258
{
    HRESULT hr;
    UINT cChars;
    IShellFolder *psfDesktop, *psfChild, *psfMyComputer, *psfSystemDir;
    SHITEMID emptyitem = { 0, { 0 } };
    LPITEMIDLIST pidlMyComputer, pidlSystemDir, pidlEmpty = (LPITEMIDLIST)&emptyitem;
    WCHAR wszSystemDir[MAX_PATH];
259
    char szSystemDir[MAX_PATH];
260 261 262 263 264 265 266 267
    WCHAR wszMyComputer[] = { 
        ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
        'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };

    /* The following tests shows that BindToObject should fail with E_INVALIDARG if called
     * with an empty pidl. This is tested for Desktop, MyComputer and the FS ShellFolder
     */
    hr = SHGetDesktopFolder(&psfDesktop);
268
    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
269 270 271
    if (FAILED(hr)) return;
    
    hr = IShellFolder_BindToObject(psfDesktop, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
272
    ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
273

274
    hr = IShellFolder_BindToObject(psfDesktop, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
275
    ok (hr == E_INVALIDARG, "Desktop's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
276

277
    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
278
    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
279 280 281 282 283 284
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }
    
    hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
285
    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
286
    IShellFolder_Release(psfDesktop);
287
    IMalloc_Free(ppM, pidlMyComputer);
288 289 290
    if (FAILED(hr)) return;

    hr = IShellFolder_BindToObject(psfMyComputer, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
291
    ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
292

293 294
#if 0
    /* this call segfaults on 98SE */
295
    hr = IShellFolder_BindToObject(psfMyComputer, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
296
    ok (hr == E_INVALIDARG, "MyComputers's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
297
#endif
298

299
    cChars = GetSystemDirectoryA(szSystemDir, MAX_PATH);
300
    ok (cChars > 0 && cChars < MAX_PATH, "GetSystemDirectoryA failed! LastError: %u\n", GetLastError());
301 302 303 304
    if (cChars == 0 || cChars >= MAX_PATH) {
        IShellFolder_Release(psfMyComputer);
        return;
    }
305
    MultiByteToWideChar(CP_ACP, 0, szSystemDir, -1, wszSystemDir, MAX_PATH);
306 307
    
    hr = IShellFolder_ParseDisplayName(psfMyComputer, NULL, NULL, wszSystemDir, NULL, &pidlSystemDir, NULL);
308
    ok (SUCCEEDED(hr), "MyComputers's ParseDisplayName failed to parse the SystemDirectory! hr = %08x\n", hr);
309 310 311 312 313 314
    if (FAILED(hr)) {
        IShellFolder_Release(psfMyComputer);
        return;
    }

    hr = IShellFolder_BindToObject(psfMyComputer, pidlSystemDir, NULL, &IID_IShellFolder, (LPVOID*)&psfSystemDir);
315
    ok (SUCCEEDED(hr), "MyComputer failed to bind to a FileSystem ShellFolder! hr = %08x\n", hr);
316
    IShellFolder_Release(psfMyComputer);
317
    IMalloc_Free(ppM, pidlSystemDir);
318 319 320 321
    if (FAILED(hr)) return;

    hr = IShellFolder_BindToObject(psfSystemDir, pidlEmpty, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
    ok (hr == E_INVALIDARG, 
322
        "FileSystem ShellFolder's BindToObject should fail, when called with empty pidl! hr = %08x\n", hr);
323
    
324 325
#if 0
    /* this call segfaults on 98SE */
326 327
    hr = IShellFolder_BindToObject(psfSystemDir, NULL, NULL, &IID_IShellFolder, (LPVOID*)&psfChild);
    ok (hr == E_INVALIDARG, 
328
        "FileSystem ShellFolder's BindToObject should fail, when called with NULL pidl! hr = %08x\n", hr);
329
#endif
330

331 332
    IShellFolder_Release(psfSystemDir);
}
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353

/* Based on PathAddBackslashW from dlls/shlwapi/path.c */
static LPWSTR myPathAddBackslashW( LPWSTR lpszPath )
{
  size_t iLen;

  if (!lpszPath || (iLen = lstrlenW(lpszPath)) >= MAX_PATH)
    return NULL;

  if (iLen)
  {
    lpszPath += iLen;
    if (lpszPath[-1] != '\\')
    {
      *lpszPath++ = '\\';
      *lpszPath = '\0';
    }
  }
  return lpszPath;
}

354 355 356 357 358 359
static void test_GetDisplayName(void)
{
    BOOL result;
    HRESULT hr;
    HANDLE hTestFile;
    WCHAR wszTestFile[MAX_PATH], wszTestFile2[MAX_PATH], wszTestDir[MAX_PATH];
360
    char szTestFile[MAX_PATH], szTestDir[MAX_PATH];
361
    DWORD attr;
362 363
    STRRET strret;
    LPSHELLFOLDER psfDesktop, psfPersonal;
364
    IUnknown *psfFile;
365 366
    SHITEMID emptyitem = { 0, { 0 } };
    LPITEMIDLIST pidlTestFile, pidlEmpty = (LPITEMIDLIST)&emptyitem;
367 368 369 370 371
    LPCITEMIDLIST pidlLast;
    static const WCHAR wszFileName[] = { 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
    static const WCHAR wszDirName[] = { 'w','i','n','e','t','e','s','t',0 };

    /* I'm trying to figure if there is a functional difference between calling
372
     * SHGetPathFromIDListW and calling GetDisplayNameOf(SHGDN_FORPARSING) after
373
     * binding to the shellfolder. One thing I thought of was that perhaps 
374 375
     * SHGetPathFromIDListW would be able to get the path to a file, which does
     * not exist anymore, while the other method wouldn't. It turns out there's
376 377 378
     * no functional difference in this respect.
     */

379 380
    if(!pSHGetSpecialFolderPathW) return;

381
    /* First creating a directory in MyDocuments and a file in this directory. */
382
    result = pSHGetSpecialFolderPathW(NULL, wszTestDir, CSIDL_PERSONAL, FALSE);
383
    ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
384 385
    if (!result) return;

386
    myPathAddBackslashW(wszTestDir);
387
    lstrcatW(wszTestDir, wszDirName);
388
    /* Use ANSI file functions so this works on Windows 9x */
389
    WideCharToMultiByte(CP_ACP, 0, wszTestDir, -1, szTestDir, MAX_PATH, 0, 0);
390 391 392 393 394 395 396
    CreateDirectoryA(szTestDir, NULL);
    attr=GetFileAttributesA(szTestDir);
    if (attr == INVALID_FILE_ATTRIBUTES || !(attr & FILE_ATTRIBUTE_DIRECTORY))
    {
        ok(0, "unable to create the '%s' directory\n", szTestDir);
        return;
    }
397 398

    lstrcpyW(wszTestFile, wszTestDir);
399
    myPathAddBackslashW(wszTestFile);
400
    lstrcatW(wszTestFile, wszFileName);
401
    WideCharToMultiByte(CP_ACP, 0, wszTestFile, -1, szTestFile, MAX_PATH, 0, 0);
402

403
    hTestFile = CreateFileA(szTestFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
404
    ok((hTestFile != INVALID_HANDLE_VALUE), "CreateFileA failed! Last error: %u\n", GetLastError());
405 406 407
    if (hTestFile == INVALID_HANDLE_VALUE) return;
    CloseHandle(hTestFile);

408
    /* Getting an itemidlist for the file. */
409
    hr = SHGetDesktopFolder(&psfDesktop);
410
    ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
411 412 413
    if (FAILED(hr)) return;

    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
414
    ok(SUCCEEDED(hr), "Desktop->ParseDisplayName failed! hr = %08x\n", hr);
415 416 417 418 419
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }

420
    /* WinXP stores the filenames as both ANSI and UNICODE in the pidls */
421
    pidlLast = pILFindLastID(pidlTestFile);
422
    ok(pidlLast->mkid.cb >=76, "Expected pidl length of at least 76, got %d.\n", pidlLast->mkid.cb);
423 424 425 426 427
    if (pidlLast->mkid.cb >= 76) {
        ok(!lstrcmpW((WCHAR*)&pidlLast->mkid.abID[46], wszFileName),
            "WinXP stores the filename as a wchar-string at this position!\n");
    }
    
428
    /* It seems as if we cannot bind to regular files on windows, but only directories. 
429 430
     */
    hr = IShellFolder_BindToObject(psfDesktop, pidlTestFile, NULL, &IID_IUnknown, (VOID**)&psfFile);
431
    todo_wine { ok (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "hr = %08x\n", hr); }
432 433 434
    if (SUCCEEDED(hr)) {
        IShellFolder_Release(psfFile);
    }
Michael Jung's avatar
Michael Jung committed
435 436 437
  
    /* Some tests for IShellFolder::SetNameOf */
    hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
438
    ok(SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
Michael Jung's avatar
Michael Jung committed
439 440 441 442 443 444 445
    if (SUCCEEDED(hr)) {
        /* It's ok to use this fixed path. Call will fail anyway. */
        WCHAR wszAbsoluteFilename[] = { 'C',':','\\','w','i','n','e','t','e','s','t', 0 };
        LPITEMIDLIST pidlNew;

        /* The pidl returned through the last parameter of SetNameOf is a simple one. */
        hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlLast, wszDirName, SHGDN_NORMAL, &pidlNew);
446
        ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
Michael Jung's avatar
Michael Jung committed
447 448 449 450 451 452 453
        ok (((LPITEMIDLIST)((LPBYTE)pidlNew+pidlNew->mkid.cb))->mkid.cb == 0, 
            "pidl returned from SetNameOf should be simple!\n");

        /* Passing an absolute path to SetNameOf fails. The HRESULT code indicates that SetNameOf
         * is implemented on top of SHFileOperation in WinXP. */
        hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszAbsoluteFilename, 
                SHGDN_FORPARSING, NULL);
454
        ok (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED), "SetNameOf succeeded! hr = %08x\n", hr);
455 456

        /* Rename the file back to its original name. SetNameOf ignores the fact, that the
Michael Jung's avatar
Michael Jung committed
457 458
         * SHGDN flags specify an absolute path. */
        hr = IShellFolder_SetNameOf(psfPersonal, NULL, pidlNew, wszFileName, SHGDN_FORPARSING, NULL);
459
        ok (SUCCEEDED(hr), "SetNameOf failed! hr = %08x\n", hr);
Michael Jung's avatar
Michael Jung committed
460

461
        pILFree(pidlNew);
Michael Jung's avatar
Michael Jung committed
462 463
        IShellFolder_Release(psfPersonal);
    }
464

465
    /* Deleting the file and the directory */
466 467
    DeleteFileA(szTestFile);
    RemoveDirectoryA(szTestDir);
468 469

    /* SHGetPathFromIDListW still works, although the file is not present anymore. */
470 471 472 473 474 475
    if (pSHGetPathFromIDListW)
    {
        result = pSHGetPathFromIDListW(pidlTestFile, wszTestFile2);
        ok (result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
        ok (!lstrcmpiW(wszTestFile, wszTestFile2), "SHGetPathFromIDListW returns incorrect path!\n");
    }
476

477 478
    if(!pSHBindToParent) return;

479 480 481 482 483 484
    /* SHBindToParent fails, if called with a NULL PIDL. */
    hr = pSHBindToParent(NULL, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
    ok (FAILED(hr), "SHBindToParent(NULL) should fail!\n");

    /* But it succeeds with an empty PIDL. */
    hr = pSHBindToParent(pidlEmpty, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast);
485
    ok (SUCCEEDED(hr), "SHBindToParent(empty PIDL) should succeed! hr = %08x\n", hr);
486 487 488 489
    ok (pidlLast == pidlEmpty, "The last element of an empty PIDL should be the PIDL itself!\n");
    if (SUCCEEDED(hr)) 
        IShellFolder_Release(psfPersonal);
    
490
    /* Binding to the folder and querying the display name of the file also works. */
491
    hr = pSHBindToParent(pidlTestFile, &IID_IShellFolder, (VOID**)&psfPersonal, &pidlLast); 
492
    ok (SUCCEEDED(hr), "SHBindToParent failed! hr = %08x\n", hr);
493 494 495 496 497
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }

498 499
    /* This test shows that Windows doesn't allocate a new pidlLast, but returns a pointer into 
     * pidlTestFile (In accordance with MSDN). */
500 501
    ok (pILFindLastID(pidlTestFile) == pidlLast, 
                                "SHBindToParent doesn't return the last id of the pidl param!\n");
502
    
503
    hr = IShellFolder_GetDisplayNameOf(psfPersonal, pidlLast, SHGDN_FORPARSING, &strret);
504
    ok (SUCCEEDED(hr), "Personal->GetDisplayNameOf failed! hr = %08x\n", hr);
505 506 507 508 509
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        IShellFolder_Release(psfPersonal);
        return;
    }
510 511 512 513

    if (pStrRetToBufW)
    {
        hr = pStrRetToBufW(&strret, pidlLast, wszTestFile2, MAX_PATH);
514
        ok (SUCCEEDED(hr), "StrRetToBufW failed! hr = %08x\n", hr);
515 516
        ok (!lstrcmpiW(wszTestFile, wszTestFile2), "GetDisplayNameOf returns incorrect path!\n");
    }
517
    
518 519 520 521
    IShellFolder_Release(psfDesktop);
    IShellFolder_Release(psfPersonal);
}

522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
static void test_CallForAttributes(void)
{
    HKEY hKey;
    LONG lResult;
    HRESULT hr;
    DWORD dwSize;
    LPSHELLFOLDER psfDesktop;
    LPITEMIDLIST pidlMyDocuments;
    DWORD dwAttributes, dwCallForAttributes, dwOrigAttributes, dwOrigCallForAttributes;
    static const WCHAR wszAttributes[] = { 'A','t','t','r','i','b','u','t','e','s',0 };
    static const WCHAR wszCallForAttributes[] = { 
        'C','a','l','l','F','o','r','A','t','t','r','i','b','u','t','e','s',0 };
    static const WCHAR wszMyDocumentsKey[] = {
        'C','L','S','I','D','\\','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-',
        '1','1','D','0','-','9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',
        '\\','S','h','e','l','l','F','o','l','d','e','r',0 };
    WCHAR wszMyDocuments[] = {
        ':',':','{','4','5','0','D','8','F','B','A','-','A','D','2','5','-','1','1','D','0','-',
        '9','8','A','8','-','0','8','0','0','3','6','1','B','1','1','0','3','}',0 };
    
    /* For the root of a namespace extension, the attributes are not queried by binding
     * to the object and calling GetAttributesOf. Instead, the attributes are read from 
     * the registry value HKCR/CLSID/{...}/ShellFolder/Attributes. This is documented on MSDN.
     *
     * The MyDocuments shellfolder on WinXP has a HKCR/CLSID/{...}/ShellFolder/CallForAttributes
     * value. It seems that if the folder is queried for one of the flags set in CallForAttributes,
     * the shell does bind to the folder object and calls GetAttributesOf. This is not documented
     * on MSDN. This test is meant to document the observed behaviour on WinXP SP2.
     */
    hr = SHGetDesktopFolder(&psfDesktop);
552
    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
553 554 555 556 557
    if (FAILED(hr)) return;
    
    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyDocuments, NULL, 
                                       &pidlMyDocuments, NULL);
    ok (SUCCEEDED(hr), 
558
        "Desktop's ParseDisplayName failed to parse MyDocuments's CLSID! hr = %08x\n", hr);
559 560 561 562 563 564 565 566
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }

    dwAttributes = 0xffffffff;
    hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
                                      (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
567
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
568 569

    /* We need the following setup (as observed on WinXP SP2), for the tests to make sense. */
570
    ok (dwAttributes & SFGAO_FILESYSTEM, "SFGAO_FILESYSTEM attribute is not set for MyDocuments!\n");
571 572 573 574 575 576 577
    ok (!(dwAttributes & SFGAO_ISSLOW), "SFGAO_ISSLOW attribute is set for MyDocuments!\n");
    ok (!(dwAttributes & SFGAO_GHOSTED), "SFGAO_GHOSTED attribute is set for MyDocuments!\n");

    /* We don't have the MyDocuments shellfolder in wine yet, and thus we don't have the registry
     * key. So the test will return at this point, if run on wine. 
     */
    lResult = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszMyDocumentsKey, 0, KEY_WRITE|KEY_READ, &hKey);
578
    ok (lResult == ERROR_SUCCESS, "RegOpenKeyEx failed! result: %08x\n", lResult);
579
    if (lResult != ERROR_SUCCESS) {
580
        IMalloc_Free(ppM, pidlMyDocuments);
581 582 583 584 585 586 587
        IShellFolder_Release(psfDesktop);
        return;
    }
    
    /* Query MyDocuments' Attributes value, to be able to restore it later. */
    dwSize = sizeof(DWORD);
    lResult = RegQueryValueExW(hKey, wszAttributes, NULL, NULL, (LPBYTE)&dwOrigAttributes, &dwSize);
588
    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
589 590
    if (lResult != ERROR_SUCCESS) {
        RegCloseKey(hKey);
591
        IMalloc_Free(ppM, pidlMyDocuments);
592 593 594 595 596 597 598 599
        IShellFolder_Release(psfDesktop);
        return;
    }

    /* Query MyDocuments' CallForAttributes value, to be able to restore it later. */
    dwSize = sizeof(DWORD);
    lResult = RegQueryValueExW(hKey, wszCallForAttributes, NULL, NULL, 
                              (LPBYTE)&dwOrigCallForAttributes, &dwSize);
600
    ok (lResult == ERROR_SUCCESS, "RegQueryValueEx failed! result: %08x\n", lResult);
601 602
    if (lResult != ERROR_SUCCESS) {
        RegCloseKey(hKey);
603
        IMalloc_Free(ppM, pidlMyDocuments);
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
        IShellFolder_Release(psfDesktop);
        return;
    }
    
    /* Define via the Attributes value that MyDocuments attributes are SFGAO_ISSLOW and 
     * SFGAO_GHOSTED and that MyDocuments should be called for the SFGAO_ISSLOW and
     * SFGAO_FILESYSTEM attributes. */
    dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED;
    RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwAttributes, sizeof(DWORD));
    dwCallForAttributes = SFGAO_ISSLOW|SFGAO_FILESYSTEM;
    RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
                   (LPBYTE)&dwCallForAttributes, sizeof(DWORD));

    /* Although it is not set in CallForAttributes, the SFGAO_GHOSTED flag is reset by 
     * GetAttributesOf. It seems that once there is a single attribute queried, for which
     * CallForAttributes is set, all flags are taken from the GetAttributesOf call and
     * the flags in Attributes are ignored. 
     */
    dwAttributes = SFGAO_ISSLOW|SFGAO_GHOSTED|SFGAO_FILESYSTEM;
    hr = IShellFolder_GetAttributesOf(psfDesktop, 1, 
                                      (LPCITEMIDLIST*)&pidlMyDocuments, &dwAttributes);
625
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyDocuments) failed! hr = %08x\n", hr);
626 627
    if (SUCCEEDED(hr)) 
        ok (dwAttributes == SFGAO_FILESYSTEM, 
628
            "Desktop->GetAttributes(MyDocuments) returned unexpected attributes: %08x\n", 
629 630 631 632 633 634 635
            dwAttributes);

    /* Restore MyDocuments' original Attributes and CallForAttributes registry values */
    RegSetValueExW(hKey, wszAttributes, 0, REG_DWORD, (LPBYTE)&dwOrigAttributes, sizeof(DWORD));
    RegSetValueExW(hKey, wszCallForAttributes, 0, REG_DWORD, 
                   (LPBYTE)&dwOrigCallForAttributes, sizeof(DWORD));
    RegCloseKey(hKey);
636
    IMalloc_Free(ppM, pidlMyDocuments);
637 638 639
    IShellFolder_Release(psfDesktop);
}

640 641 642
static void test_GetAttributesOf(void) 
{
    HRESULT hr;
643
    LPSHELLFOLDER psfDesktop, psfMyComputer;
644 645
    SHITEMID emptyitem = { 0, { 0 } };
    LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
646
    LPITEMIDLIST pidlMyComputer;
647
    DWORD dwFlags;
648
    static const DWORD dwDesktopFlags = /* As observed on WinXP SP2 */
649
        SFGAO_STORAGE | SFGAO_HASPROPSHEET | SFGAO_STORAGEANCESTOR |
650
        SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_HASSUBFOLDER;
651
    static const DWORD dwMyComputerFlags = /* As observed on WinXP SP2 */
652 653 654 655 656
        SFGAO_CANRENAME | SFGAO_CANDELETE | SFGAO_HASPROPSHEET |
        SFGAO_DROPTARGET | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER;
    WCHAR wszMyComputer[] = { 
        ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
        'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
657 658
    char  cCurrDirA [MAX_PATH] = {0};
    WCHAR cCurrDirW [MAX_PATH];
659
    static WCHAR cTestDirW[] = {'t','e','s','t','d','i','r',0};
660 661 662 663
    static const WCHAR cBackSlash[] = {'\\',0};
    IShellFolder *IDesktopFolder, *testIShellFolder;
    ITEMIDLIST *newPIDL;
    int len;
664

665
    hr = SHGetDesktopFolder(&psfDesktop);
666
    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
667 668 669 670 671
    if (FAILED(hr)) return;

    /* The Desktop attributes can be queried with a single empty itemidlist, .. */
    dwFlags = 0xffffffff;
    hr = IShellFolder_GetAttributesOf(psfDesktop, 1, &pidlEmpty, &dwFlags);
672 673
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(empty pidl) failed! hr = %08x\n", hr);
    ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08x, expected: %08x\n", 
674 675 676 677 678
        dwFlags, dwDesktopFlags);

    /* .. or with no itemidlist at all. */
    dwFlags = 0xffffffff;
    hr = IShellFolder_GetAttributesOf(psfDesktop, 0, NULL, &dwFlags);
679 680
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(NULL) failed! hr = %08x\n", hr);
    ok (dwFlags == dwDesktopFlags, "Wrong Desktop attributes: %08x, expected: %08x\n", 
681
        dwFlags, dwDesktopFlags);
682 683 684
   
    /* Testing the attributes of the MyComputer shellfolder */
    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
685
    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
686 687 688 689 690 691 692 693 694 695 696 697 698 699
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }

    /* WinXP SP2 sets the SFGAO_CANLINK flag, when MyComputer is queried via the Desktop 
     * folder object. It doesn't do this, if MyComputer is queried directly (see below).
     * SFGAO_CANLINK is the same as DROPEFFECT_LINK, which MSDN says means: "Drag source
     * should create a link to the original data". You can't create links on MyComputer on
     * Windows, so this flag shouldn't be set. Seems like a bug in Windows. As long as nobody
     * depends on this bug, we probably shouldn't imitate it.
     */
    dwFlags = 0xffffffff;
    hr = IShellFolder_GetAttributesOf(psfDesktop, 1, (LPCITEMIDLIST*)&pidlMyComputer, &dwFlags);
700
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf(MyComputer) failed! hr = %08x\n", hr);
701
    ok ((dwFlags & ~(DWORD)SFGAO_CANLINK) == dwMyComputerFlags, 
702
                    "Wrong MyComputer attributes: %08x, expected: %08x\n", dwFlags, dwMyComputerFlags);
703 704

    hr = IShellFolder_BindToObject(psfDesktop, pidlMyComputer, NULL, &IID_IShellFolder, (LPVOID*)&psfMyComputer);
705
    ok (SUCCEEDED(hr), "Desktop failed to bind to MyComputer object! hr = %08x\n", hr);
706
    IShellFolder_Release(psfDesktop);
707
    IMalloc_Free(ppM, pidlMyComputer);
708 709 710
    if (FAILED(hr)) return;

    hr = IShellFolder_GetAttributesOf(psfMyComputer, 1, &pidlEmpty, &dwFlags);
711
    todo_wine {ok (hr == E_INVALIDARG, "MyComputer->GetAttributesOf(emtpy pidl) should fail! hr = %08x\n", hr); }
712 713 714

    dwFlags = 0xffffffff;
    hr = IShellFolder_GetAttributesOf(psfMyComputer, 0, NULL, &dwFlags);
715
    ok (SUCCEEDED(hr), "MyComputer->GetAttributesOf(NULL) failed! hr = %08x\n", hr); 
716
    todo_wine { ok (dwFlags == dwMyComputerFlags, 
717
                    "Wrong MyComputer attributes: %08x, expected: %08x\n", dwFlags, dwMyComputerFlags); }
718 719

    IShellFolder_Release(psfMyComputer);
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736

    /* create test directory */
    CreateFilesFolders();

    GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
    len = lstrlenA(cCurrDirA);

    if (len == 0) {
	trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
	return;
    }
    if(cCurrDirA[len-1] == '\\')
	cCurrDirA[len-1] = 0;

    MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
 
    hr = SHGetDesktopFolder(&IDesktopFolder);
737
    ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
738 739

    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
740
    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
741 742

    hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
743
    ok(hr == S_OK, "BindToObject failed %08x\n", hr);
744 745 746 747

    IMalloc_Free(ppM, newPIDL);

    /* get relative PIDL */
748
    hr = IShellFolder_ParseDisplayName(testIShellFolder, NULL, NULL, cTestDirW, NULL, &newPIDL, 0);
749
    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
750 751 752 753

    /* test the shell attributes of the test directory using the relative PIDL */
    dwFlags = SFGAO_FOLDER;
    hr = IShellFolder_GetAttributesOf(testIShellFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
754 755
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
    ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for relative PIDL: %08x\n", dwFlags);
756 757 758 759 760

    /* free memory */
    IMalloc_Free(ppM, newPIDL);

    /* append testdirectory name to path */
761 762
    lstrcatW(cCurrDirW, cBackSlash);
    lstrcatW(cCurrDirW, cTestDirW);
763 764

    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
765
    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
766 767 768 769

    /* test the shell attributes of the test directory using the absolute PIDL */
    dwFlags = SFGAO_FOLDER;
    hr = IShellFolder_GetAttributesOf(IDesktopFolder, 1, (LPCITEMIDLIST*)&newPIDL, &dwFlags);
770 771
    ok (SUCCEEDED(hr), "Desktop->GetAttributesOf() failed! hr = %08x\n", hr);
    ok ((dwFlags&SFGAO_FOLDER), "Wrong directory attribute for absolute PIDL: %08x\n", dwFlags);
772 773 774 775
        
    /* free memory */
    IMalloc_Free(ppM, newPIDL);

776
    IShellFolder_Release(testIShellFolder);
777 778 779

    Cleanup();

780
    IShellFolder_Release(IDesktopFolder);
781 782
}    

783 784 785 786 787 788 789 790 791 792 793 794
static void test_SHGetPathFromIDList(void)
{
    SHITEMID emptyitem = { 0, { 0 } };
    LPCITEMIDLIST pidlEmpty = (LPCITEMIDLIST)&emptyitem;
    LPITEMIDLIST pidlMyComputer;
    WCHAR wszPath[MAX_PATH], wszDesktop[MAX_PATH];
    BOOL result;
    HRESULT hr;
    LPSHELLFOLDER psfDesktop;
    WCHAR wszMyComputer[] = { 
        ':',':','{','2','0','D','0','4','F','E','0','-','3','A','E','A','-','1','0','6','9','-',
        'A','2','D','8','-','0','8','0','0','2','B','3','0','3','0','9','D','}',0 };
795 796 797 798 799 800
    WCHAR wszFileName[MAX_PATH];
    LPITEMIDLIST pidlTestFile;
    HANDLE hTestFile;
    STRRET strret;
    static WCHAR wszTestFile[] = {
        'w','i','n','e','t','e','s','t','.','f','o','o',0 };
801 802 803
	HRESULT (WINAPI *pSHGetSpecialFolderLocation)(HWND, int, LPITEMIDLIST *);
	HMODULE hShell32;
	LPITEMIDLIST pidlPrograms;
804

805 806 807 808 809
    if(!pSHGetPathFromIDListW || !pSHGetSpecialFolderPathW)
    {
        skip("SHGetPathFromIDListW() or SHGetSpecialFolderPathW() is missing\n");
        return;
    }
810

811
    /* Calling SHGetPathFromIDListW with no pidl should return the empty string */
812 813
    wszPath[0] = 'a';
    wszPath[1] = '\0';
814
    result = pSHGetPathFromIDListW(NULL, wszPath);
815 816 817
    ok(!result, "Expected failure\n");
    ok(!wszPath[0], "Expected empty string\n");

818
    /* Calling SHGetPathFromIDListW with an empty pidl should return the desktop folder's path. */
819
    result = pSHGetSpecialFolderPathW(NULL, wszDesktop, CSIDL_DESKTOP, FALSE);
820
    ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOP) failed! Last error: %u\n", GetLastError());
821 822
    if (!result) return;
    
823
    result = pSHGetPathFromIDListW(pidlEmpty, wszPath);
824
    ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
825
    if (!result) return;
826
    ok(!lstrcmpiW(wszDesktop, wszPath), "SHGetPathFromIDListW didn't return desktop path for empty pidl!\n");
827

828
    /* MyComputer does not map to a filesystem path. SHGetPathFromIDListW should fail. */
829
    hr = SHGetDesktopFolder(&psfDesktop);
830
    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
831 832 833
    if (FAILED(hr)) return;

    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL);
834
    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08x\n", hr);
835 836 837 838
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }
839 840

    SetLastError(0xdeadbeef);
841 842
    wszPath[0] = 'a';
    wszPath[1] = '\0';
843 844 845
    result = pSHGetPathFromIDListW(pidlMyComputer, wszPath);
    ok (!result, "SHGetPathFromIDListW succeeded where it shouldn't!\n");
    ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDListW shouldn't set last error! Last error: %u\n", GetLastError());
846
    ok (!wszPath[0], "Expected empty path\n");
847 848 849 850
    if (result) {
        IShellFolder_Release(psfDesktop);
        return;
    }
851

852
    IMalloc_Free(ppM, pidlMyComputer);
853 854

    result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE);
855
    ok(result, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
856 857 858 859
    if (!result) {
        IShellFolder_Release(psfDesktop);
        return;
    }
860
    myPathAddBackslashW(wszFileName);
861 862
    lstrcatW(wszFileName, wszTestFile);
    hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL);
863
    ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %u\n", GetLastError());
864 865 866 867 868 869 870
    if (hTestFile == INVALID_HANDLE_VALUE) {
        IShellFolder_Release(psfDesktop);
        return;
    }
    CloseHandle(hTestFile);

    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL);
871
    ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08x\n", hr);
872 873 874 875 876 877 878 879 880 881
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        DeleteFileW(wszFileName);
        IMalloc_Free(ppM, pidlTestFile);
        return;
    }

    /* This test is to show that the Desktop shellfolder prepends the CSIDL_DESKTOPDIRECTORY
     * path for files placed on the desktop, if called with SHGDN_FORPARSING. */
    hr = IShellFolder_GetDisplayNameOf(psfDesktop, pidlTestFile, SHGDN_FORPARSING, &strret);
882
    ok (SUCCEEDED(hr), "Desktop's GetDisplayNamfOf failed! hr = %08x\n", hr);
883 884 885 886 887 888
    IShellFolder_Release(psfDesktop);
    DeleteFileW(wszFileName);
    if (FAILED(hr)) {
        IMalloc_Free(ppM, pidlTestFile);
        return;
    }
889 890 891 892 893 894 895
    if (pStrRetToBufW)
    {
        pStrRetToBufW(&strret, pidlTestFile, wszPath, MAX_PATH);
        ok(0 == lstrcmpW(wszFileName, wszPath), 
           "Desktop->GetDisplayNameOf(pidlTestFile, SHGDN_FORPARSING) "
           "returned incorrect path for file placed on desktop\n");
    }
896

897
    result = pSHGetPathFromIDListW(pidlTestFile, wszPath);
898
    ok(result, "SHGetPathFromIDListW failed! Last error: %u\n", GetLastError());
899 900 901
    IMalloc_Free(ppM, pidlTestFile);
    if (!result) return;
    ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n");
902 903 904 905


	/* Test if we can get the path from the start menu "program files" PIDL. */
    hShell32 = GetModuleHandleA("shell32");
906
    pSHGetSpecialFolderLocation = (void *)GetProcAddress(hShell32, "SHGetSpecialFolderLocation");
907 908

    hr = pSHGetSpecialFolderLocation(NULL, CSIDL_PROGRAM_FILES, &pidlPrograms);
909
    ok(SUCCEEDED(hr), "SHGetFolderLocation failed: 0x%08x\n", hr);
910 911

    SetLastError(0xdeadbeef);
912
    result = pSHGetPathFromIDListW(pidlPrograms, wszPath);
913
	IMalloc_Free(ppM, pidlPrograms);
914
    ok(result, "SHGetPathFromIDListW failed\n");
915 916
}

917
static void test_EnumObjects_and_CompareIDs(void)
918 919 920
{
    ITEMIDLIST *newPIDL;
    IShellFolder *IDesktopFolder, *testIShellFolder;
921
    char  cCurrDirA [MAX_PATH] = {0};
922 923
    WCHAR cCurrDirW [MAX_PATH];
    static const WCHAR cTestDirW[] = {'\\','t','e','s','t','d','i','r',0};
924
    int len;
925
    HRESULT hr;
926

927
    GetCurrentDirectoryA(MAX_PATH, cCurrDirA);
928 929 930 931 932 933 934 935 936
    len = lstrlenA(cCurrDirA);

    if(len == 0) {
        trace("GetCurrentDirectoryA returned empty string. Skipping test_EnumObjects_and_CompareIDs\n");
        return;
    }
    if(cCurrDirA[len-1] == '\\')
        cCurrDirA[len-1] = 0;

937
    MultiByteToWideChar(CP_ACP, 0, cCurrDirA, -1, cCurrDirW, MAX_PATH);
938
    lstrcatW(cCurrDirW, cTestDirW);
939

940
    hr = SHGetDesktopFolder(&IDesktopFolder);
941
    ok(hr == S_OK, "SHGetDesktopfolder failed %08x\n", hr);
942

943 944
    CreateFilesFolders();

945
    hr = IShellFolder_ParseDisplayName(IDesktopFolder, NULL, NULL, cCurrDirW, NULL, &newPIDL, 0);
946
    ok(hr == S_OK, "ParseDisplayName failed %08x\n", hr);
947

948
    hr = IShellFolder_BindToObject(IDesktopFolder, newPIDL, NULL, (REFIID)&IID_IShellFolder, (LPVOID *)&testIShellFolder);
949
    ok(hr == S_OK, "BindToObject failed %08x\n", hr);
950

951 952
    test_EnumObjects(testIShellFolder);

953
    IShellFolder_Release(testIShellFolder);
954

955
    Cleanup();
956 957

    IMalloc_Free(ppM, newPIDL);
958

959
    IShellFolder_Release(IDesktopFolder);
960
}
961

962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000
/* A simple implementation of an IPropertyBag, which returns fixed values for
 * 'Target' and 'Attributes' properties.
 */
static HRESULT WINAPI InitPropertyBag_IPropertyBag_QueryInterface(IPropertyBag *iface, REFIID riid,
    void **ppvObject) 
{
    if (!ppvObject)
        return E_INVALIDARG;

    if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IPropertyBag, riid)) {
        *ppvObject = iface;
    } else {
        ok (FALSE, "InitPropertyBag asked for unknown interface!\n");
        return E_NOINTERFACE;
    }

    IPropertyBag_AddRef(iface);
    return S_OK;
}

static ULONG WINAPI InitPropertyBag_IPropertyBag_AddRef(IPropertyBag *iface) {
    return 2;
}

static ULONG WINAPI InitPropertyBag_IPropertyBag_Release(IPropertyBag *iface) {
    return 1;
}

static HRESULT WINAPI InitPropertyBag_IPropertyBag_Read(IPropertyBag *iface, LPCOLESTR pszPropName,
    VARIANT *pVar, IErrorLog *pErrorLog)
{
    static const WCHAR wszTargetSpecialFolder[] = {
        'T','a','r','g','e','t','S','p','e','c','i','a','l','F','o','l','d','e','r',0 };
    static const WCHAR wszTarget[] = {
        'T','a','r','g','e','t',0 };
    static const WCHAR wszAttributes[] = {
        'A','t','t','r','i','b','u','t','e','s',0 };
    static const WCHAR wszResolveLinkFlags[] = {
        'R','e','s','o','l','v','e','L','i','n','k','F','l','a','g','s',0 };
1001 1002 1003 1004 1005 1006 1007
       
    if (!lstrcmpW(pszPropName, wszTargetSpecialFolder)) {
        ok(V_VT(pVar) == VT_I4, "Wrong variant type for 'TargetSpecialFolder' property!\n");
        return E_INVALIDARG;
    }
    
    if (!lstrcmpW(pszPropName, wszResolveLinkFlags)) 
1008
    {
1009
        ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'ResolveLinkFlags' property!\n");
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020
        return E_INVALIDARG;
    }

    if (!lstrcmpW(pszPropName, wszTarget)) {
        WCHAR wszPath[MAX_PATH];
        BOOL result;
        
        ok(V_VT(pVar) == VT_BSTR, "Wrong variant type for 'Target' property!\n");
        if (V_VT(pVar) != VT_BSTR) return E_INVALIDARG;

        result = pSHGetSpecialFolderPathW(NULL, wszPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1021
        ok(result, "SHGetSpecialFolderPathW(DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
        if (!result) return E_INVALIDARG;

        V_BSTR(pVar) = SysAllocString(wszPath);
        return S_OK;
    }

    if (!lstrcmpW(pszPropName, wszAttributes)) {
        ok(V_VT(pVar) == VT_UI4, "Wrong variant type for 'Attributes' property!\n");
        if (V_VT(pVar) != VT_UI4) return E_INVALIDARG;
        V_UI4(pVar) = SFGAO_FOLDER|SFGAO_HASSUBFOLDER|SFGAO_FILESYSANCESTOR|
                      SFGAO_CANRENAME|SFGAO_FILESYSTEM;
        return S_OK;
    }

    ok(FALSE, "PropertyBag was asked for unknown property (vt=%d)!\n", V_VT(pVar));
    return E_INVALIDARG;
}

static HRESULT WINAPI InitPropertyBag_IPropertyBag_Write(IPropertyBag *iface, LPCOLESTR pszPropName,
    VARIANT *pVar)
{
    ok(FALSE, "Unexpected call to IPropertyBag_Write\n");
    return E_NOTIMPL;
}
    
static const IPropertyBagVtbl InitPropertyBag_IPropertyBagVtbl = {
    InitPropertyBag_IPropertyBag_QueryInterface,
    InitPropertyBag_IPropertyBag_AddRef,
    InitPropertyBag_IPropertyBag_Release,
    InitPropertyBag_IPropertyBag_Read,
    InitPropertyBag_IPropertyBag_Write
};

1055
static struct IPropertyBag InitPropertyBag = {
1056 1057 1058
    &InitPropertyBag_IPropertyBagVtbl
};

1059
static void test_FolderShortcut(void) {
1060
    IPersistPropertyBag *pPersistPropertyBag;
1061
    IShellFolder *pShellFolder, *pDesktopFolder;
1062 1063 1064
    IPersistFolder3 *pPersistFolder3;
    HRESULT hr;
    STRRET strret;
1065
    WCHAR wszDesktopPath[MAX_PATH], wszBuffer[MAX_PATH];
1066 1067
    BOOL result;
    CLSID clsid;
1068
    LPITEMIDLIST pidlCurrentFolder, pidlWineTestFolder, pidlSubFolder;
1069
    HKEY hShellExtKey;
1070 1071 1072
    WCHAR wszWineTestFolder[] = {
        ':',':','{','9','B','3','5','2','E','B','F','-','2','7','6','5','-','4','5','C','1','-',
        'B','4','C','6','-','8','5','C','C','7','F','7','A','B','C','6','4','}',0 };
1073 1074 1075 1076 1077 1078 1079 1080
    WCHAR wszShellExtKey[] = { 'S','o','f','t','w','a','r','e','\\',
        'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
        'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
        'E','x','p','l','o','r','e','r','\\','D','e','s','k','t','o','p','\\',
        'N','a','m','e','S','p','a','c','e','\\',
        '{','9','b','3','5','2','e','b','f','-','2','7','6','5','-','4','5','c','1','-',
        'b','4','c','6','-','8','5','c','c','7','f','7','a','b','c','6','4','}',0 };
    
1081
    WCHAR wszSomeSubFolder[] = { 'S','u','b','F','o','l','d','e','r', 0};
1082 1083 1084
    static const GUID CLSID_UnixDosFolder = 
        {0x9d20aae8, 0x0625, 0x44b0, {0x9c, 0xa7, 0x71, 0x88, 0x9c, 0x22, 0x54, 0xd9}};

1085 1086 1087 1088 1089 1090 1091 1092
    if (!pSHGetSpecialFolderPathW || !pStrRetToBufW) return;
   
    /* These tests basically show, that CLSID_FolderShortcuts are initialized
     * via their IPersistPropertyBag interface. And that the target folder
     * is taken from the IPropertyBag's 'Target' property.
     */
    hr = CoCreateInstance(&CLSID_FolderShortcut, NULL, CLSCTX_INPROC_SERVER, 
                          &IID_IPersistPropertyBag, (LPVOID*)&pPersistPropertyBag);
1093
    ok (SUCCEEDED(hr), "CoCreateInstance failed! hr = 0x%08x\n", hr);
1094 1095 1096
    if (FAILED(hr)) return;

    hr = IPersistPropertyBag_Load(pPersistPropertyBag, &InitPropertyBag, NULL);
1097
    ok(SUCCEEDED(hr), "IPersistPropertyBag_Load failed! hr = %08x\n", hr);
1098 1099 1100 1101 1102 1103 1104 1105
    if (FAILED(hr)) {
        IPersistPropertyBag_Release(pPersistPropertyBag);
        return;
    }
    
    hr = IPersistPropertyBag_QueryInterface(pPersistPropertyBag, &IID_IShellFolder, 
                                            (LPVOID*)&pShellFolder);
    IPersistPropertyBag_Release(pPersistPropertyBag);
1106
    ok(SUCCEEDED(hr), "IPersistPropertyBag_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1107 1108 1109
    if (FAILED(hr)) return;

    hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1110
    ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1111 1112 1113 1114 1115
    if (FAILED(hr)) {
        IShellFolder_Release(pShellFolder);
        return;
    }

1116
    result = pSHGetSpecialFolderPathW(NULL, wszDesktopPath, CSIDL_DESKTOPDIRECTORY, FALSE);
1117
    ok(result, "SHGetSpecialFolderPathW(CSIDL_DESKTOPDIRECTORY) failed! %u\n", GetLastError());
1118 1119 1120
    if (!result) return;

    pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
1121
    ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");
1122 1123 1124

    hr = IShellFolder_QueryInterface(pShellFolder, &IID_IPersistFolder3, (LPVOID*)&pPersistFolder3);
    IShellFolder_Release(pShellFolder);
1125
    ok(SUCCEEDED(hr), "IShellFolder_QueryInterface(IID_IPersistFolder3 failed! hr = 0x%08x\n", hr);
1126 1127 1128
    if (FAILED(hr)) return;

    hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1129
    ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1130 1131 1132
    ok(IsEqualCLSID(&clsid, &CLSID_FolderShortcut), "Unexpected CLSID!\n");

    hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1133
    ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1134 1135
    ok(!pidlCurrentFolder, "IPersistFolder3_GetCurFolder should return a NULL pidl!\n");
                    
1136 1137 1138 1139 1140 1141 1142
    /* For FolderShortcut objects, the Initialize method initialized the folder's position in the
     * shell namespace. The target folder, read from the property bag above, remains untouched. 
     * The following tests show this: The itemidlist for some imaginary shellfolder object
     * is created and the FolderShortcut is initialized with it. GetCurFolder now returns this
     * itemidlist, but GetDisplayNameOf still returns the path from above.
     */
    hr = SHGetDesktopFolder(&pDesktopFolder);
1143
    ok (SUCCEEDED(hr), "SHGetDesktopFolder failed! hr = %08x\n", hr);
1144 1145
    if (FAILED(hr)) return;

1146 1147 1148 1149
    /* Temporarily register WineTestFolder as a shell namespace extension at the Desktop. 
     * Otherwise ParseDisplayName fails on WinXP with E_INVALIDARG */
    RegCreateKeyW(HKEY_CURRENT_USER, wszShellExtKey, &hShellExtKey);
    RegCloseKey(hShellExtKey);
1150 1151
    hr = IShellFolder_ParseDisplayName(pDesktopFolder, NULL, NULL, wszWineTestFolder, NULL,
                                       &pidlWineTestFolder, NULL);
1152
    RegDeleteKeyW(HKEY_CURRENT_USER, wszShellExtKey);
1153
    IShellFolder_Release(pDesktopFolder);
1154
    ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1155 1156 1157
    if (FAILED(hr)) return;

    hr = IPersistFolder3_Initialize(pPersistFolder3, pidlWineTestFolder);
1158
    ok (SUCCEEDED(hr), "IPersistFolder3::Initialize failed! hr = %08x\n", hr);
1159 1160
    if (FAILED(hr)) {
        IPersistFolder3_Release(pPersistFolder3);
1161
        pILFree(pidlWineTestFolder);
1162 1163
        return;
    }
1164

1165
    hr = IPersistFolder3_GetCurFolder(pPersistFolder3, &pidlCurrentFolder);
1166
    ok(SUCCEEDED(hr), "IPersistFolder3_GetCurFolder failed! hr=0x%08x\n", hr);
1167
    ok(pILIsEqual(pidlCurrentFolder, pidlWineTestFolder),
1168
        "IPersistFolder3_GetCurFolder should return pidlWineTestFolder!\n");
1169 1170 1171
    pILFree(pidlCurrentFolder);
    pILFree(pidlWineTestFolder);

1172 1173
    hr = IPersistFolder3_QueryInterface(pPersistFolder3, &IID_IShellFolder, (LPVOID*)&pShellFolder);
    IPersistFolder3_Release(pPersistFolder3);
1174
    ok(SUCCEEDED(hr), "IPersistFolder3_QueryInterface(IShellFolder) failed! hr = %08x\n", hr);
1175 1176 1177
    if (FAILED(hr)) return;

    hr = IShellFolder_GetDisplayNameOf(pShellFolder, NULL, SHGDN_FORPARSING, &strret);
1178
    ok(SUCCEEDED(hr), "IShellFolder_GetDisplayNameOf(NULL) failed! hr = %08x\n", hr);
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
    if (FAILED(hr)) {
        IShellFolder_Release(pShellFolder);
        return;
    }

    pStrRetToBufW(&strret, NULL, wszBuffer, MAX_PATH);
    ok(!lstrcmpiW(wszDesktopPath, wszBuffer), "FolderShortcut returned incorrect folder!\n");

    /* Next few lines are meant to show that children of FolderShortcuts are not FolderShortcuts,
     * but ShellFSFolders. */
1189
    myPathAddBackslashW(wszDesktopPath);
1190 1191 1192 1193 1194 1195 1196 1197
    lstrcatW(wszDesktopPath, wszSomeSubFolder);
    if (!CreateDirectoryW(wszDesktopPath, NULL)) {
        IShellFolder_Release(pShellFolder);
        return;
    }
    
    hr = IShellFolder_ParseDisplayName(pShellFolder, NULL, NULL, wszSomeSubFolder, NULL, 
                                       &pidlSubFolder, NULL);
1198
    RemoveDirectoryW(wszDesktopPath);
1199
    ok (SUCCEEDED(hr), "IShellFolder::ParseDisplayName failed! hr = %08x\n", hr);
1200 1201 1202 1203 1204
    if (FAILED(hr)) {
        IShellFolder_Release(pShellFolder);
        return;
    }

1205
    hr = IShellFolder_BindToObject(pShellFolder, pidlSubFolder, NULL, &IID_IPersistFolder3,
1206 1207
                                   (LPVOID*)&pPersistFolder3);
    IShellFolder_Release(pShellFolder);
1208
    pILFree(pidlSubFolder);
1209
    ok (SUCCEEDED(hr), "IShellFolder::BindToObject failed! hr = %08x\n", hr);
1210
    if (FAILED(hr))
1211 1212
        return;

1213 1214
    /* On windows, we expect CLSID_ShellFSFolder. On wine we relax this constraint
     * a little bit and also allow CLSID_UnixDosFolder. */
1215
    hr = IPersistFolder3_GetClassID(pPersistFolder3, &clsid);
1216
    ok(SUCCEEDED(hr), "IPersistFolder3_GetClassID failed! hr=0x%08x\n", hr);
1217 1218
    ok(IsEqualCLSID(&clsid, &CLSID_ShellFSFolder) || IsEqualCLSID(&clsid, &CLSID_UnixDosFolder),
        "IPersistFolder3::GetClassID returned unexpected CLSID!\n");
1219

1220 1221 1222
    IPersistFolder3_Release(pPersistFolder3);
}

1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248
#include "pshpack1.h"
struct FileStructA {
    BYTE  type;
    BYTE  dummy;
    DWORD dwFileSize;
    WORD  uFileDate;    /* In our current implementation this is */
    WORD  uFileTime;    /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastWriteTime) */
    WORD  uFileAttribs;
    CHAR  szName[1];
};

struct FileStructW {
    WORD  cbLen;        /* Length of this element. */
    BYTE  abFooBar1[6]; /* Beyond any recognition. */
    WORD  uDate;        /* FileTimeToDosDate(WIN32_FIND_DATA->ftCreationTime)? */
    WORD  uTime;        /* (this is currently speculation) */
    WORD  uDate2;       /* FileTimeToDosDate(WIN32_FIND_DATA->ftLastAccessTime)? */
    WORD  uTime2;       /* (this is currently speculation) */
    BYTE  abFooBar2[4]; /* Beyond any recognition. */
    WCHAR wszName[1];   /* The long filename in unicode. */
    /* Just for documentation: Right after the unicode string: */
    WORD  cbOffset;     /* FileStructW's offset from the beginning of the SHITMEID. 
                         * SHITEMID->cb == uOffset + cbLen */
};
#include "poppack.h"

1249
static void test_ITEMIDLIST_format(void) {
1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
    WCHAR wszPersonal[MAX_PATH];
    LPSHELLFOLDER psfDesktop, psfPersonal;
    LPITEMIDLIST pidlPersonal, pidlFile;
    HANDLE hFile;
    HRESULT hr;
    BOOL bResult;
    WCHAR wszFile[3][17] = { { 'e','v','e','n','_',0 }, { 'o','d','d','_',0 }, 
        { 'l','o','n','g','e','r','_','t','h','a','n','.','8','_','3',0 } };
    int i;
    
    if(!pSHGetSpecialFolderPathW) return;

    bResult = pSHGetSpecialFolderPathW(NULL, wszPersonal, CSIDL_PERSONAL, FALSE);
1263
    ok(bResult, "SHGetSpecialFolderPathW failed! Last error: %u\n", GetLastError());
1264 1265 1266
    if (!bResult) return;

    bResult = SetCurrentDirectoryW(wszPersonal);
1267
    ok(bResult, "SetCurrentDirectory failed! Last error: %u\n", GetLastError());
1268 1269 1270
    if (!bResult) return;

    hr = SHGetDesktopFolder(&psfDesktop);
1271
    ok(SUCCEEDED(hr), "SHGetDesktopFolder failed! hr: %08x\n", hr);
1272 1273 1274
    if (FAILED(hr)) return;

    hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszPersonal, NULL, &pidlPersonal, NULL);
1275
    ok(SUCCEEDED(hr), "psfDesktop->ParseDisplayName failed! hr = %08x\n", hr);
1276 1277 1278 1279 1280
    if (FAILED(hr)) {
        IShellFolder_Release(psfDesktop);
        return;
    }

1281
    hr = IShellFolder_BindToObject(psfDesktop, pidlPersonal, NULL, &IID_IShellFolder,
1282 1283
        (LPVOID*)&psfPersonal);
    IShellFolder_Release(psfDesktop);
1284
    pILFree(pidlPersonal);
1285
    ok(SUCCEEDED(hr), "psfDesktop->BindToObject failed! hr = %08x\n", hr);
1286 1287 1288 1289 1290 1291 1292 1293 1294 1295
    if (FAILED(hr)) return;

    for (i=0; i<3; i++) {
        CHAR szFile[MAX_PATH];
        struct FileStructA *pFileStructA;
        WORD cbOffset;
        
        WideCharToMultiByte(CP_ACP, 0, wszFile[i], -1, szFile, MAX_PATH, NULL, NULL);
        
        hFile = CreateFileW(wszFile[i], GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_FLAG_WRITE_THROUGH, NULL);
1296
        ok(hFile != INVALID_HANDLE_VALUE, "CreateFile failed! (%u)\n", GetLastError());
1297 1298 1299 1300 1301 1302 1303 1304
        if (hFile == INVALID_HANDLE_VALUE) {
            IShellFolder_Release(psfPersonal);
            return;
        }
        CloseHandle(hFile);

        hr = IShellFolder_ParseDisplayName(psfPersonal, NULL, NULL, wszFile[i], NULL, &pidlFile, NULL);
        DeleteFileW(wszFile[i]);
1305
        ok(SUCCEEDED(hr), "psfPersonal->ParseDisplayName failed! hr: %08x\n", hr);
1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326
        if (FAILED(hr)) {
            IShellFolder_Release(psfPersonal);
            return;
        }

        pFileStructA = (struct FileStructA *)pidlFile->mkid.abID;
        ok(pFileStructA->type == 0x32, "PIDLTYPE should be 0x32!\n");
        ok(pFileStructA->dummy == 0x00, "Dummy Byte should be 0x00!\n");
        ok(pFileStructA->dwFileSize == 0, "Filesize should be zero!\n"); 

        if (i < 2) /* First two file names are already in valid 8.3 format */ 
            ok(!strcmp(szFile, (CHAR*)&pidlFile->mkid.abID[12]), "Wrong file name!\n");
        else 
            /* WinXP stores a derived 8.3 dos name (LONGER~1.8_3) here. We probably
             * can't implement this correctly, since unix filesystems don't support
             * this nasty short/long filename stuff. So we'll probably stay with our
             * current habbit of storing the long filename here, which seems to work
             * just fine. */
            todo_wine { ok(pidlFile->mkid.abID[18] == '~', "Should be derived 8.3 name!\n"); }

        if (i == 0) /* First file name has an even number of chars. No need for alignment. */
1327 1328
            ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] != '\0', 
                "Alignment byte, where there shouldn't be!\n"); 
1329 1330 1331 1332 1333 1334 1335
        
        if (i == 1) /* Second file name has an uneven number of chars => alignment byte */
            ok(pidlFile->mkid.abID[12 + strlen(szFile) + 1] == '\0', 
                "There should be an alignment byte, but isn't!\n");

        /* The offset of the FileStructW member is stored as a WORD at the end of the pidl. */
        cbOffset = *(WORD*)(((LPBYTE)pidlFile)+pidlFile->mkid.cb-sizeof(WORD));
1336
        ok (cbOffset >= sizeof(struct FileStructA) &&
1337
            cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW),
1338
            "Wrong offset value (%d) stored at the end of the PIDL\n", cbOffset);
1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367

        if (cbOffset >= sizeof(struct FileStructA) &&
            cbOffset <= pidlFile->mkid.cb - sizeof(struct FileStructW)) 
        {
            struct FileStructW *pFileStructW = (struct FileStructW *)(((LPBYTE)pidlFile)+cbOffset);

            ok(pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen, 
                "FileStructW's offset and length should add up to the PIDL's length!\n");

            if (pidlFile->mkid.cb == cbOffset + pFileStructW->cbLen) {
                /* Since we just created the file, time of creation,
                 * time of last access and time of last write access just be the same. 
                 * These tests seem to fail sometimes (on WinXP), if the test is run again shortly 
                 * after the first run. I do remember something with NTFS keeping the creation time
                 * if a file is deleted and then created again within a couple of seconds or so.
                 * Might be the reason. */
                ok (pFileStructA->uFileDate == pFileStructW->uDate &&
                    pFileStructA->uFileTime == pFileStructW->uTime,
                    "Last write time should match creation time!\n");
    
                ok (pFileStructA->uFileDate == pFileStructW->uDate2 &&
                    pFileStructA->uFileTime == pFileStructW->uTime2,
                    "Last write time should match last access time!\n");

                ok (!lstrcmpW(wszFile[i], pFileStructW->wszName),
                    "The filename should be stored in unicode at this position!\n");
            }
        }

1368
        pILFree(pidlFile);
1369 1370 1371
    }
}

1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
static void testSHGetFolderPathAndSubDirA(void)
{
    HRESULT ret;
    BOOL delret;
    DWORD dwret;
    int i;
    static char wine[] = "wine";
    static char winetemp[] = "wine\\temp";
    static char appdata[MAX_PATH];
    static char testpath[MAX_PATH];
    static char toolongpath[MAX_PATH+1];

    if(!pSHGetFolderPathA) {
        skip("SHGetFolderPathA not present!\n");
        return;
    }
    if(!SUCCEEDED(pSHGetFolderPathA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdata)))
    {
        skip("SHGetFolderPathA failed for CSIDL_LOCAL_APPDATA!\n");
        return;
    }

    sprintf(testpath, "%s\\%s", appdata, winetemp);
    delret = RemoveDirectoryA(testpath);
    if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) ) {
        skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
        return;
    }

    sprintf(testpath, "%s\\%s", appdata, wine);
    delret = RemoveDirectoryA(testpath);
    if(!delret && (ERROR_PATH_NOT_FOUND != GetLastError()) && (ERROR_FILE_NOT_FOUND != GetLastError())) {
        skip("RemoveDirectoryA(%s) failed with error %u\n", testpath, GetLastError());
        return;
    }
    for(i=0; i< MAX_PATH; i++)
        toolongpath[i] = '0' + i % 10;
    toolongpath[MAX_PATH] = '\0';

    /* test invalid second parameter */
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | 0xff, NULL, SHGFP_TYPE_CURRENT, wine, testpath);
    ok(E_INVALIDARG == ret, "expected E_INVALIDARG, got  %x\n", ret);

    /* test invalid forth parameter */
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, 2, wine, testpath);
1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428
    switch(ret) {
        case S_OK: /* winvista */
            ok(!strncmp(appdata, testpath, strlen(appdata)),
                "expected %s to start with %s\n", testpath, appdata);
            ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
                "expected %s to end with %s\n", testpath, winetemp);
            break;
        case E_INVALIDARG: /* winxp, win2k3 */
            break;
        default:
            ok(0, "expected S_OK or E_INVALIDARG, got  %x\n", ret);
    }
1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451

    /* test fifth parameter */
    testpath[0] = '\0';
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, NULL, testpath);
    ok(S_OK == ret, "expected S_OK, got %x\n", ret);
    ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);

    testpath[0] = '\0';
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "", testpath);
    ok(S_OK == ret, "expected S_OK, got %x\n", ret);
    ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);

    testpath[0] = '\0';
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, "\\", testpath);
    ok(S_OK == ret, "expected S_OK, got %x\n", ret);
    ok(!lstrcmpA(appdata, testpath), "expected %s, got %s\n", appdata, testpath);

    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, toolongpath, testpath);
    ok(HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE) == ret,
        "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE), ret);

    testpath[0] = '\0';
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_DONT_VERIFY | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, wine, NULL);
1452
    ok((S_OK == ret) || (E_INVALIDARG == ret), "expected S_OK or E_INVALIDARG, got %x\n", ret);
1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478

    /* test a not existing path */
    testpath[0] = '\0';
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
    ok(HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == ret,
        "expected %x, got %x\n", HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND), ret);

    /* create a directory inside a not existing directory */
    testpath[0] = '\0';
    ret = pSHGetFolderPathAndSubDirA(NULL, CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, NULL, SHGFP_TYPE_CURRENT, winetemp, testpath);
    ok(S_OK == ret, "expected S_OK, got %x\n", ret);
    ok(!strncmp(appdata, testpath, strlen(appdata)),
        "expected %s to start with %s\n", testpath, appdata);
    ok(!lstrcmpA(&testpath[1 + strlen(appdata)], winetemp),
        "expected %s to end with %s\n", testpath, winetemp);
    dwret = GetFileAttributes(testpath);
    ok(FILE_ATTRIBUTE_DIRECTORY | dwret, "expected %x to contain FILE_ATTRIBUTE_DIRECTORY\n", dwret);

    /* cleanup */
    sprintf(testpath, "%s\\%s", appdata, winetemp);
    RemoveDirectoryA(testpath);
    sprintf(testpath, "%s\\%s", appdata, wine);
    RemoveDirectoryA(testpath);
}


1479 1480
START_TEST(shlfolder)
{
1481
    init_function_pointers();
1482 1483 1484
    /* if OleInitialize doesn't get called, ParseDisplayName returns
       CO_E_NOTINITIALIZED for malformed directory names on win2k. */
    OleInitialize(NULL);
1485

1486
    test_ParseDisplayName();
1487
    test_BindToObject();
1488
    test_EnumObjects_and_CompareIDs();
1489 1490 1491
    test_GetDisplayName();
    test_GetAttributesOf();
    test_SHGetPathFromIDList();
1492
    test_CallForAttributes();
1493
    test_FolderShortcut();
1494
    test_ITEMIDLIST_format();
1495 1496 1497 1498
    if(pSHGetFolderPathAndSubDirA)
        testSHGetFolderPathAndSubDirA();
    else
        skip("SHGetFolderPathAndSubDirA not present\n");
1499 1500

    OleUninitialize();
1501
}