This is the first part of a patch to get SHChangeNotify working properly on deleted and moved/renamed files. There needs to be another change in all shlfldr_xx.c to actually make it work as desired. As it is now with this patch SHSimpleIDListFromPath still does what it is supposed to do for existing paths, which is already a lot more than the function did until now and SHChangeNotify does work exactly as it did already until now. dlls\shell32\shlfsbind.h and dlls\shell32\shlfsbind.c are new files added to the tree Changelog * dlls/shell32/changenotify.c Make SHChangeNotify use SHSimpleIDListFromPath as it is supposed to do * dlls/shell32/pidl.c Fix SHSimpleIDListFromPath to create fully qualified ItemIDLists and passing a bindcontext to IShellFolder_ParseDisplayName for conversion of a path to an ItemIDList. * dlls\shell32\shlfsbind.h New header file for the FileSystemBindContext * dlls\shell32\shlfsbind.c New source file for the FileSystemBindContext License: X11/LGPL Rolf Kalbermatter Index: dlls/shell32/changenotify.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/changenotify.c,v retrieving revision 1.20 diff -u -r1.20 changenotify.c --- dlls/shell32/changenotify.c 21 Jan 2003 19:36:24 -0000 1.20 +++ dlls/shell32/changenotify.c 4 May 2003 19:23:08 -0000 @@ -18,8 +18,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" + #include <string.h> +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "winerror.h" +#include "winbase.h" +#include "winreg.h" + +#include "shlwapi.h" #include "wine/debug.h" #include "pidl.h" #include "shell32_main.h" @@ -212,24 +221,25 @@ { LPITEMIDLIST Pidls[2]; LPNOTIFICATIONLIST ptr; - DWORD dummy; UINT typeFlag = uFlags & SHCNF_TYPE; Pidls[0] = (LPITEMIDLIST)dwItem1; Pidls[1] = (LPITEMIDLIST)dwItem2; - TRACE("(0x%08lx,0x%08x,%p,%p):stub.\n", wEventId, uFlags, dwItem1, dwItem2); + TRACE("(0x%08lx,0x%08x,%p,%p)\n", wEventId, uFlags, dwItem1, dwItem2); /* convert paths in IDLists*/ switch (typeFlag) { + case SHCNF_IDLIST: + break; case SHCNF_PATHA: - if (dwItem1) SHILCreateFromPathA((LPCSTR)dwItem1, &Pidls[0], &dummy); - if (dwItem2) SHILCreateFromPathA((LPCSTR)dwItem2, &Pidls[1], &dummy); + if (dwItem1) Pidls[0] = SHSimpleIDListFromPathA((LPCSTR)dwItem1); + if (dwItem2) Pidls[1] = SHSimpleIDListFromPathA((LPCSTR)dwItem2); break; case SHCNF_PATHW: - if (dwItem1) SHILCreateFromPathW((LPCWSTR)dwItem1, &Pidls[0], &dummy); - if (dwItem2) SHILCreateFromPathW((LPCWSTR)dwItem2, &Pidls[1], &dummy); + if (dwItem1) Pidls[0] = SHSimpleIDListFromPathW((LPCWSTR)dwItem1); + if (dwItem2) Pidls[1] = SHSimpleIDListFromPathW((LPCWSTR)dwItem2); break; case SHCNF_PRINTERA: case SHCNF_PRINTERW: @@ -255,8 +265,8 @@ LeaveCriticalSection(&SHELL32_ChangenotifyCS); - /* if we allocated it, free it */ - if ((typeFlag == SHCNF_PATHA) || (typeFlag == SHCNF_PATHW)) + /* if we allocated it, free it. The ANSI flag is also set in its Unicode sibling. */ + if ((typeFlag & SHCNF_PATHA) /* || (typeFlag & SHCNF_PRINTERA) */ ) { if (Pidls[0]) SHFree(Pidls[0]); if (Pidls[1]) SHFree(Pidls[1]); Index: dlls/shell32/pidl.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/pidl.c,v retrieving revision 1.83 diff -u -r1.83 pidl.c --- dlls/shell32/pidl.c 17 Apr 2003 02:18:33 -0000 1.83 +++ dlls/shell32/pidl.c 4 May 2003 19:25:30 -0000 @@ -39,6 +39,7 @@ #include "shlwapi.h" #include "pidl.h" +#include "shlfsbind.h" #include "debughlp.h" #include "wine/debug.h" @@ -794,46 +795,115 @@ return ILCreateFromPathA (path); } /************************************************************************* - * SHSimpleIDListFromPath [SHELL32.162] + * _ILParsePathW [internal] + * + * Creates an ItemIDList from a path and returns it. + * + * PARAMS + * path [I] path to parse and convert into an ItemIDList + * lpFindFile [I] pointer to buffer to initialize the FileSystem + * Bind Data object with + * bBindCtx [I] indicates to create a BindContext and assign a + * FileSystem Bind Data object + * ppidl [O] the newly create ItemIDList + * prgfInOut [I/O] requested attributes on input and actual + * attributes on return + * + * RETURNS + * NO_ERROR on success or an OLE error code + * + * NOTES + * If either lpFindFile is non-NULL or bBindCtx is TRUE, this function + * creates a BindContext object and assigns a FileSystem Bind Data object + * to it, passing the BindContext to IShellFolder_ParseDisplayName. Each + * IShellFolder uses that FileSystem Bind Data object of the BindContext + * to pass data about the current path element to the next object. This + * is used to avoid having to verify the current path element on disk, so + * that creating an ItemIDList from a non existing path still can work. */ -LPITEMIDLIST WINAPI SHSimpleIDListFromPathA (LPCSTR lpszPath) +HRESULT WINAPI _ILParsePathW(LPCWSTR path, LPWIN32_FIND_DATAW lpFindFile, + BOOL bBindCtx, LPITEMIDLIST *ppidl, LPDWORD prgfInOut) { - LPITEMIDLIST pidl=NULL; - HANDLE hFile; - WIN32_FIND_DATAA stffile; + LPSHELLFOLDER pSF = NULL; + LPBC pBC = NULL; + HRESULT ret; - TRACE("path=%s\n", lpszPath); + TRACE("%s %p %ld %p %p %p 0x%lx\n", debugstr_w(path), lpFindFile, bBindCtx, + ppidl, ppidl ? *ppidl : NULL, + prgfInOut, prgfInOut ? *prgfInOut : 0); - if (!lpszPath) return NULL; + ret = SHGetDesktopFolder(&pSF); + if (FAILED(ret)) + { + return ret; + } - hFile = FindFirstFileA(lpszPath, &stffile); + if (lpFindFile || bBindCtx) + ret = FSBindData_Constructor(lpFindFile, &pBC); - if ( hFile != INVALID_HANDLE_VALUE ) + if (SUCCEEDED(ret)) { - if (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - pidl = _ILCreateFolder (&stffile); - } - else - { - pidl = _ILCreateValue (&stffile); - } - FindClose (hFile); + ret = IShellFolder_ParseDisplayName(pSF, 0, pBC, (LPOLESTR)path, NULL, ppidl, prgfInOut); + IUnknown_Release(pBC); } + + if (pBC) + IBindCtx_Release(pBC); + + IShellFolder_Release(pSF); + + if (!SUCCEEDED(ret) && ppidl) + *ppidl = NULL; + + TRACE("%s %p 0x%lx\n", debugstr_w(path), ppidl ? *ppidl : NULL, prgfInOut ? *prgfInOut : 0); + + return ret; +} + +/************************************************************************* + * SHSimpleIDListFromPath [SHELL32.162] + * + * Creates a simple ItemIDList from a path and returns it. This function + * does not fail on non-existing paths. + * + * PARAMS + * path [I] path to parse and convert into an ItemIDList + * + * RETURNS + * the newly created simple ItemIDList + * + * NOTES + * Simple in the name does not mean a relative ItemIDList but rather a + * fully qualified list, where only the file name is filled in and the + * directory flag for those ItemID elements this is known about, eg. + * it is not the last element in the ItemIDList or the actual directory + * exists on disk. + * exported by ordinal. + */ +LPITEMIDLIST WINAPI SHSimpleIDListFromPathA(LPCSTR path) +{ + LPITEMIDLIST pidl = NULL; + WCHAR wPath[MAX_PATH]; + + TRACE("%s\n", debugstr_a(path)); + + MultiByteToWideChar(CP_ACP, 0, path, -1, wPath, MAX_PATH); + _ILParsePathW(wPath, NULL, TRUE, &pidl, NULL); return pidl; } -LPITEMIDLIST WINAPI SHSimpleIDListFromPathW (LPCWSTR lpszPath) + +LPITEMIDLIST WINAPI SHSimpleIDListFromPathW(LPCWSTR path) { - char lpszTemp[MAX_PATH]; - TRACE("path=%s\n",debugstr_w(lpszPath)); + LPITEMIDLIST pidl = NULL; - if (!WideCharToMultiByte( CP_ACP, 0, lpszPath, -1, lpszTemp, sizeof(lpszTemp), NULL, NULL )) - lpszTemp[sizeof(lpszTemp)-1] = 0; + TRACE("%s\n", debugstr_w(path)); - return SHSimpleIDListFromPathA (lpszTemp); + _ILParsePathW(path, NULL, TRUE, &pidl, NULL); + TRACE("%s %p\n", debugstr_w(path), pidl); + return pidl; } -LPITEMIDLIST WINAPI SHSimpleIDListFromPathAW (LPCVOID lpszPath) +LPITEMIDLIST WINAPI SHSimpleIDListFromPathAW(LPCVOID lpszPath) { if ( SHELL_OsIsUnicode()) return SHSimpleIDListFromPathW (lpszPath); --- /dev/nul Sun May 04 19:15:26 2003 +++ dlls/shell32/shlfsbind.h Sun May 04 20:08:32 2003 @@ -0,0 +1,64 @@ +/* + * File System Bind Data object to use as parameter for the bind context to + * IShellFolder_ParseDisplayName + * + * Copyright 2003 Rolf Kalbermatter + * + * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*********************************************************************** +* FSBindData declarations +*/ +#ifndef __WINE_SHELL_FS_BINDDATA_H +#define __WINE_SHELL_FS_BINDDATA_H + +#ifdef __cplusplus +extern "C" { +#endif /* defined(__cplusplus) */ + +typedef struct IFSBindData IFSBindData, *LPFSBINDDATA; + +#define INTERFACE IFSBindData +#define IFSBindData_METHODS \ + IUnknown_METHODS \ + STDMETHOD(PutData)(THIS_ LPWIN32_FIND_DATAW lpFindFile) PURE; \ + STDMETHOD(GetData)(THIS_ LPWIN32_FIND_DATAW lpFindFile) PURE; +ICOM_DEFINE(IFSBindData,IUnknown) +#undef INTERFACE + +DEFINE_GUID(IID_IFSBindData, 0x01E18D10,0x4D8B,0x11D2,0x85,0x5D,0x00,0x60,0x08,0x05,0x93,0x67); + + +/*** IUnknown methods ***/ +#define IFSBindData_QueryInterface(p,a,b) (p)->lpVtbl->QueryInterface(p,a,b) +#define IFSBindData_AddRef(p) (p)->lpVtbl->AddRef(p) +#define IFSBindData_Release(p) (p)->lpVtbl->Release(p) +/*** IFSBindData methods ***/ +#define IFSBindData_PutData(p,a) (p)->lpVtbl->PutData(p,a) +#define IFSBindData_GetData(p,a) (p)->lpVtbl->GetData(p,a) + +HRESULT WINAPI FSBindData_Constructor(LPWIN32_FIND_DATAW lpFindFile, LPBC *ppV); + +HRESULT WINAPI FSBindData_QueryData(LPBC pbc, LPWIN32_FIND_DATAW lpFindFile); + +HRESULT WINAPI FSBindData_StoreData(LPBC pbc, LPWIN32_FIND_DATAW lpFindFile); + +#ifdef __cplusplus +extern } +#endif /* defined(__cplusplus) */ + +#endif --- /dev/nul Sun May 04 19:15:26 2003 +++ dlls/shell32/shlfsbind.c Sun May 04 19:19:00 2003 @@ -0,0 +1,203 @@ +/* + * File System Bind Data object to use as parameter for the bind context to + * IShellFolder_ParseDisplayName + * + * Copyright 2003 Rolf Kalbermatter + * + * 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 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "wine/port.h" + +#include "winbase.h" +#include "shell32_main.h" +#include "shlfsbind.h" + +#include "debughlp.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(pidl); + +/*********************************************************************** + * FSBindData implementation + */ +typedef struct +{ + ICOM_VFIELD(IFSBindData); + DWORD ref; + LPWIN32_FIND_DATAW lpFindFile; +} IFSBindDataImpl; + +static HRESULT WINAPI IFSBindData_fnQueryInterface(LPFSBINDDATA iface, REFIID riid, LPVOID* ppvObj); +static ULONG WINAPI IFSBindData_fnAddRef(LPFSBINDDATA iface); +static ULONG WINAPI IFSBindData_fnRelease(LPFSBINDDATA iface); +static HRESULT WINAPI IFSBindData_fnPutData(LPFSBINDDATA iface, LPWIN32_FIND_DATAW lpFindFile); +static HRESULT WINAPI IFSBindData_fnGetData(LPFSBINDDATA iface, LPWIN32_FIND_DATAW lpFindFile); + +static struct ICOM_VTABLE(IFSBindData) sbvt = +{ + ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE + IFSBindData_fnQueryInterface, + IFSBindData_fnAddRef, + IFSBindData_fnRelease, + IFSBindData_fnPutData, + IFSBindData_fnGetData, +}; + +static WCHAR lpFileSystemBindData[] = {'F','i','l','e',' ','S','y','s','t','e','m',' ','B','i','n','d','D','a','t','a',0}; + +HRESULT WINAPI FSBindData_Constructor(LPWIN32_FIND_DATAW lpFindFile, LPBC *ppV) +{ + IFSBindDataImpl *sb = (IFSBindDataImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IFSBindDataImpl)); + HRESULT ret = E_OUTOFMEMORY; + + *ppV = NULL; + if (!sb) + return ret; + + sb->lpVtbl = &sbvt; + sb->ref = 1; + if (lpFindFile) + memcpy(sb->lpFindFile, lpFindFile, sizeof(LPWIN32_FIND_DATAW)); + else + memset(sb->lpFindFile, 0, sizeof(LPWIN32_FIND_DATAW)); + + ret = CreateBindCtx(0, ppV); + if (SUCCEEDED(ret)) + { + BIND_OPTS bindOpts; + bindOpts.cbStruct = sizeof(BIND_OPTS); + bindOpts.grfFlags = 0; + bindOpts.grfMode = STGM_CREATE; + bindOpts.dwTickCountDeadline = 0; + IBindCtx_SetBindOptions(*ppV, &bindOpts); + IBindCtx_RegisterObjectParam(*ppV, lpFileSystemBindData, (LPUNKNOWN)sb); + + IFSBindData_Release((LPFSBINDDATA)sb); + } + return ret; +} + +HRESULT WINAPI FSBindData_QueryData(LPBC pbc, LPWIN32_FIND_DATAW lpFindFile) +{ + LPUNKNOWN pUnk; + LPFSBINDDATA pFSBindData = NULL; + HRESULT ret; + + ret = IBindCtx_GetObjectParam(pbc, lpFileSystemBindData, &pUnk); + if (SUCCEEDED(ret)) + { + ret = IUnknown_QueryInterface(pUnk, &IID_IFSBindData, &pFSBindData); + if (SUCCEEDED(ret)) + { + ret = IFSBindData_GetData(pFSBindData, lpFindFile); + IFSBindData_Release(pFSBindData); + } + IUnknown_Release(pUnk); + } + return ret; +} + +HRESULT WINAPI FSBindData_StoreData(LPBC pbc, LPWIN32_FIND_DATAW lpFindFile) +{ + LPUNKNOWN pUnk; + LPFSBINDDATA pFSBindData = NULL; + HRESULT ret; + + ret = IBindCtx_GetObjectParam(pbc, lpFileSystemBindData, &pUnk); + if (SUCCEEDED(ret)) + { + ret = IUnknown_QueryInterface(pUnk, &IID_IFSBindData, &pFSBindData); + if (SUCCEEDED(ret)) + { + ret = IFSBindData_PutData(pFSBindData, lpFindFile); + IFSBindData_Release(pFSBindData); + } + IUnknown_Release(pUnk); + } + return ret;} + +HRESULT WINAPI IFSBindData_fnQueryInterface(LPFSBINDDATA iface, REFIID riid, LPVOID *ppV) +{ + ICOM_THIS(IFSBindDataImpl, iface); + TRACE("(%p)->(\n\tIID:\t%s,%p)\n", This, debugstr_guid(riid), ppV); + + *ppV = NULL; + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *ppV = This; + } + else if (IsEqualIID(riid, &IID_IFSBindData)) + { + *ppV = (IFSBindData*)This; + } + + if(*ppV) + { + IUnknown_AddRef((IUnknown*)(*ppV)); + TRACE("-- Interface: (%p)->(%p)\n", ppV, *ppV); + return S_OK; + } + TRACE("-- Interface: E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +ULONG WINAPI IFSBindData_fnAddRef(LPFSBINDDATA iface) +{ + ICOM_THIS(IFSBindDataImpl, iface); + TRACE("(%p)\n", This); + return InterlockedIncrement(&This->ref); +} + +ULONG WINAPI IFSBindData_fnRelease(LPFSBINDDATA iface) +{ + ICOM_THIS(IFSBindDataImpl, iface); + TRACE("(%p)\n", This); + + if (!InterlockedDecrement(&This->ref)) + { + TRACE(" destroying ISFBindPidl(%p)\n",This); + HeapFree(GetProcessHeap(), 0, This); + return 0; + } + return This->ref; +} + +HRESULT WINAPI IFSBindData_fnPutData(LPFSBINDDATA iface, LPWIN32_FIND_DATAW lpFindFile) +{ + ICOM_THIS(IFSBindDataImpl, iface); + TRACE("(%p %p)\n", This, lpFindFile); + + if (!lpFindFile) + memcpy(This->lpFindFile, lpFindFile, sizeof(LPWIN32_FIND_DATAW)); + else + memset(This->lpFindFile, 0, sizeof(LPWIN32_FIND_DATAW)); + return NOERROR; +} + +HRESULT WINAPI IFSBindData_fnGetData(LPFSBINDDATA iface, LPWIN32_FIND_DATAW lpFindFile) +{ + ICOM_THIS(IFSBindDataImpl, iface); + TRACE("(%p %p)\n", This, lpFindFile); + + if (!lpFindFile) + return E_INVALIDARG; + + memcpy(lpFindFile, This->lpFindFile, sizeof(LPWIN32_FIND_DATAW)); + return NOERROR; +}