Index: apitests/shell32/CShellDesktop.cpp =================================================================== --- apitests/shell32/CShellDesktop.cpp (revision 70435) +++ apitests/shell32/CShellDesktop.cpp (working copy) @@ -3,6 +3,7 @@ * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory * PURPOSE: Test for CShellDesktop * PROGRAMMER: Thomas Faber + * Mark Jansen */ #include "shelltest.h" @@ -14,8 +15,231 @@ #include #include + +// + Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20130503-00/?p=4463/ +// In short: We want to create an IDLIST from an item that does not exist, +// so we have to provide WIN32_FIND_DATAW in a bind context. +// If we don't, the FS will be queried, and we do not get a valid IDLIST for a non-existing path. + +CComModule gModule; + +class CFileSysBindData : + public CComCoClass, + public CComObjectRootEx, + public IFileSystemBindData +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetFindData(const WIN32_FIND_DATAW *pfd) + { + m_Data = *pfd; + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE GetFindData(WIN32_FIND_DATAW *pfd) + { + *pfd = m_Data; + return S_OK; + } + + DECLARE_NOT_AGGREGATABLE(CFileSysBindData) + DECLARE_PROTECT_FINAL_CONSTRUCT() + BEGIN_COM_MAP(CFileSysBindData) + COM_INTERFACE_ENTRY_IID(IID_IFileSystemBindData, IFileSystemBindData) + END_COM_MAP() +private: + WIN32_FIND_DATAW m_Data; +}; + static +HRESULT +AddFileSysBindCtx(_In_ IBindCtx *pbc) +{ + CComPtr spfsbc(new CComObject()); + WIN32_FIND_DATAW wfd = { 0 }; + wfd.dwFileAttributes = FILE_ATTRIBUTE_NORMAL; + spfsbc->SetFindData(&wfd); + HRESULT hr = pbc->RegisterObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, spfsbc); + ok(hr == S_OK, "hr = %lx\n", hr); + return hr; +} + +static +HRESULT +CreateBindCtxWithOpts(_In_ BIND_OPTS *pbo, _Outptr_ IBindCtx **ppbc) +{ + CComPtr spbc; + HRESULT hr = CreateBindCtx(0, &spbc); + ok(hr == S_OK, "hr = %lx\n", hr); + if (SUCCEEDED(hr)) + { + hr = spbc->SetBindOptions(pbo); + ok(hr == S_OK, "hr = %lx\n", hr); + } + *ppbc = SUCCEEDED(hr) ? spbc.Detach() : NULL; + return hr; +} + +static HRESULT +CreateFileSysBindCtx(_Outptr_ IBindCtx **ppbc) +{ + CComPtr spbc; + BIND_OPTS bo = { sizeof(bo), 0, STGM_CREATE, 0 }; + HRESULT hr = CreateBindCtxWithOpts(&bo, &spbc); + ok(hr == S_OK, "hr = %lx\n", hr); + if (SUCCEEDED(hr)) + { + hr = AddFileSysBindCtx(spbc); + ok(hr == S_OK, "hr = %lx\n", hr); + } + *ppbc = SUCCEEDED(hr) ? spbc.Detach() : NULL; + return hr; +} + +static VOID +PathToIDList(LPCWSTR pszPath, ITEMIDLIST** ppidl) +{ + CComPtr spbc; + HRESULT hr = CreateFileSysBindCtx(&spbc); + ok(hr == S_OK, "hr = %lx\n", hr); + if (SUCCEEDED(hr)) + { + hr = SHParseDisplayName(pszPath, spbc, ppidl, 0, NULL); + ok(hr == S_OK, "hr = %lx\n", hr); + } +} + +// - Adapted from https://blogs.msdn.microsoft.com/oldnewthing/20130503-00/?p=4463/ + + +// We would normally use S_LESSTHAN and S_GREATERTHAN, but w2k3 returns numbers like 3 and -3... +// So instead we check on the sign bit (compare result is the low word of the hresult). +#define SHORT_SIGN_BIT 0x8000 + +static +VOID +compare_imp(IShellFolder* psf, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, HRESULT expected) +{ + HRESULT hr = psf->CompareIDs(0, pidl1, pidl2); + if (expected == S_LESSTHAN) + winetest_ok(SUCCEEDED(hr) && (hr & SHORT_SIGN_BIT), "hr = %lx\n", hr); + else if (expected == S_EQUAL) + winetest_ok(hr == S_EQUAL, "hr = %lx\n", hr); + else if (expected == S_GREATERTHAN) + winetest_ok(SUCCEEDED(hr) && !(hr & SHORT_SIGN_BIT), "hr = %lx\n", hr); + else + winetest_ok(hr == expected, "hr = %lx\n", hr); +} + +// make the winetest_ok look like it came from the line where the compare function was called, and not from inside the compare_imp function :) +#define compare (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : compare_imp + +// Some combination of nullptrs could cause a crash. +static +VOID +TestCompareGuarded(IShellFolder* psf, LPITEMIDLIST desktop) +{ + HRESULT hr = S_OK; + _SEH2_TRY + { + compare(psf, desktop, NULL, E_INVALIDARG); + compare(psf, NULL, desktop, E_INVALIDARG); + compare(psf, desktop, desktop, S_EQUAL); + } + _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER) + { + hr = E_ABORT; + } + _SEH2_END; + ok(hr != E_ABORT, "hr = %lx\n", hr); +} + +static +VOID +TestCompareIDList(IShellFolder* psf) +{ + compare(psf, NULL, NULL, E_INVALIDARG); + + CComHeapPtr desktop; + HRESULT hr = SHGetFolderLocation(NULL, CSIDL_DESKTOP, NULL, NULL, &desktop); + ok(hr == S_OK, "hr = %lx\n", hr); + TestCompareGuarded(psf, desktop); + + // First check the ordering of some special folders against eachother + CComHeapPtr internet; + hr = SHGetFolderLocation(NULL, CSIDL_INTERNET, NULL, NULL, &internet); + ok(hr == S_OK, "hr = %lx\n", hr); + compare(psf, internet, desktop, S_LESSTHAN); + compare(psf, desktop, internet, S_GREATERTHAN); + + CComHeapPtr programs; + hr = SHGetFolderLocation(NULL, CSIDL_PROGRAMS, NULL, NULL, &programs); + ok(hr == S_OK, "hr = %lx\n", hr); + compare(psf, programs, desktop, S_LESSTHAN); + compare(psf, desktop, programs, S_GREATERTHAN); + compare(psf, internet, programs, S_GREATERTHAN); + compare(psf, programs, internet, S_LESSTHAN); + + // Verify that an idlist retrieved from GetCurFolder is equal to the original one. + CComPtr persist; + hr = psf->QueryInterface(IID_PPV_ARG(IPersistFolder2, &persist)); + ok(hr == S_OK, "hr = %lx\n", hr); + if (hr == S_OK) + { + CComHeapPtr cur; + hr = persist->GetCurFolder(&cur); + ok(hr == S_OK, "hr = %lx\n", hr); + compare(psf, cur, desktop, S_EQUAL); + compare(psf, desktop, cur, S_EQUAL); + } + + // Compare special folders against full paths + CComHeapPtr dir1, dir2; + PathToIDList(L"A:\\AAA.AAA", &dir1); + PathToIDList(L"A:\\ZZZ.ZZZ", &dir2); + + compare(psf, dir1, desktop, S_LESSTHAN); + compare(psf, desktop, dir1, S_GREATERTHAN); + compare(psf, dir1, programs, S_LESSTHAN); + compare(psf, programs, dir1, S_GREATERTHAN); + compare(psf, dir1, dir1, S_EQUAL); + + compare(psf, dir2, desktop, S_LESSTHAN); + compare(psf, desktop, dir2, S_GREATERTHAN); + compare(psf, dir2, programs, S_LESSTHAN); + compare(psf, programs, dir2, S_GREATERTHAN); + compare(psf, dir2, dir2, S_EQUAL); + + CComHeapPtr dir3, dir4; + PathToIDList(L"Z:\\AAA.AAA", &dir3); + PathToIDList(L"Z:\\ZZZ.ZZZ", &dir4); + + compare(psf, dir3, desktop, S_LESSTHAN); + compare(psf, desktop, dir3, S_GREATERTHAN); + compare(psf, dir3, programs, S_GREATERTHAN); + compare(psf, programs, dir3, S_LESSTHAN); + compare(psf, dir3, dir3, S_EQUAL); + + compare(psf, dir4, desktop, S_LESSTHAN); + compare(psf, desktop, dir4, S_GREATERTHAN); + compare(psf, dir4, programs, S_GREATERTHAN); + compare(psf, programs, dir4, S_LESSTHAN); + compare(psf, dir4, dir4, S_EQUAL); + + // Now compare the paths against eachother. + compare(psf, dir1, dir2, S_LESSTHAN); + compare(psf, dir2, dir1, S_GREATERTHAN); + + compare(psf, dir2, dir3, S_LESSTHAN); + compare(psf, dir3, dir2, S_GREATERTHAN); + + compare(psf, dir3, dir4, S_LESSTHAN); + compare(psf, dir4, dir3, S_GREATERTHAN); +} + + +static +VOID TestShellFolder( _In_ IShellFolder2 *psf2) { @@ -83,4 +307,5 @@ ok(psf == static_cast(psf2), "Expected %p == %p\n", static_cast(psf), static_cast(psf2)); TestShellFolder(psf2); + TestCompareIDList(psf); }