Since this patch: http://www.winehq.org/hypermail/wine-cvs/2005/06/0338.html SHGetPathFromIDList() seems broken. The problem is that files stored in the Desktop directory don't get the path to the Desktop directory prepended. I've tried to fix it in the attached patch and added a testcase, but the shell COM interfaces easily confuse me, so perhaps someone with a better understanding of this stuff should look it over before I submit it to wine-patches.
Ge van Geldorp.
Index: dlls/shell32/pidl.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/pidl.c,v retrieving revision 1.133 diff -u -r1.133 pidl.c --- dlls/shell32/pidl.c 8 Jul 2005 14:18:32 -0000 1.133 +++ dlls/shell32/pidl.c 12 Aug 2005 15:04:05 -0000 @@ -1242,6 +1242,7 @@ BOOL WINAPI SHGetPathFromIDListW(LPCITEMIDLIST pidl, LPWSTR pszPath) { HRESULT hr; + BOOL result; LPCITEMIDLIST pidlLast; LPSHELLFOLDER psfFolder; DWORD dwAttributes; @@ -1258,13 +1259,34 @@
dwAttributes = SFGAO_FILESYSTEM; hr = IShellFolder_GetAttributesOf(psfFolder, 1, &pidlLast, &dwAttributes); - if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) return FALSE; + if (FAILED(hr) || !(dwAttributes & SFGAO_FILESYSTEM)) { + IShellFolder_Release(psfFolder); + ILFree((LPITEMIDLIST) pidlLast); + return FALSE; + }
hr = IShellFolder_GetDisplayNameOf(psfFolder, pidlLast, SHGDN_FORPARSING, &strret); - if (FAILED(hr)) return FALSE; - - hr = StrRetToBufW(&strret, pidlLast, pszPath, MAX_PATH); IShellFolder_Release(psfFolder); + if (FAILED(hr)) { + ILFree((LPITEMIDLIST) pidlLast); + return FALSE; + } + + /* Check for a PIDL rooted at desktop level */ + if (_ILIsValue(pidl) || _ILIsFolder(pidl)) { + result = SHGetSpecialFolderPathW(NULL, pszPath, CSIDL_DESKTOPDIRECTORY, FALSE); + if (! result) { + ILFree((LPITEMIDLIST) pidlLast); + return FALSE; + } + PathAddBackslashW(pszPath); + } else { + pszPath[0] = '\0'; + } + + hr = StrRetToBufW(&strret, pidlLast, pszPath + lstrlenW(pszPath), + MAX_PATH - lstrlenW(pszPath)); + ILFree((LPITEMIDLIST) pidlLast);
TRACE_(shell)("-- %s, 0x%08lx\n",debugstr_w(pszPath), hr); return SUCCEEDED(hr); Index: dlls/shell32/tests/shlfolder.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/tests/shlfolder.c,v retrieving revision 1.29 diff -u -r1.29 shlfolder.c --- dlls/shell32/tests/shlfolder.c 12 Aug 2005 10:33:37 -0000 1.29 +++ dlls/shell32/tests/shlfolder.c 12 Aug 2005 15:04:06 -0000 @@ -584,6 +584,11 @@ 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 }; + WCHAR wszFileName[MAX_PATH]; + LPITEMIDLIST pidlTestFile; + HANDLE hTestFile; + static WCHAR wszTestFile[] = { + 'w','i','n','e','t','e','s','t','.','f','o','o',0 };
if(!pSHGetSpecialFolderPathW) return;
@@ -604,15 +609,52 @@
hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszMyComputer, NULL, &pidlMyComputer, NULL); ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse MyComputer's CLSID! hr = %08lx\n", hr); - IShellFolder_Release(psfDesktop); - if (FAILED(hr)) return; + if (FAILED(hr)) { + IShellFolder_Release(psfDesktop); + return; + }
SetLastError(0xdeadbeef); result = SHGetPathFromIDListW(pidlMyComputer, wszPath); ok (!result, "SHGetPathFromIDList succeeded where it shouldn't!\n"); ok (GetLastError()==0xdeadbeef, "SHGetPathFromIDList shouldn't set last error! Last error: %08lx\n", GetLastError()); + if (result) { + IShellFolder_Release(psfDesktop); + return; + }
IMalloc_Free(ppM, pidlMyComputer); + + result = pSHGetSpecialFolderPathW(NULL, wszFileName, CSIDL_DESKTOPDIRECTORY, FALSE); + ok(result, "SHGetSpecialFolderPathW failed! Last error: %08lx\n", GetLastError()); + if (!result) { + IShellFolder_Release(psfDesktop); + return; + } + PathAddBackslashW(wszFileName); + lstrcatW(wszFileName, wszTestFile); + hTestFile = CreateFileW(wszFileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, 0, NULL); + ok(hTestFile != INVALID_HANDLE_VALUE, "CreateFileW failed! Last error: %08lx\n", GetLastError()); + if (hTestFile == INVALID_HANDLE_VALUE) { + IShellFolder_Release(psfDesktop); + return; + } + CloseHandle(hTestFile); + + hr = IShellFolder_ParseDisplayName(psfDesktop, NULL, NULL, wszTestFile, NULL, &pidlTestFile, NULL); + ok (SUCCEEDED(hr), "Desktop's ParseDisplayName failed to parse filename hr = %08lx\n", hr); + IShellFolder_Release(psfDesktop); + DeleteFileW(wszFileName); + if (FAILED(hr)) { + IMalloc_Free(ppM, pidlTestFile); + return; + } + + result = SHGetPathFromIDListW(pidlTestFile, wszPath); + ok(result, "SHGetPathFromIDListW failed! Last error: %08lx\n", GetLastError()); + IMalloc_Free(ppM, pidlTestFile); + if (!result) return; + ok(0 == lstrcmpW(wszFileName, wszPath), "SHGetPathFromIDListW returned incorrect path for file placed on desktop\n"); }
static void test_EnumObjects_and_CompareIDs(void)
Hi Ge,
On Friday 12 August 2005 17:19, Ge van Geldorp wrote:
Since this patch: http://www.winehq.org/hypermail/wine-cvs/2005/06/0338.html SHGetPathFromIDList() seems broken. The problem is that files stored in the Desktop directory don't get the path to the Desktop directory prepended. I've tried to fix it in the attached patch and added a testcase, but the shell COM interfaces easily confuse me, so perhaps someone with a better understanding of this stuff should look it over before I submit it to wine-patches.
I think the prepending of the Desktop path should happen in the Desktop Shellfolder, if it's GetDisplayNameOf method is called with the SHGDN_FORPARSING flag is set (instead of in SHGetPathFromIDList). I've modified your patch accordingly and added a testcase. Please submit to wine-patches, if you agree.
Thanks for cleaning up all those resource leaks after me.
Bye,