From f99c81621c4f9efc575d58983fd5bc5c55fff3cd Mon Sep 17 00:00:00 2001 From: Andrew Eikum Date: Wed, 21 Jul 2010 14:04:22 -0500 Subject: [PATCH] shell32: IShellFolder::ParseDisplayName should work for missing files if given valid IBindCtx. Additionally, SHSimpleIDListFromPath now returns PIDLs for non-existent paths, as it should. --- dlls/shell32/shfldr_unixfs.c | 55 +++++++-- dlls/shell32/shlfsbind.c | 2 +- dlls/shell32/tests/shlfolder.c | 263 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 306 insertions(+), 14 deletions(-) diff --git a/dlls/shell32/shfldr_unixfs.c b/dlls/shell32/shfldr_unixfs.c index 9c9f94afad6..745f30848db 100644 --- a/dlls/shell32/shfldr_unixfs.c +++ b/dlls/shell32/shfldr_unixfs.c @@ -128,6 +128,7 @@ #include #include #include +#include #ifdef HAVE_DIRENT_H # include #endif @@ -184,6 +185,9 @@ WINE_DEFAULT_DEBUG_CHANNEL(shell); #define PATHMODE_UNIX 0 #define PATHMODE_DOS 1 +static const WCHAR wFileSystemBindData[] = { + 'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0}; + /* UnixFolder object layout and typedef. */ typedef struct _UnixFolder { @@ -464,6 +468,7 @@ static inline void UNIXFS_seconds_since_1970_to_dos_date_time( * * PARAMS * pszUnixPath [I] An absolute path. The SHITEMID will be built for the last component. + * pbc [I] Bind context for this action, used to determine if the file must exist * pIDL [O] SHITEMID will be constructed here. * * RETURNS @@ -475,7 +480,7 @@ static inline void UNIXFS_seconds_since_1970_to_dos_date_time( * If what you need is a PIDLLIST with a single SHITEMID, don't forget to append * a 0 USHORT value. */ -static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) { +static char* UNIXFS_build_shitemid(char *pszUnixPath, LPBC pbc, void *pIDL) { LPPIDLDATA pIDLData; struct stat fileStat; char *pszComponentU, *pszComponentA; @@ -484,12 +489,35 @@ static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) { USHORT cbLen; FileStructW *pFileStructW; WORD uOffsetW, *pOffsetW; + BOOL must_exist = TRUE; + + TRACE("(pszUnixPath=%s, pbc=%p, pIDL=%p)\n", debugstr_a(pszUnixPath), pbc, pIDL); + + if (pbc){ + IUnknown *unk; + IFileSystemBindData *fsb; + HRESULT hr; - TRACE("(pszUnixPath=%s, pIDL=%p)\n", debugstr_a(pszUnixPath), pIDL); + hr = IBindCtx_GetObjectParam(pbc, (LPOLESTR)wFileSystemBindData, &unk); + if (SUCCEEDED(hr)) { + hr = IUnknown_QueryInterface(unk, &IID_IFileSystemBindData, (LPVOID*)&fsb); + if (SUCCEEDED(hr)) { + /* Windows tries to get WIN32_FIND_DATAW structure from + * fsb here for no known reason */ + must_exist = FALSE; + IFileSystemBindData_Release(fsb); + } + IUnknown_Release(unk); + } + } /* We are only interested in regular files and directories. */ - if (stat(pszUnixPath, &fileStat)) return NULL; - if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) return NULL; + if (stat(pszUnixPath, &fileStat)){ + if (must_exist || errno != ENOENT) + return NULL; + }else + if (!S_ISDIR(fileStat.st_mode) && !S_ISREG(fileStat.st_mode)) + return NULL; /* Compute the SHITEMID's length and wipe it. */ pszComponentU = strrchr(pszUnixPath, '/') + 1; @@ -544,13 +572,14 @@ static char* UNIXFS_build_shitemid(char *pszUnixPath, void *pIDL) { * NOTES * pUnixFolder also carries the information if the path is expected to be unix or dos. */ -static HRESULT UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, LPITEMIDLIST *ppidl) { +static HRESULT UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, LPBC pbc, const WCHAR *path, + LPITEMIDLIST *ppidl) { LPITEMIDLIST pidl; int cPidlLen, cPathLen; char *pSlash, *pNextSlash, szCompletePath[FILENAME_MAX], *pNextPathElement, *pszAPath; WCHAR *pwszPath; - TRACE("pUnixFolder=%p, path=%s, ppidl=%p\n", pUnixFolder, debugstr_w(path), ppidl); + TRACE("pUnixFolder=%p, pbc=%p, path=%s, ppidl=%p\n", pUnixFolder, pbc, debugstr_w(path), ppidl); if (!ppidl || !path) return E_INVALIDARG; @@ -643,7 +672,7 @@ static HRESULT UNIXFS_path_to_pidl(UnixFolder *pUnixFolder, const WCHAR *path, L while (*pNextPathElement) { pSlash = strchr(pNextPathElement+1, '/'); if (pSlash) *pSlash = '\0'; - pNextPathElement = UNIXFS_build_shitemid(szCompletePath, pidl); + pNextPathElement = UNIXFS_build_shitemid(szCompletePath, pbc, pidl); if (pSlash) *pSlash = '/'; if (!pNextPathElement) { @@ -849,17 +878,17 @@ static ULONG WINAPI UnixFolder_IShellFolder2_Release(IShellFolder2 *iface) { } static HRESULT WINAPI UnixFolder_IShellFolder2_ParseDisplayName(IShellFolder2* iface, HWND hwndOwner, - LPBC pbcReserved, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, + LPBC pbc, LPOLESTR lpszDisplayName, ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG* pdwAttributes) { UnixFolder *This = ADJUST_THIS(UnixFolder, IShellFolder2, iface); HRESULT result; - TRACE("(iface=%p, hwndOwner=%p, pbcReserved=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, " - "pdwAttributes=%p) stub\n", iface, hwndOwner, pbcReserved, debugstr_w(lpszDisplayName), + TRACE("(iface=%p, hwndOwner=%p, pbc=%p, lpszDisplayName=%s, pchEaten=%p, ppidl=%p, " + "pdwAttributes=%p) stub\n", iface, hwndOwner, pbc, debugstr_w(lpszDisplayName), pchEaten, ppidl, pdwAttributes); - result = UNIXFS_path_to_pidl(This, lpszDisplayName, ppidl); + result = UNIXFS_path_to_pidl(This, pbc, lpszDisplayName, ppidl); if (SUCCEEDED(result) && pdwAttributes && *pdwAttributes) { IShellFolder *pParentSF; @@ -1768,7 +1797,7 @@ static HRESULT WINAPI UnixFolder_ISFHelper_AddFolder(ISFHelper* iface, HWND hwnd LPITEMIDLIST pidlRelative; /* Inform the shell */ - if (SUCCEEDED(UNIXFS_path_to_pidl(This, pwszName, &pidlRelative))) { + if (SUCCEEDED(UNIXFS_path_to_pidl(This, NULL, pwszName, &pidlRelative))) { LPITEMIDLIST pidlAbsolute = ILCombine(This->m_pidlLocation, pidlRelative); if (ppidlOut) *ppidlOut = pidlRelative; @@ -2303,7 +2332,7 @@ static HRESULT WINAPI UnixSubFolderIterator_IEnumIDList_Next(IEnumIDList* iface, lstrcpyA(pszRelativePath, pDirEntry->d_name); rgelt[i] = SHAlloc( UNIXFS_shitemid_len_from_filename(pszRelativePath, NULL, NULL)+sizeof(USHORT)); - if (!UNIXFS_build_shitemid(This->m_szFolder, rgelt[i]) || + if (!UNIXFS_build_shitemid(This->m_szFolder, NULL, rgelt[i]) || !UNIXFS_is_pidl_of_type(rgelt[i], This->m_fFilter)) { SHFree(rgelt[i]); diff --git a/dlls/shell32/shlfsbind.c b/dlls/shell32/shlfsbind.c index d773ef94ca4..0870b0f8950 100644 --- a/dlls/shell32/shlfsbind.c +++ b/dlls/shell32/shlfsbind.c @@ -62,7 +62,7 @@ static const IFileSystemBindDataVtbl sbvt = }; static const WCHAR wFileSystemBindData[] = { - 'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d','D','a','t','a',0}; + 'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0}; HRESULT WINAPI IFileSystemBindData_Constructor(const WIN32_FIND_DATAW *pfd, LPBC *ppV) { diff --git a/dlls/shell32/tests/shlfolder.c b/dlls/shell32/tests/shlfolder.c index 210631d8856..fc1c1e09b40 100644 --- a/dlls/shell32/tests/shlfolder.c +++ b/dlls/shell32/tests/shlfolder.c @@ -55,6 +55,7 @@ static BOOL (WINAPI *pILIsEqual)(LPCITEMIDLIST, LPCITEMIDLIST); static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**); static LPITEMIDLIST (WINAPI *pILCombine)(LPCITEMIDLIST,LPCITEMIDLIST); static HRESULT (WINAPI *pSHParseDisplayName)(LPCWSTR,IBindCtx*,LPITEMIDLIST*,SFGAOF,SFGAOF*); +static LPITEMIDLIST (WINAPI *pSHSimpleIDListFromPathAW)(LPCVOID); static void init_function_pointers(void) { @@ -80,6 +81,7 @@ static void init_function_pointers(void) MAKEFUNC_ORD(ILIsEqual, 21); MAKEFUNC_ORD(ILCombine, 25); MAKEFUNC_ORD(ILFree, 155); + MAKEFUNC_ORD(SHSimpleIDListFromPathAW, 162); #undef MAKEFUNC_ORD /* test named exports */ @@ -2264,6 +2266,265 @@ static void test_GetUIObject(void) Cleanup(); } +#define verify_pidl(i,p) r_verify_pidl(__LINE__, i, p) +static void r_verify_pidl(unsigned l, LPCITEMIDLIST pidl, const WCHAR *path) +{ + LPCITEMIDLIST child; + IShellFolder *parent; + STRRET filename; + HRESULT hr; + + if(!pSHBindToParent){ + win_skip("SHBindToParent is not available, not performing full PIDL verification\n"); + if(path) + ok_(__FILE__,l)(pidl != NULL, "Expected PIDL to be non-NULL\n"); + else + ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n"); + return; + } + + if(path){ + if(!pidl){ + ok_(__FILE__,l)(0, "didn't get expected path (%s), instead: NULL\n", wine_dbgstr_w(path)); + return; + } + + hr = pSHBindToParent(pidl, &IID_IShellFolder, (LPVOID*)&parent, &child); + ok_(__FILE__,l)(hr == S_OK, "SHBindToParent failed: 0x%08x\n", hr); + if(FAILED(hr)) + return; + + hr = IShellFolder_GetDisplayNameOf(parent, child, SHGDN_FORPARSING, &filename); + ok_(__FILE__,l)(hr == S_OK, "GetDisplayNameOf failed: 0x%08x\n", hr); + if(FAILED(hr)){ + IShellFolder_Release(parent); + return; + } + + ok_(__FILE__,l)(filename.uType == STRRET_WSTR, "Got unexpected string type: %d\n", filename.uType); + ok_(__FILE__,l)(lstrcmpW(path, filename.pOleStr) == 0, + "didn't get expected path (%s), instead: %s\n", + wine_dbgstr_w(path), wine_dbgstr_w(filename.pOleStr)); + + IShellFolder_Release(parent); + }else + ok_(__FILE__,l)(pidl == NULL, "Expected PIDL to be NULL\n"); +} + +static void test_SHSimpleIDListFromPath(void) +{ + const WCHAR adirW[] = {'C',':','\\','s','i','d','l','f','p','d','i','r',0}; + const CHAR adirA[] = "C:\\sidlfpdir"; + BOOL br, is_unicode = !(GetVersion() & 0x80000000); + + LPITEMIDLIST pidl = NULL; + + if(!pSHSimpleIDListFromPathAW){ + win_skip("SHSimpleIDListFromPathAW not available\n"); + return; + } + + br = CreateDirectoryA(adirA, NULL); + ok(br == TRUE, "CreateDirectory failed: %d\n", GetLastError()); + + if(is_unicode) + pidl = pSHSimpleIDListFromPathAW(adirW); + else + pidl = pSHSimpleIDListFromPathAW(adirA); + verify_pidl(pidl, adirW); + pILFree(pidl); + + br = RemoveDirectoryA(adirA); + ok(br == TRUE, "RemoveDirectory failed: %d\n", GetLastError()); + + if(is_unicode) + pidl = pSHSimpleIDListFromPathAW(adirW); + else + pidl = pSHSimpleIDListFromPathAW(adirA); + verify_pidl(pidl, adirW); + pILFree(pidl); +} + +/* IFileSystemBindData impl */ +static HRESULT WINAPI fsbd_QueryInterface(IFileSystemBindData *fsbd, + REFIID riid, void **ppv) +{ + if(IsEqualIID(riid, &IID_IFileSystemBindData) || + IsEqualIID(riid, &IID_IUnknown)){ + *ppv = fsbd; + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI fsbd_AddRef(IFileSystemBindData *fsbd) +{ + return 2; +} + +static ULONG WINAPI fsbd_Release(IFileSystemBindData *fsbd) +{ + return 1; +} + +static HRESULT WINAPI fsbd_SetFindData(IFileSystemBindData *fsbd, + const WIN32_FIND_DATAW *pfd) +{ + ok(0, "SetFindData called\n"); + return E_NOTIMPL; +} + +static HRESULT WINAPI fsbd_GetFindData_nul(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + memset(pfd, 0, sizeof(WIN32_FIND_DATAW)); + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_junk(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + memset(pfd, 0xdeadbeef, sizeof(WIN32_FIND_DATAW)); + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_invalid(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + memset(pfd, 0, sizeof(WIN32_FIND_DATAW)); + *pfd->cFileName = 'a'; + *pfd->cAlternateFileName = 'a'; + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_valid(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + static const WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0}; + HANDLE handle = FindFirstFileW(adirW, pfd); + FindClose(handle); + return S_OK; +} + +static HRESULT WINAPI fsbd_GetFindData_fail(IFileSystemBindData *fsbd, + WIN32_FIND_DATAW *pfd) +{ + return E_FAIL; +} + +static IFileSystemBindDataVtbl fsbdVtbl = { + fsbd_QueryInterface, + fsbd_AddRef, + fsbd_Release, + fsbd_SetFindData, + NULL +}; + +static IFileSystemBindData fsbd = { &fsbdVtbl }; + +static void test_ParseDisplayNamePBC(void) +{ + WCHAR wFileSystemBindData[] = + {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d',' ','D','a','t','a',0}; + WCHAR adirW[] = {'C',':','\\','f','s','b','d','d','i','r',0}; + const HRESULT exp_err = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + IShellFolder *psf; + IBindCtx *pbc; + HRESULT hres; + ITEMIDLIST *pidl; + + /* Check if we support WCHAR functions */ + SetLastError(0xdeadbeef); + lstrcmpiW(adirW, adirW); + if(GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){ + win_skip("Most W-calls are not implemented\n"); + return; + } + + hres = SHGetDesktopFolder(&psf); + ok(hres == S_OK, "SHGetDesktopFolder failed: 0x%08x\n", hres); + if(FAILED(hres)){ + win_skip("Failed to get IShellFolder, can't run tests\n"); + return; + } + + /* fails on unknown dir with no IBindCtx */ + hres = IShellFolder_ParseDisplayName(psf, NULL, NULL, adirW, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + + /* fails on unknown dir with IBindCtx with no IFileSystemBindData */ + hres = CreateBindCtx(0, &pbc); + ok(hres == S_OK, "CreateBindCtx failed: 0x%08x\n", hres); + + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == exp_err || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed with wrong error: 0x%08x\n", hres); + + /* unknown dir with IBindCtx with IFileSystemBindData */ + hres = IBindCtx_RegisterObjectParam(pbc, wFileSystemBindData, (IUnknown*)&fsbd); + ok(hres == S_OK, "RegisterObjectParam failed: 0x%08x\n", hres); + + /* return E_FAIL from GetFindData */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_fail; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + /* set FIND_DATA struct to NULLs */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_nul; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + /* set FIND_DATA struct to junk */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_junk; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + /* set FIND_DATA struct to invalid data */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_invalid; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + /* set FIND_DATA struct to valid data */ + pidl = (ITEMIDLIST*)0xdeadbeef; + fsbdVtbl.GetFindData = fsbd_GetFindData_valid; + hres = IShellFolder_ParseDisplayName(psf, NULL, pbc, adirW, NULL, &pidl, NULL); + ok(hres == S_OK || broken(hres == E_FAIL) /* NT4 */, + "ParseDisplayName failed: 0x%08x\n", hres); + if(SUCCEEDED(hres)){ + verify_pidl(pidl, adirW); + ILFree(pidl); + } + + IBindCtx_Release(pbc); + IShellFolder_Release(psf); +} + START_TEST(shlfolder) { init_function_pointers(); @@ -2286,6 +2547,8 @@ START_TEST(shlfolder) test_SHCreateShellItem(); test_desktop_IPersist(); test_GetUIObject(); + test_SHSimpleIDListFromPath(); + test_ParseDisplayNamePBC(); OleUninitialize(); } -- 2.11.4.GIT