shell32: IShellFolder::ParseDisplayName should work for missing files if given valid IBindCtx.

Additionally, SHSimpleIDListFromPath now returns PIDLs for non-existent
paths, as it should.
This commit is contained in:
Andrew Eikum 2010-07-21 14:04:22 -05:00 committed by Alexandre Julliard
parent e8c5e2b890
commit f99c81621c
3 changed files with 306 additions and 14 deletions

View File

@ -128,6 +128,7 @@
#include <stdio.h>
#include <stdarg.h>
#include <limits.h>
#include <errno.h>
#ifdef HAVE_DIRENT_H
# include <dirent.h>
#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, pIDL=%p)\n", debugstr_a(pszUnixPath), pIDL);
TRACE("(pszUnixPath=%s, pbc=%p, pIDL=%p)\n", debugstr_a(pszUnixPath), pbc, pIDL);
if (pbc){
IUnknown *unk;
IFileSystemBindData *fsb;
HRESULT hr;
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]);

View File

@ -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)
{

View File

@ -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();
}