diff --git a/modules/rostests/apitests/shell32/CMakeLists.txt b/modules/rostests/apitests/shell32/CMakeLists.txt index df1e6cf8af2..02b28625d63 100644 --- a/modules/rostests/apitests/shell32/CMakeLists.txt +++ b/modules/rostests/apitests/shell32/CMakeLists.txt @@ -20,6 +20,7 @@ list(APPEND SOURCE IShellFolderViewCB.cpp OpenAs_RunDLL.cpp PathResolve.cpp + SHCreateDataObject.cpp SHCreateFileExtractIconW.cpp SHParseDisplayName.cpp ShellExecCmdLine.cpp diff --git a/modules/rostests/apitests/shell32/SHCreateDataObject.cpp b/modules/rostests/apitests/shell32/SHCreateDataObject.cpp new file mode 100644 index 00000000000..186134f33ea --- /dev/null +++ b/modules/rostests/apitests/shell32/SHCreateDataObject.cpp @@ -0,0 +1,466 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Test for SHCreateDataObject + * COPYRIGHT: Copyright 2019 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "shelltest.h" +#include +#include +#include +#include + +static DWORD g_WinVersion; + +typedef HRESULT (WINAPI *tSHCreateDataObject)(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUITEMID_CHILD_ARRAY apidl, IDataObject *pdtInner, REFIID riid, void **ppv); +static tSHCreateDataObject pSHCreateDataObject; + + +static void TestAdviseAndCanonical(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUIDLIST_RELATIVE_ARRAY apidl) +{ + CComPtr spDataObj; + HRESULT hr = pSHCreateDataObject(pidlFolder, cidl, apidl, NULL, IID_PPV_ARG(IDataObject, &spDataObj)); + + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + hr = spDataObj->DAdvise(NULL, 0, NULL, NULL); + ok_hex(hr, OLE_E_ADVISENOTSUPPORTED); + + hr = spDataObj->DUnadvise(0); + ok_hex(hr, OLE_E_ADVISENOTSUPPORTED); + + hr = spDataObj->EnumDAdvise(NULL); + ok_hex(hr, OLE_E_ADVISENOTSUPPORTED); + + + FORMATETC in = {1, (DVTARGETDEVICE*)2, 3, 4, 5}; + FORMATETC out = {6, (DVTARGETDEVICE*)7, 8, 9, 10}; + + hr = spDataObj->GetCanonicalFormatEtc(&in, &out); + ok_hex(hr, DATA_S_SAMEFORMATETC); + + if (g_WinVersion < _WIN32_WINNT_VISTA) + { + ok_int(out.cfFormat, 6); + ok_ptr(out.ptd, (void*)7); + ok_int(out.dwAspect, 8); + ok_int(out.lindex, 9); + ok_int(out.tymed, 10); + trace("out unmodified\n"); + } + else + { + ok_int(out.cfFormat, in.cfFormat); + ok_ptr(out.ptd, NULL); + ok_int(out.dwAspect, (int)in.dwAspect); + ok_int(out.lindex, in.lindex); + ok_int(out.tymed, (int)in.tymed); + trace("in copied to out\n"); + } +} + + +static inline PCUIDLIST_ABSOLUTE HIDA_GetPIDLFolder(CIDA const* pida) +{ + return (PCUIDLIST_ABSOLUTE)(((LPBYTE)pida) + (pida)->aoffset[0]); +} + +static inline PCUIDLIST_RELATIVE HIDA_GetPIDLItem(CIDA const* pida, SIZE_T i) +{ + return (PCUIDLIST_RELATIVE)(((LPBYTE)pida) + (pida)->aoffset[i + 1]); +} + +#define ok_wstri(x, y) \ + ok(wcsicmp(x, y) == 0, "Wrong string. Expected '%S', got '%S'\n", y, x) + +static void TestHIDA(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2) +{ + LPIDA pida = (LPIDA)pData; + + ok_int(pida->cidl, 2); + if (pida->cidl != 2) + return; + + WCHAR FolderPath[MAX_PATH], Item1[MAX_PATH], Item2[MAX_PATH]; + BOOL bRet = SHGetPathFromIDListW(HIDA_GetPIDLFolder(pida), FolderPath); + ok_int(bRet, TRUE); + if (!bRet) + return; + ok_wstri(FolderPath, ExpectRoot); + + CComHeapPtr pidl1(ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, 0))); + CComHeapPtr pidl2(ILCombine(HIDA_GetPIDLFolder(pida), HIDA_GetPIDLItem(pida, 1))); + + bRet = SHGetPathFromIDListW(pidl1, Item1); + ok_int(bRet, TRUE); + if (!bRet) + return; + ok_wstri(Item1, ExpectPath1); + + bRet = SHGetPathFromIDListW(pidl2, Item2); + ok_int(bRet, TRUE); + if (!bRet) + return; + ok_wstri(Item2, ExpectPath2); +} + +static void TestHDROP(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2) +{ + DROPFILES* pDropFiles = (DROPFILES*)pData; + ok_int(pDropFiles->fWide, TRUE); + + LPCWSTR Expected[2] = { ExpectPath1, ExpectPath2 }; + + DWORD offset = pDropFiles->pFiles; + UINT Count = 0; + for (;;Count++) + { + LPCWSTR ptr = (LPCWSTR)(((BYTE*)pDropFiles) + offset); + if (!*ptr) + break; + + if (Count < _countof(Expected)) + ok_wstri(Expected[Count], ptr); + + offset += (wcslen(ptr) + 1) * sizeof(WCHAR); + } + ok_int(Count, 2); +} + +static void TestFilenameA(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2) +{ + LPCSTR FirstFile = (LPCSTR)pData; + LPWSTR FirstFileW; + + HRESULT hr = SHStrDupA(FirstFile, &FirstFileW); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + ok_wstri(ExpectPath1, FirstFileW); + CoTaskMemFree(FirstFileW); +} + +static void TestFilenameW(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2) +{ + LPCWSTR FirstFile = (LPCWSTR)pData; + ok_wstri(ExpectPath1, FirstFile); +} + + +static void TestDefaultFormat(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUIDLIST_RELATIVE_ARRAY apidl) +{ + CComPtr spDataObj; + HRESULT hr = pSHCreateDataObject(pidlFolder, cidl, apidl, NULL, IID_PPV_ARG(IDataObject, &spDataObj)); + + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + CComPtr pEnumFmt; + hr = spDataObj->EnumFormatEtc(DATADIR_GET, &pEnumFmt); + + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + UINT Expected[4] = { + RegisterClipboardFormatA(CFSTR_SHELLIDLISTA), + CF_HDROP, + RegisterClipboardFormatA(CFSTR_FILENAMEA), + RegisterClipboardFormatA("FileNameW"), + }; + + UINT Count = 0; + FORMATETC fmt; + while (S_OK == (hr=pEnumFmt->Next(1, &fmt, NULL))) + { + char szGot[512], szExpected[512]; + GetClipboardFormatNameA(fmt.cfFormat, szGot, sizeof(szGot)); + ok(Count < _countof(Expected), "%u\n", Count); + if (Count < _countof(Expected)) + { + GetClipboardFormatNameA(Expected[Count], szExpected, sizeof(szExpected)); + ok(fmt.cfFormat == Expected[Count], "Got 0x%x(%s), expected 0x%x(%s) for %u\n", + fmt.cfFormat, szGot, Expected[Count], szExpected, Count); + } + + ok(fmt.ptd == NULL, "Got 0x%p, expected 0x%p for [%u].ptd\n", fmt.ptd, (void*)NULL, Count); + ok(fmt.dwAspect == DVASPECT_CONTENT, "Got 0x%lu, expected 0x%d for [%u].dwAspect\n", fmt.dwAspect, DVASPECT_CONTENT, Count); + ok(fmt.lindex == -1, "Got 0x%lx, expected 0x%x for [%u].lindex\n", fmt.lindex, -1, Count); + ok(fmt.tymed == TYMED_HGLOBAL, "Got 0x%lu, expected 0x%d for [%u].tymed\n", fmt.tymed, TYMED_HGLOBAL, Count); + + Count++; + } + trace("Got %u formats\n", Count); + ULONG ExpectedCount = (g_WinVersion < _WIN32_WINNT_WIN8) ? 1 : 4; + ok_int(Count, (int)ExpectedCount); + ok_hex(hr, S_FALSE); + + typedef void (*TestFunction)(PVOID pData, SIZE_T Size, LPCWSTR ExpectRoot, LPCWSTR ExpectPath1, LPCWSTR ExpectPath2); + TestFunction TestFormats[] = { + TestHIDA, + TestHDROP, + TestFilenameA, + TestFilenameW, + }; + + WCHAR ExpectRoot[MAX_PATH], ExpectItem1[MAX_PATH], ExpectItem2[MAX_PATH]; + + hr = SHGetFolderPathW(NULL, CSIDL_WINDOWS, NULL, 0, ExpectRoot); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + hr = SHGetFolderPathW(NULL, CSIDL_SYSTEM, NULL, 0, ExpectItem1); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + hr = SHGetFolderPathW(NULL, CSIDL_RESOURCES, NULL, 0, ExpectItem2); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + + /* The formats are not synthesized on request */ + for (Count = 0; Count < _countof(Expected); ++Count) + { + STGMEDIUM medium = {0}; + FORMATETC etc = { (CLIPFORMAT)Expected[Count], NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + char szExpected[512]; + + GetClipboardFormatNameA(etc.cfFormat, szExpected, sizeof(szExpected)); + hr = spDataObj->GetData(&etc, &medium); + HRESULT hr2 = spDataObj->QueryGetData(&etc); + ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE); + + if (Count < ExpectedCount) + { + ok(hr == S_OK, "0x%x (0x%x(%s))\n", (unsigned int)hr, Expected[Count], szExpected); + ok(medium.tymed == TYMED_HGLOBAL, "0x%lx (0x%x(%s))\n", medium.tymed, Expected[Count], szExpected); + if (hr == S_OK && medium.tymed == TYMED_HGLOBAL) + { + PVOID pData = GlobalLock(medium.hGlobal); + SIZE_T Size = GlobalSize(medium.hGlobal); + TestFormats[Count](pData, Size, ExpectRoot, ExpectItem1, ExpectItem2); + GlobalUnlock(medium.hGlobal); + } + } + else + { + if (g_WinVersion < _WIN32_WINNT_VISTA) + ok(hr == E_INVALIDARG, "0x%x (0x%x(%s))\n", (unsigned int)hr, Expected[Count], szExpected); + else + ok(hr == DV_E_FORMATETC, "0x%x (0x%x(%s))\n", (unsigned int)hr, Expected[Count], szExpected); + } + + if (SUCCEEDED(hr)) + ReleaseStgMedium(&medium); + } + + CLIPFORMAT Format = RegisterClipboardFormatW(CFSTR_PREFERREDDROPEFFECTW); + FORMATETC formatetc = { Format, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM medium; + + hr = spDataObj->GetData(&formatetc, &medium); + if (g_WinVersion < _WIN32_WINNT_VISTA) + ok_hex(hr, E_INVALIDARG); + else + ok_hex(hr, DV_E_FORMATETC); +} + + +static void TestSetAndGetExtraFormat(PCIDLIST_ABSOLUTE pidlFolder, UINT cidl, PCUIDLIST_RELATIVE_ARRAY apidl) +{ + CComPtr spDataObj; + HRESULT hr = pSHCreateDataObject(pidlFolder, cidl, apidl, NULL, IID_PPV_ARG(IDataObject, &spDataObj)); + + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + STGMEDIUM medium = {0}; + medium.tymed = TYMED_HGLOBAL; + medium.hGlobal = GlobalAlloc(GHND, sizeof(DWORD)); + ok(medium.hGlobal != NULL, "Download more ram\n"); + PDWORD data = (PDWORD)GlobalLock(medium.hGlobal); + *data = 12345; + GlobalUnlock(medium.hGlobal); + + UINT flags = GlobalFlags(medium.hGlobal); + SIZE_T size = GlobalSize(medium.hGlobal); + ok_hex(flags, 0); + ok_size_t(size, sizeof(DWORD)); + + FORMATETC etc = { (CLIPFORMAT)RegisterClipboardFormatA(CFSTR_INDRAGLOOPA), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + FORMATETC etc2 = etc; + + /* Not supported! */ + hr = spDataObj->SetData(&etc, &medium, FALSE); + if (g_WinVersion < _WIN32_WINNT_WIN8) + ok_hex(hr, E_INVALIDARG); + else + ok_hex(hr, E_NOTIMPL); + + /* Object takes ownership! */ + hr = spDataObj->SetData(&etc, &medium, TRUE); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + /* Does not touch the hGlobal! */ + flags = GlobalFlags(medium.hGlobal); + size = GlobalSize(medium.hGlobal); + ok_hex(flags, 0); + ok_size_t(size, sizeof(DWORD)); + + STGMEDIUM medium2 = {0}; + + /* No conversion */ + etc2.dwAspect = DVASPECT_DOCPRINT; + hr = spDataObj->GetData(&etc2, &medium2); + HRESULT hr2 = spDataObj->QueryGetData(&etc2); + ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE); + if (g_WinVersion < _WIN32_WINNT_VISTA) + ok_hex(hr, E_INVALIDARG); + else + ok_hex(hr, DV_E_FORMATETC); + + etc2.dwAspect = DVASPECT_CONTENT; + etc2.tymed = TYMED_NULL; + hr = spDataObj->GetData(&etc2, &medium2); + hr2 = spDataObj->QueryGetData(&etc2); + ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE); + if (g_WinVersion < _WIN32_WINNT_VISTA) + ok_hex(hr, E_INVALIDARG); + else + ok_hex(hr, DV_E_FORMATETC); + etc2.tymed = TYMED_HGLOBAL; + + ok_ptr(medium2.pUnkForRelease, NULL); + hr = spDataObj->GetData(&etc2, &medium2); + hr2 = spDataObj->QueryGetData(&etc2); + ok_hex(hr2, SUCCEEDED(hr) ? S_OK : S_FALSE); + ok_hex(hr, S_OK); + if (hr == S_OK) + { + ok_hex(medium2.tymed, TYMED_HGLOBAL); + if (g_WinVersion < _WIN32_WINNT_VISTA) + { + /* The IDataObject is set as pUnkForRelease */ + ok(medium2.pUnkForRelease == (IUnknown*)spDataObj, "Expected the data object (0x%p), got 0x%p\n", + (IUnknown*)spDataObj, medium2.pUnkForRelease); + ok(medium.hGlobal == medium2.hGlobal, "Pointers are not the same!, got 0x%p and 0x%p\n", medium.hGlobal, medium2.hGlobal); + } + else + { + ok_ptr(medium2.pUnkForRelease, NULL); + ok(medium.hGlobal != medium2.hGlobal, "Pointers are the same!\n"); + } + + flags = GlobalFlags(medium2.hGlobal); + size = GlobalSize(medium2.hGlobal); + ok_hex(flags, 0); + ok_size_t(size, sizeof(DWORD)); + + data = (PDWORD)GlobalLock(medium2.hGlobal); + if (data) + ok_int(*data, 12345); + else + ok(0, "GlobalLock: %lu\n", GetLastError()); + GlobalUnlock(medium2.hGlobal); + + HGLOBAL backup = medium2.hGlobal; + ReleaseStgMedium(&medium2); + + flags = GlobalFlags(backup); + size = GlobalSize(backup); + if (g_WinVersion < _WIN32_WINNT_VISTA) + { + /* Same object! just the pUnkForRelease was set, so original hGlobal is still valid */ + ok_hex(flags, 0); + ok_size_t(size, sizeof(DWORD)); + } + else + { + ok_hex(flags, GMEM_INVALID_HANDLE); + ok_size_t(size, 0); + } + + /* Original is still intact (but no longer ours!) */ + flags = GlobalFlags(medium.hGlobal); + size = GlobalSize(medium.hGlobal); + ok_hex(flags, 0); + ok_size_t(size, sizeof(DWORD)); + } + + HGLOBAL backup = medium.hGlobal; + spDataObj.Release(); + + /* Now our hGlobal is deleted */ + flags = GlobalFlags(backup); + size = GlobalSize(backup); + ok_hex(flags, GMEM_INVALID_HANDLE); + ok_size_t(size, 0); +} + +START_TEST(SHCreateDataObject) +{ + HRESULT hr; + + pSHCreateDataObject = (tSHCreateDataObject)GetProcAddress(GetModuleHandleA("shell32.dll"), "SHCreateDataObject"); + if (!pSHCreateDataObject) + { + skip("shell32!SHCreateDataObject not exported\n"); + } + + + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + + RTL_OSVERSIONINFOEXW rtlinfo = {0}; + + rtlinfo.dwOSVersionInfoSize = sizeof(rtlinfo); + RtlGetVersion((PRTL_OSVERSIONINFOW)&rtlinfo); + g_WinVersion = (rtlinfo.dwMajorVersion << 8) | rtlinfo.dwMinorVersion; + + CComHeapPtr pidlWindows; + CComHeapPtr pidlSystem32; + CComHeapPtr pidlResources; + + hr = SHGetFolderLocation(NULL, CSIDL_WINDOWS, NULL, 0, &pidlWindows); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + hr = SHGetFolderLocation(NULL, CSIDL_SYSTEM, NULL, 0, &pidlSystem32); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + hr = SHGetFolderLocation(NULL, CSIDL_RESOURCES, NULL, 0, &pidlResources); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + CComPtr shellFolder; + PCUITEMID_CHILD child1; + hr = SHBindToParent(pidlSystem32, IID_PPV_ARG(IShellFolder, &shellFolder), &child1); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + PCUITEMID_CHILD child2 = ILFindLastID(pidlResources); + + UINT cidl = 2; + PCUIDLIST_RELATIVE apidl[2] = { + child1, child2 + }; + + TestAdviseAndCanonical(pidlWindows, cidl, apidl); + TestDefaultFormat(pidlWindows, cidl, apidl); + TestSetAndGetExtraFormat(pidlWindows, cidl, apidl); +} diff --git a/modules/rostests/apitests/shell32/testlist.c b/modules/rostests/apitests/shell32/testlist.c index ffd832f0ba2..7b603dff147 100644 --- a/modules/rostests/apitests/shell32/testlist.c +++ b/modules/rostests/apitests/shell32/testlist.c @@ -15,6 +15,7 @@ extern void func_IShellFolderViewCB(void); extern void func_menu(void); extern void func_OpenAs_RunDLL(void); extern void func_PathResolve(void); +extern void func_SHCreateDataObject(void); extern void func_SHCreateFileExtractIconW(void); extern void func_ShellExecCmdLine(void); extern void func_ShellExecuteEx(void); @@ -35,6 +36,7 @@ const struct test winetest_testlist[] = { "menu", func_menu }, { "OpenAs_RunDLL", func_OpenAs_RunDLL }, { "PathResolve", func_PathResolve }, + { "SHCreateDataObject", func_SHCreateDataObject }, { "SHCreateFileExtractIconW", func_SHCreateFileExtractIconW }, { "ShellExecCmdLine", func_ShellExecCmdLine }, { "ShellExecuteEx", func_ShellExecuteEx },