From 0340c5188c62077b6a2a55ee4bdea24e23d2dab3 Mon Sep 17 00:00:00 2001 From: Mark Jansen Date: Sat, 4 Jan 2020 17:49:02 +0100 Subject: [PATCH] [ZIPFLDR_APITEST] Add some basic IDataObject tests CORE-16241 --- modules/rostests/apitests/CMakeLists.txt | 1 + modules/rostests/apitests/include/apitest.h | 3 + .../rostests/apitests/zipfldr/CMakeLists.txt | 22 + .../rostests/apitests/zipfldr/EnumObjects.cpp | 266 ++++++++ .../rostests/apitests/zipfldr/IDataObject.cpp | 568 ++++++++++++++++++ modules/rostests/apitests/zipfldr/precomp.h | 32 + modules/rostests/apitests/zipfldr/resource.h | 3 + modules/rostests/apitests/zipfldr/testlist.c | 12 + .../apitests/zipfldr/zipfldr_apitest.rc | 8 + .../apitests/zipfldr/zipfldr_test_enum.zip | Bin 0 -> 1634 bytes .../apitests/zipfldr/zipfldr_test_file.zip | Bin 0 -> 600 bytes 11 files changed, 915 insertions(+) create mode 100644 modules/rostests/apitests/zipfldr/CMakeLists.txt create mode 100644 modules/rostests/apitests/zipfldr/EnumObjects.cpp create mode 100644 modules/rostests/apitests/zipfldr/IDataObject.cpp create mode 100644 modules/rostests/apitests/zipfldr/precomp.h create mode 100644 modules/rostests/apitests/zipfldr/resource.h create mode 100644 modules/rostests/apitests/zipfldr/testlist.c create mode 100644 modules/rostests/apitests/zipfldr/zipfldr_apitest.rc create mode 100644 modules/rostests/apitests/zipfldr/zipfldr_test_enum.zip create mode 100644 modules/rostests/apitests/zipfldr/zipfldr_test_file.zip diff --git a/modules/rostests/apitests/CMakeLists.txt b/modules/rostests/apitests/CMakeLists.txt index 2662835eeeb..59bdeb46832 100644 --- a/modules/rostests/apitests/CMakeLists.txt +++ b/modules/rostests/apitests/CMakeLists.txt @@ -56,3 +56,4 @@ add_subdirectory(winprint) add_subdirectory(winspool) add_subdirectory(wlanapi) add_subdirectory(ws2_32) +add_subdirectory(zipfldr) diff --git a/modules/rostests/apitests/include/apitest.h b/modules/rostests/apitests/include/apitest.h index da2430e929e..27777f7a730 100644 --- a/modules/rostests/apitests/include/apitest.h +++ b/modules/rostests/apitests/include/apitest.h @@ -32,4 +32,7 @@ ExceptionStatus, (ExpectedStatus)); \ } +#define ok_hr(status, expected) ok_hex(status, expected) +#define ok_hr_(file, line, status, expected) ok_hex_(file, line, status, expected) + #endif /* _APITEST_H */ diff --git a/modules/rostests/apitests/zipfldr/CMakeLists.txt b/modules/rostests/apitests/zipfldr/CMakeLists.txt new file mode 100644 index 00000000000..20a7abf8dc8 --- /dev/null +++ b/modules/rostests/apitests/zipfldr/CMakeLists.txt @@ -0,0 +1,22 @@ + +add_definitions(-DINITGUID -DWINETEST_USE_DBGSTR_LONGLONG) +set_cpp(WITH_RUNTIME WITH_EXCEPTIONS) + +include_directories(${REACTOS_SOURCE_DIR}/sdk/lib/atl) + +list(APPEND SOURCE + EnumObjects.cpp + IDataObject.cpp + precomp.h + resource.h) + +add_executable(zipfldr_apitest + ${SOURCE} + testlist.c + zipfldr_apitest.rc) + +target_link_libraries(zipfldr_apitest wine uuid) +set_module_type(zipfldr_apitest win32cui) +add_importlibs(zipfldr_apitest shlwapi ole32 shell32 user32 msvcrt kernel32 ntdll) +add_pch(zipfldr_apitest precomp.h SOURCE) +add_rostests_file(TARGET zipfldr_apitest) diff --git a/modules/rostests/apitests/zipfldr/EnumObjects.cpp b/modules/rostests/apitests/zipfldr/EnumObjects.cpp new file mode 100644 index 00000000000..a7c1749c481 --- /dev/null +++ b/modules/rostests/apitests/zipfldr/EnumObjects.cpp @@ -0,0 +1,266 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Test for zipfldr + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +static bool g_bOldZipfldr = false; + +#define ok_displayname(pFolder, pidl, Flags, Name) ok_displayname_(__FILE__, __LINE__, pFolder, pidl, Flags, Name) +static void ok_displayname_(const char* file, int line, IShellFolder* pFolder, PCUITEMID_CHILD pidl, SHGDNF Flags, LPCWSTR Name) +{ + STRRET NameRet; + HRESULT hr; + ok_hr_(file, line, (hr = pFolder->GetDisplayNameOf(pidl, Flags, &NameRet)), S_OK); + if (!SUCCEEDED(hr)) + return; + + WCHAR DisplayName[MAX_PATH]; + ok_hr_(file, line, (hr = StrRetToBufW(&NameRet, pidl, DisplayName, ARRAYSIZE(DisplayName))), S_OK); + if (!SUCCEEDED(hr)) + return; + + ok_wstr_(file, line, DisplayName, Name); +} + +struct ZipFiles +{ + LPCWSTR Name; + SFGAOF Attributes; +}; + +static CLIPFORMAT cfHIDA = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA); +static CLIPFORMAT cfFileDescriptor = RegisterClipboardFormatW(CFSTR_FILEDESCRIPTORW); + +static void test_EnumObjects_Files(const WCHAR* Filename, IShellFolder* pFolder) +{ + CComPtr spEnum; + HRESULT hr = pFolder->EnumObjects(NULL, SHCONTF_NONFOLDERS, &spEnum); + ok_hr(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + SFGAOF BaseAttributes = SFGAO_STREAM | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_CANCOPY | SFGAO_CANMOVE; + ZipFiles ExpectedFiles[] = { + { L"cccc.txt", BaseAttributes }, + { L"bbbb.txt", BaseAttributes }, + }; + + ULONG totalFetched = 0; + do + { + CComHeapPtr child; + ULONG celtFetched = 0; + hr = spEnum->Next(1, &child, &celtFetched); + if (hr != S_OK) + break; + ok_int(celtFetched, 1); + if (celtFetched != 1) + break; + + LPCWSTR ExpectedName = totalFetched < ARRAYSIZE(ExpectedFiles) ? ExpectedFiles[totalFetched].Name : L""; + SFGAOF ExpectedAttributes = totalFetched < ARRAYSIZE(ExpectedFiles) ? ExpectedFiles[totalFetched].Attributes : 0xdeaddead; + + totalFetched++; + + + CComPtr spDataObj; + hr = pFolder->GetUIObjectOf(NULL, 1, &child, IID_IDataObject, NULL, reinterpret_cast(&spDataObj)); + ok_hr(hr, S_OK); + + if (!g_bOldZipfldr && !IsFormatAdvertised(spDataObj, cfHIDA, TYMED_HGLOBAL)) + { + trace("Pre-Vista zipfldr\n"); + // No seconds in filetimes, less functional IStream* implementation + g_bOldZipfldr = true; + } + + ok_displayname(pFolder, child, SHGDN_NORMAL, ExpectedName); + if (g_bOldZipfldr) + { + ok_displayname(pFolder, child, SHGDN_FORPARSING, ExpectedName); + } + else + { + WCHAR Buffer[MAX_PATH]; + StringCchPrintfW(Buffer, _countof(Buffer), L"%s\\%s", Filename, ExpectedName); + ok_displayname(pFolder, child, SHGDN_FORPARSING, Buffer); + } + ok_displayname(pFolder, child, SHGDN_FORPARSING | SHGDN_INFOLDER, ExpectedName); + ok_displayname(pFolder, child, SHGDN_FOREDITING , ExpectedName); + ok_displayname(pFolder, child, SHGDN_FOREDITING | SHGDN_INFOLDER, ExpectedName); + ok_displayname(pFolder, child, SHGDN_FORADDRESSBAR , ExpectedName); + ok_displayname(pFolder, child, SHGDN_FORADDRESSBAR | SHGDN_INFOLDER, ExpectedName); + + SFGAOF Attributes = 0; + hr = pFolder->GetAttributesOf(1, &child, &Attributes); + ok_hr(hr, S_OK); + ok_hex(Attributes, ExpectedAttributes); + + STGMEDIUM medium = {0}; + FORMATETC etc = { cfFileDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + HRESULT hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + continue; + + ok_hex(medium.tymed, TYMED_HGLOBAL); + PVOID pData = GlobalLock(medium.hGlobal); + FILEGROUPDESCRIPTORW* Descriptor = static_cast(pData); + ok_hex(Descriptor->cItems, 1); + if (Descriptor->cItems == 1) + ok_wstr(Descriptor->fgd[0].cFileName, ExpectedName); + GlobalUnlock(medium.hGlobal); + ReleaseStgMedium(&medium); + } while (true); + + ok_int(totalFetched, ARRAYSIZE(ExpectedFiles)); + ok_hr(hr, S_FALSE); +} + +static void test_EnumObjects_Folders(const WCHAR* Filename, IShellFolder* pFolder) +{ + CComPtr spEnum; + HRESULT hr = pFolder->EnumObjects(NULL, SHCONTF_FOLDERS, &spEnum); + ok_hr(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + SFGAOF BaseAttributes = SFGAO_FOLDER | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | SFGAO_CANCOPY | SFGAO_CANMOVE; + ZipFiles ExpectedFolders[] = { + { L"aaaa", BaseAttributes | (g_bOldZipfldr ? 0 : SFGAO_STORAGE) }, + { L"cccc", BaseAttributes | (g_bOldZipfldr ? 0 : SFGAO_STORAGE) }, + }; + + LPCWSTR ExpectedExtraFiles[2][4] = + { + { + L"aaaa\\a_aaaa", + L"aaaa\\a_cccc", + L"aaaa\\a_bbbb.txt", + L"aaaa\\a_cccc.txt", + }, + { + L"cccc\\c_cccc", + L"cccc\\c_bbbb.txt", + L"cccc\\c_cccc.txt", + L"cccc\\c_aaaa", + }, + }; + + ULONG totalFetched = 0; + do + { + CComHeapPtr child; + ULONG celtFetched = 0; + hr = spEnum->Next(1, &child, &celtFetched); + if (hr != S_OK) + break; + ok_int(celtFetched, 1); + if (celtFetched != 1) + break; + + LPCWSTR ExpectedName = totalFetched < ARRAYSIZE(ExpectedFolders) ? ExpectedFolders[totalFetched].Name : L""; + SFGAOF ExpectedAttributes = totalFetched < ARRAYSIZE(ExpectedFolders) ? ExpectedFolders[totalFetched].Attributes : 0xdeaddead; + LPCWSTR* ExtraFiles = totalFetched < ARRAYSIZE(ExpectedExtraFiles) ? ExpectedExtraFiles[totalFetched] : ExpectedExtraFiles[0]; + + totalFetched++; + + ok_displayname(pFolder, child, SHGDN_NORMAL, ExpectedName); + if (g_bOldZipfldr) + { + ok_displayname(pFolder, child, SHGDN_FORPARSING, ExpectedName); + } + else + { + WCHAR Buffer[MAX_PATH]; + StringCchPrintfW(Buffer, _countof(Buffer), L"%s\\%s", Filename, ExpectedName); + ok_displayname(pFolder, child, SHGDN_FORPARSING, Buffer); + } + ok_displayname(pFolder, child, SHGDN_FORPARSING | SHGDN_INFOLDER, ExpectedName); + ok_displayname(pFolder, child, SHGDN_FOREDITING , ExpectedName); + ok_displayname(pFolder, child, SHGDN_FOREDITING | SHGDN_INFOLDER, ExpectedName); + + SFGAOF Attributes = 0; + hr = pFolder->GetAttributesOf(1, &child, &Attributes); + ok_hr(hr, S_OK); + ok_hex(Attributes, ExpectedAttributes); + + CComPtr spData; + hr = pFolder->GetUIObjectOf(NULL, 1, &child, IID_IDataObject, NULL, reinterpret_cast(&spData)); + ok_hr(hr, S_OK); + + STGMEDIUM medium = {0}; + FORMATETC etc = { cfFileDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + HRESULT hr = spData->GetData(&etc, &medium); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + continue; + + ok_hex(medium.tymed, TYMED_HGLOBAL); + PVOID pData = GlobalLock(medium.hGlobal); + FILEGROUPDESCRIPTORW* Descriptor = static_cast(pData); + ok_hex(Descriptor->cItems, 5); + if (Descriptor->cItems == 5) + { + ok_wstr(Descriptor->fgd[0].cFileName, ExpectedName); + ok_wstr(Descriptor->fgd[1].cFileName, ExtraFiles[0]); + ok_wstr(Descriptor->fgd[2].cFileName, ExtraFiles[1]); + ok_wstr(Descriptor->fgd[3].cFileName, ExtraFiles[2]); + ok_wstr(Descriptor->fgd[4].cFileName, ExtraFiles[3]); + } + GlobalUnlock(medium.hGlobal); + ReleaseStgMedium(&medium); + } while (true); + + ok_int(totalFetched, ARRAYSIZE(ExpectedFolders)); + ok_hr(hr, S_FALSE); +} + +static void test_EnumObjects(const WCHAR* Filename) +{ + CComPtr spFolder; + if (!InitializeShellFolder(Filename, spFolder)) + return; + + // Let's ask the shell how it wants to display the full path to our zip file + WCHAR ZipFilename[MAX_PATH] = L""; + { + CComPtr spPersistFolder; + HRESULT hr = spFolder->QueryInterface(IID_PPV_ARG(IPersistFolder2, &spPersistFolder)); + ok_hr(hr, S_OK); + if (SUCCEEDED(hr)) + { + CComHeapPtr zipPidl; + hr = spPersistFolder->GetCurFolder(&zipPidl); + ok_hr(hr, S_OK); + if (SUCCEEDED(hr)) + { + BOOL bHasPath = SHGetPathFromIDListW(zipPidl, ZipFilename); + ok_int(bHasPath, TRUE); + } + } + } + + + test_EnumObjects_Files(ZipFilename, spFolder); + test_EnumObjects_Folders(ZipFilename, spFolder); +} + +START_TEST(EnumObjects) +{ + HRESULT hr = CoInitialize(NULL); + + ok_hr(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + WCHAR ZipTestFile[MAX_PATH]; + if (!extract_resource(ZipTestFile, MAKEINTRESOURCEW(IDR_ZIP_TEST_ENUM))) + return; + test_EnumObjects(ZipTestFile); + DeleteFileW(ZipTestFile); +} diff --git a/modules/rostests/apitests/zipfldr/IDataObject.cpp b/modules/rostests/apitests/zipfldr/IDataObject.cpp new file mode 100644 index 00000000000..ac2a7dfe4bc --- /dev/null +++ b/modules/rostests/apitests/zipfldr/IDataObject.cpp @@ -0,0 +1,568 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Test for zipfldr + * COPYRIGHT: Copyright 2020 Mark Jansen (mark.jansen@reactos.org) + */ + +#include "precomp.h" + +#ifndef FILE_ATTRIBUTE_VIRTUAL +#define FILE_ATTRIBUTE_VIRTUAL 0x10000 +#endif + +DEFINE_GUID(CLSID_ZipFolderContextMenu, 0xb8cdcb65, 0xb1bf, 0x4b42, 0x94, 0x28, 0x1d, 0xfd, 0xb7, 0xee, 0x92, 0xaf); + + +static bool g_bOldZipfldr; +const char test_file_1_contents[] = "Some generic text in the root file.\r\nMore text on a new line."; +const char test_file_2_contents[] = "Some generic text in the file in folder_1.\r\nMore text on a new line."; + +static BOOL write_raw_file(const WCHAR* FileName, const void* Data, DWORD Size) +{ + BOOL Success; + DWORD dwWritten; + HANDLE Handle = CreateFileW(FileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (Handle == INVALID_HANDLE_VALUE) + { + skip("Failed to create temp file %ls, error %lu\n", FileName, GetLastError()); + return FALSE; + } + Success = WriteFile(Handle, Data, Size, &dwWritten, NULL); + ok(Success == TRUE, "WriteFile failed with %lu\n", GetLastError()); + ok(dwWritten == Size, "WriteFile wrote %lu bytes instead of %lu\n", dwWritten, Size); + CloseHandle(Handle); + return Success && (dwWritten == Size); +} + +BOOL extract_resource(WCHAR* Filename, LPCWSTR ResourceName) +{ + WCHAR workdir[MAX_PATH]; + GetTempPathW(_countof(workdir), workdir); + StringCchPrintfW(Filename, MAX_PATH, L"%sTMP%u.zip", workdir, GetTickCount() & 0xffff); + + HMODULE hMod = GetModuleHandleW(NULL); + HRSRC hRsrc = FindResourceW(hMod, ResourceName, MAKEINTRESOURCEW(RT_RCDATA)); + ok(!!hRsrc, "Unable to find %s\n", wine_dbgstr_w(ResourceName)); + if (!hRsrc) + return FALSE; + + HGLOBAL hGlobal = LoadResource(hMod, hRsrc); + DWORD Size = SizeofResource(hMod, hRsrc); + LPVOID pData = LockResource(hGlobal); + + ok(Size && !!pData, "Unable to load %s\n", wine_dbgstr_w(ResourceName)); + if (!Size || !pData) + return FALSE; + + BOOL Written = write_raw_file(Filename, pData, Size); + UnlockResource(pData); + return Written; +} + +bool InitializeShellFolder_(const char* file, int line, const WCHAR* Filename, CComPtr& spFolder) +{ + CComHeapPtr pidl; + HRESULT hr; + ok_hr_(file, line, (hr = SHParseDisplayName(Filename, NULL, &pidl, 0, NULL)), S_OK); + if (!SUCCEEDED(hr)) + return false; + + CComPtr spParent; + PCUIDLIST_RELATIVE pidlLast; + ok_hr_(file, line, (hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &spParent), &pidlLast)), S_OK); + if (!SUCCEEDED(hr)) + return false; + + ok_hr_(file, line, (hr = spParent->BindToObject(pidlLast, 0, IID_PPV_ARG(IShellFolder, &spFolder))), S_OK); + + return SUCCEEDED(hr); +} + +#define GetFirstDataObject(spFolder, grfFlags, spData) GetFirstDataObject_(__FILE__, __LINE__, spFolder, grfFlags, spData) +static bool GetFirstDataObject_(const char* file, int line, IShellFolder* spFolder, SHCONTF grfFlags, CComPtr& spData) +{ + CComPtr spEnum; + HRESULT hr; + ok_hr_(file, line, (hr = spFolder->EnumObjects(NULL, grfFlags, &spEnum)), S_OK); + if (!SUCCEEDED(hr)) + return false; + + CComHeapPtr child; + ULONG celtFetched = 0; + ok_hr_(file, line, (hr = spEnum->Next(1, &child, &celtFetched)), S_OK); + ok_int_(file, line, celtFetched, 1); + if (!SUCCEEDED(hr)) + return false; + + // This call fails without the extension being '.zip' + ok_hr_(file, line, (hr = spFolder->GetUIObjectOf(NULL, 1, &child, IID_IDataObject, NULL, reinterpret_cast(&spData))), S_OK); + return SUCCEEDED(hr); +} + +bool IsFormatAdvertised_(const char* file, int line, IDataObject* pDataObj, CLIPFORMAT cfFormat, TYMED tymed) +{ + CComPtr pEnumFmt; + HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &pEnumFmt); + + ok_hex_(file, line, hr, S_OK); + if (!SUCCEEDED(hr)) + return false; + + FORMATETC fmt; + while (S_OK == (hr = pEnumFmt->Next(1, &fmt, NULL))) + { + if (fmt.cfFormat == cfFormat) + { + ok_hex_(file, line, fmt.lindex, -1); + if (tymed) + ok_hex_(file, line, fmt.tymed, tymed); + return true; + } + } + ok_hex_(file, line, hr, S_FALSE); + return false; +} + +#if 0 +#define DumpDataObjectFormats(pDataObj) DumpDataObjectFormats_(__FILE__, __LINE__, pDataObj) +static inline void DumpDataObjectFormats_(const char* file, int line, IDataObject* pDataObj) +{ + CComPtr pEnumFmt; + HRESULT hr = pDataObj->EnumFormatEtc(DATADIR_GET, &pEnumFmt); + + if (FAILED_UNEXPECTEDLY(hr)) + return; + + FORMATETC fmt; + while (S_OK == pEnumFmt->Next(1, &fmt, NULL)) + { + char szBuf[512]; + GetClipboardFormatNameA(fmt.cfFormat, szBuf, sizeof(szBuf)); + trace_(file, line)("Format: %s\n", szBuf); + trace_(file, line)(" Tymed: %u\n", fmt.tymed); + if (fmt.tymed & TYMED_HGLOBAL) + { + trace_(file, line)(" TYMED_HGLOBAL supported\n"); + } + } +} +#endif + +static void test_FileDescriptor(FILEGROUPDESCRIPTORW* Descriptor) +{ + ok_int(Descriptor->cItems, 1u); + if (Descriptor->cItems > 0) + { + FILETIME LocalFileTime, FileTime; + WORD Mask = g_bOldZipfldr ? 0xffe0 : (0xffff); // bits 0-4 are the seconds + DosDateTimeToFileTime(0x5024, (0xa5f2 & Mask), &LocalFileTime); + LocalFileTimeToFileTime(&LocalFileTime, &FileTime); + + FILEDESCRIPTORW* FileDescriptor = Descriptor->fgd; + ok_hex(FileDescriptor->dwFlags, FD_ATTRIBUTES | FD_WRITESTIME | FD_FILESIZE | FD_PROGRESSUI); + ok_hex(FileDescriptor->dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_VIRTUAL); + ok_hex(FileDescriptor->ftLastWriteTime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(FileDescriptor->ftLastWriteTime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(FileDescriptor->nFileSizeHigh, 0); + ok_hex(FileDescriptor->nFileSizeLow, strlen(test_file_1_contents)); + ok_wstr(FileDescriptor->cFileName, L"test_file_for_zip.txt"); + } +} + +static void test_FileDescriptor_Folder(FILEGROUPDESCRIPTORW* Descriptor) +{ + ok_int(Descriptor->cItems, 2u); + if (Descriptor->cItems > 0) + { + FILETIME LocalFileTime, FileTime; + WORD Mask = g_bOldZipfldr ? 0xffe0 : (0xffff); // bits 0-4 are the seconds + DosDateTimeToFileTime(0x5024, (0xa5fc & Mask), &LocalFileTime); + LocalFileTimeToFileTime(&LocalFileTime, &FileTime); + + FILEDESCRIPTORW* FileDescriptor = Descriptor->fgd; + ok_hex(FileDescriptor->dwFlags, FD_ATTRIBUTES | FD_WRITESTIME | FD_FILESIZE | FD_PROGRESSUI); + ok(FileDescriptor->dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY || + FileDescriptor->dwFileAttributes == (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_VIRTUAL), "Got attr: 0x%lx\n", FileDescriptor->dwFileAttributes); + //ok_hex(FileDescriptor->dwFileAttributes, FILE_ATTRIBUTE_DIRECTORY); + ok_hex(FileDescriptor->ftLastWriteTime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(FileDescriptor->ftLastWriteTime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(FileDescriptor->nFileSizeHigh, 0); + ok_hex(FileDescriptor->nFileSizeLow, 0); + ok_wstr(FileDescriptor->cFileName, L"folder_1"); + + if (Descriptor->cItems > 1) + { + DosDateTimeToFileTime(0x5024, (0xa60d & Mask), &LocalFileTime); + LocalFileTimeToFileTime(&LocalFileTime, &FileTime); + + FileDescriptor = Descriptor->fgd + 1; + ok_hex(FileDescriptor->dwFlags, FD_ATTRIBUTES | FD_WRITESTIME | FD_FILESIZE | FD_PROGRESSUI); + ok_hex(FileDescriptor->dwFileAttributes, FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_VIRTUAL); + ok_hex(FileDescriptor->ftLastWriteTime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(FileDescriptor->ftLastWriteTime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(FileDescriptor->nFileSizeHigh, 0); + ok_hex(FileDescriptor->nFileSizeLow, strlen(test_file_2_contents)); + ok_wstr(FileDescriptor->cFileName, L"folder_1\\test_file_for_zip.txt"); + } + } +} + +static GUID GUID_NULL_; +static void test_FileContents1(IStream* Stream) +{ + STATSTG statstg = {0}; + HRESULT hr = Stream->Stat(&statstg, STATFLAG_DEFAULT); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + if (SUCCEEDED(hr)) + { + FILETIME LocalFileTime, FileTime; + WORD Mask = g_bOldZipfldr ? 0xffe0 : (0xffff); // bits 0-4 are the seconds + DosDateTimeToFileTime(0x5024, (0xa5f2 & Mask), &LocalFileTime); + LocalFileTimeToFileTime(&LocalFileTime, &FileTime); + + ok_wstr(statstg.pwcsName, L"test_file_for_zip.txt"); + ok_hex(statstg.type, STGTY_STREAM); + ok_int(statstg.cbSize.LowPart, strlen(test_file_1_contents)); + ok_hex(statstg.cbSize.HighPart, 0); + ok_hex(statstg.mtime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(statstg.mtime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(statstg.ctime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(statstg.ctime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(statstg.atime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(statstg.atime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(statstg.grfMode, STGM_SHARE_DENY_WRITE); + ok_hex(statstg.grfLocksSupported, 0); + ok(!memcmp(&statstg.clsid, &GUID_NULL_, sizeof(GUID_NULL_)), "Expected GUID_NULL, got %s\n", wine_dbgstr_guid(&statstg.clsid)); + ok_hex(statstg.grfStateBits, 0); + CoTaskMemFree(statstg.pwcsName); + } + + LARGE_INTEGER Offset = { {0} }; + ULARGE_INTEGER NewPosition = { {0} }; + hr = Stream->Seek(Offset, STREAM_SEEK_CUR, &NewPosition); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + ok_int(NewPosition.HighPart, 0); + ok_int(NewPosition.LowPart, 0); + + char buf[100] = { 0 }; + ULONG cbRead; + hr = Stream->Read(buf, sizeof(buf)-1, &cbRead); + ok_hex(hr, S_FALSE); + ok_int(cbRead, strlen(test_file_1_contents)); + ok_str(buf, test_file_1_contents); + + hr = Stream->Seek(Offset, STREAM_SEEK_CUR, &NewPosition); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + ok_int(NewPosition.HighPart, 0); + if (SUCCEEDED(hr)) + ok_int(NewPosition.LowPart, strlen(test_file_1_contents)); + + ULONG cbWritten; + hr = Stream->Write("DUMMY", 5, &cbWritten); + if (!g_bOldZipfldr) + { + ok_hex(hr, STG_E_ACCESSDENIED); + } + else + { + // Write succeeds, but is not reflected in the file on disk + ok_hex(hr, S_OK); + } + + // Can increase the size... + NewPosition.LowPart = statstg.cbSize.LowPart + 1; + hr = Stream->SetSize(NewPosition); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + + // But is not reflected in the Stat result + hr = Stream->Stat(&statstg, STATFLAG_DEFAULT); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + if (SUCCEEDED(hr)) + { + ok_int(statstg.cbSize.LowPart, strlen(test_file_1_contents)); + CoTaskMemFree(statstg.pwcsName); + } + + // Old zipfldr does not support seek, so we can not read it again + if (!g_bOldZipfldr) + { + Offset.QuadPart = 0; + hr = Stream->Seek(Offset, STREAM_SEEK_SET, &NewPosition); + ok_hex(hr, S_OK); + + memset(buf, 0, sizeof(buf)); + hr = Stream->Read(buf, sizeof(buf)-1, &cbRead); + ok_hex(hr, S_FALSE); + ok_int(cbRead, strlen(test_file_1_contents)); + } +} + + +static void test_FileContents2(IStream* Stream) +{ + STATSTG statstg = {0}; + HRESULT hr = Stream->Stat(&statstg, STATFLAG_DEFAULT); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + if (SUCCEEDED(hr)) + { + FILETIME LocalFileTime, FileTime; + WORD Mask = g_bOldZipfldr ? 0xffe0 : (0xffff); // bits 0-4 are the seconds + DosDateTimeToFileTime(0x5024, (0xa60d & Mask), &LocalFileTime); + LocalFileTimeToFileTime(&LocalFileTime, &FileTime); + + ok_wstr(statstg.pwcsName, L"test_file_for_zip.txt"); + ok_hex(statstg.type, STGTY_STREAM); + ok_int(statstg.cbSize.LowPart, strlen(test_file_2_contents)); + ok_hex(statstg.cbSize.HighPart, 0); + ok_hex(statstg.mtime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(statstg.mtime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(statstg.ctime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(statstg.ctime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(statstg.atime.dwHighDateTime, FileTime.dwHighDateTime); + ok_hex(statstg.atime.dwLowDateTime, FileTime.dwLowDateTime); + ok_hex(statstg.grfMode, STGM_SHARE_DENY_WRITE); + ok_hex(statstg.grfLocksSupported, 0); + ok(!memcmp(&statstg.clsid, &GUID_NULL_, sizeof(GUID_NULL_)), "Expected GUID_NULL, got %s\n", wine_dbgstr_guid(&statstg.clsid)); + ok_hex(statstg.grfStateBits, 0); + CoTaskMemFree(statstg.pwcsName); + } + + LARGE_INTEGER Offset = { {0} }; + ULARGE_INTEGER NewPosition = { {0} }; + hr = Stream->Seek(Offset, STREAM_SEEK_CUR, &NewPosition); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + ok_int(NewPosition.HighPart, 0); + ok_int(NewPosition.LowPart, 0); + + char buf[100] = { 0 }; + ULONG cbRead; + hr = Stream->Read(buf, sizeof(buf)-1, &cbRead); + ok_hex(hr, S_FALSE); + ok_int(cbRead, strlen(test_file_2_contents)); + ok_str(buf, test_file_2_contents); + + hr = Stream->Seek(Offset, STREAM_SEEK_CUR, &NewPosition); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + ok_int(NewPosition.HighPart, 0); + if (SUCCEEDED(hr)) + ok_int(NewPosition.LowPart, strlen(test_file_2_contents)); + + ULONG cbWritten; + hr = Stream->Write("DUMMY", 5, &cbWritten); + if (!g_bOldZipfldr) + { + ok_hex(hr, STG_E_ACCESSDENIED); + } + else + { + // Write succeeds, but is not reflected in the file on disk + ok_hex(hr, S_OK); + } + + // Can increase the size... + NewPosition.LowPart = statstg.cbSize.LowPart + 1; + hr = Stream->SetSize(NewPosition); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + + // But is not reflected in the Stat result + hr = Stream->Stat(&statstg, STATFLAG_DEFAULT); + ok_hex(hr, g_bOldZipfldr ? E_NOTIMPL : S_OK); + if (SUCCEEDED(hr)) + { + ok_int(statstg.cbSize.LowPart, strlen(test_file_2_contents)); + CoTaskMemFree(statstg.pwcsName); + } + + // Old zipfldr does not support seek, so we can not read it again + if (!g_bOldZipfldr) + { + Offset.QuadPart = 0; + hr = Stream->Seek(Offset, STREAM_SEEK_SET, &NewPosition); + ok_hex(hr, S_OK); + + memset(buf, 0, sizeof(buf)); + hr = Stream->Read(buf, sizeof(buf)-1, &cbRead); + ok_hex(hr, S_FALSE); + ok_int(cbRead, strlen(test_file_2_contents)); + ok_str(buf, test_file_2_contents); + } +} + + + +static CLIPFORMAT cfHIDA = RegisterClipboardFormatA(CFSTR_SHELLIDLISTA); +static CLIPFORMAT cfFileDescriptor = RegisterClipboardFormatW(CFSTR_FILEDESCRIPTORW); +static CLIPFORMAT cfFileContents = RegisterClipboardFormatW(CFSTR_FILECONTENTSW); + +static void test_DataObject_FirstFile(IShellFolder* pFolder) +{ + CComPtr spDataObj; + if (!GetFirstDataObject(pFolder, SHCONTF_NONFOLDERS, spDataObj)) + return; + + if (!IsFormatAdvertised(spDataObj, cfHIDA, TYMED_HGLOBAL)) + { + trace("Pre-Vista zipfldr\n"); + // No seconds in filetimes, less functional IStream* implementation + g_bOldZipfldr = true; + } + + ok(!IsFormatAdvertised(spDataObj, CF_HDROP, TYMED_NULL), "Expected CF_HDROP to be absent\n"); + ok(IsFormatAdvertised(spDataObj, cfFileDescriptor, TYMED_HGLOBAL), "Expected FileDescriptorW to be supported\n"); + ok(IsFormatAdvertised(spDataObj, cfFileContents, TYMED_ISTREAM), "Expected FileContents to be supported\n"); + + STGMEDIUM medium = {0}; + FORMATETC etc = { cfFileDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + HRESULT hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + ok_hex(medium.tymed, TYMED_HGLOBAL); + PVOID pData = GlobalLock(medium.hGlobal); + test_FileDescriptor(static_cast(pData)); + GlobalUnlock(medium.hGlobal); + ReleaseStgMedium(&medium); + + // Invalid index + etc.cfFormat = cfFileContents; + etc.ptd = NULL; + etc.dwAspect = DVASPECT_CONTENT; + etc.lindex = -1; + etc.tymed = TYMED_ISTREAM; + memset(&medium, 0xcc, sizeof(medium)); + hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, E_INVALIDARG); + ok_hex(medium.tymed, TYMED_NULL); + ok_ptr(medium.hGlobal, NULL); + ok_ptr(medium.pUnkForRelease, NULL); + if (SUCCEEDED(hr)) + ReleaseStgMedium(&medium); + + // Correct index + etc.cfFormat = cfFileContents; + etc.ptd = NULL; + etc.dwAspect = DVASPECT_CONTENT; + etc.lindex = 0; + etc.tymed = TYMED_ISTREAM; + memset(&medium, 0xcc, sizeof(medium)); + // During this call a temp file is created: %TMP%\Temp%u_%s\test_file_for_zip.txt + // Or for the 2k3 version: %TMP%\Temporary Directory %u for %s\test_file_for_zip.txt + hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, S_OK); + ok_hex(medium.tymed, TYMED_ISTREAM); + if (SUCCEEDED(hr)) + { + test_FileContents1(medium.pstm); + ReleaseStgMedium(&medium); + } + + //if (winetest_get_failures()) + // DumpDataObjectFormats(spDataObj); +} + +static void test_DataObject_FirstFolder(IShellFolder* pFolder) +{ + CComPtr spDataObj; + if (!GetFirstDataObject(pFolder, SHCONTF_FOLDERS, spDataObj)) + return; + + ok(!IsFormatAdvertised(spDataObj, CF_HDROP, TYMED_NULL), "Expected CF_HDROP to be absent\n"); + ok(IsFormatAdvertised(spDataObj, cfFileDescriptor, TYMED_HGLOBAL), "Expected FileDescriptorW to be supported\n"); + ok(IsFormatAdvertised(spDataObj, cfFileContents, TYMED_ISTREAM), "Expected FileContents to be supported\n"); + // 7+ + ok(!!IsFormatAdvertised(spDataObj, cfHIDA, TYMED_HGLOBAL) != g_bOldZipfldr, "Expected HIDA to be %s\n", g_bOldZipfldr ? "absent" : "supported"); + + STGMEDIUM medium = {0}; + FORMATETC etc = { cfFileDescriptor, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + HRESULT hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + ok_hex(medium.tymed, TYMED_HGLOBAL); + PVOID pData = GlobalLock(medium.hGlobal); + test_FileDescriptor_Folder(static_cast(pData)); + GlobalUnlock(medium.hGlobal); + ReleaseStgMedium(&medium); + + // Invalid index + etc.cfFormat = cfFileContents; + etc.ptd = NULL; + etc.dwAspect = DVASPECT_CONTENT; + etc.lindex = -1; + etc.tymed = TYMED_ISTREAM; + memset(&medium, 0xcc, sizeof(medium)); + hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, E_INVALIDARG); + ok_hex(medium.tymed, TYMED_NULL); + ok_ptr(medium.hGlobal, NULL); + ok_ptr(medium.pUnkForRelease, NULL); + if (SUCCEEDED(hr)) + ReleaseStgMedium(&medium); + + // Not a file (first index is the folder) + etc.cfFormat = cfFileContents; + etc.ptd = NULL; + etc.dwAspect = DVASPECT_CONTENT; + etc.lindex = 0; + etc.tymed = TYMED_ISTREAM; + memset(&medium, 0xcc, sizeof(medium)); + hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, E_FAIL); + ok_hex(medium.tymed, TYMED_NULL); + ok_ptr(medium.hGlobal, NULL); + ok_ptr(medium.pUnkForRelease, NULL); + if (SUCCEEDED(hr)) + ReleaseStgMedium(&medium); + + // The file (content of the folder) + etc.cfFormat = cfFileContents; + etc.ptd = NULL; + etc.dwAspect = DVASPECT_CONTENT; + etc.lindex = 1; + etc.tymed = TYMED_ISTREAM; + memset(&medium, 0xcc, sizeof(medium)); + // During this call a temp file is created: %TMP%\Temp%u_%s\folder1\test_file_for_zip.txt + // Or for the 2k3 version: %TMP%\Temporary Directory %u for %s\folder1\test_file_for_zip.txt + hr = spDataObj->GetData(&etc, &medium); + ok_hex(hr, S_OK); + ok_hex(medium.tymed, TYMED_ISTREAM); + if (SUCCEEDED(hr)) + { + test_FileContents2(medium.pstm); + ReleaseStgMedium(&medium); + } + + //if (winetest_get_failures()) + // DumpDataObjectFormats(spDataObj); +} + + +static void test_DataObject(const WCHAR* Filename) +{ + CComPtr spFolder; + if (!InitializeShellFolder(Filename, spFolder)) + return; + + test_DataObject_FirstFile(spFolder); + test_DataObject_FirstFolder(spFolder); +} + + +START_TEST(IDataObject) +{ + HRESULT hr = CoInitialize(NULL); + + ok_hr(hr, S_OK); + if (!SUCCEEDED(hr)) + return; + + WCHAR ZipTestFile[MAX_PATH]; + if (!extract_resource(ZipTestFile, MAKEINTRESOURCEW(IDR_ZIP_TEST_FILE))) + return; + test_DataObject(ZipTestFile); + DeleteFileW(ZipTestFile); +} diff --git a/modules/rostests/apitests/zipfldr/precomp.h b/modules/rostests/apitests/zipfldr/precomp.h new file mode 100644 index 00000000000..6af5f674709 --- /dev/null +++ b/modules/rostests/apitests/zipfldr/precomp.h @@ -0,0 +1,32 @@ +#ifndef _ZIPFLDR_APITEST_PRECOMP_H_ +#define _ZIPFLDR_APITEST_PRECOMP_H_ + +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + + +#include +#include +#include +#include +#include // gcc needs to resolve unused template content +#include +#include +#include + +#include + +#include + +#include "resource.h" + +BOOL extract_resource(WCHAR* Filename, LPCWSTR ResourceName); +#define InitializeShellFolder(Filename, pFolder) InitializeShellFolder_(__FILE__, __LINE__, Filename, pFolder) +bool InitializeShellFolder_(const char* file, int line, const WCHAR* Filename, CComPtr& spFolder); + +#define IsFormatAdvertised(pDataObj, cfFormat, tymed) IsFormatAdvertised_(__FILE__, __LINE__, pDataObj, cfFormat, tymed) +bool IsFormatAdvertised_(const char* file, int line, IDataObject* pDataObj, CLIPFORMAT cfFormat, TYMED tymed); + + +#endif /* _ZIPFLDR_APITEST_PRECOMP_H_ */ diff --git a/modules/rostests/apitests/zipfldr/resource.h b/modules/rostests/apitests/zipfldr/resource.h new file mode 100644 index 00000000000..78f26aa9ae2 --- /dev/null +++ b/modules/rostests/apitests/zipfldr/resource.h @@ -0,0 +1,3 @@ + +#define IDR_ZIP_TEST_FILE 1 +#define IDR_ZIP_TEST_ENUM 2 diff --git a/modules/rostests/apitests/zipfldr/testlist.c b/modules/rostests/apitests/zipfldr/testlist.c new file mode 100644 index 00000000000..ecd1bb9d700 --- /dev/null +++ b/modules/rostests/apitests/zipfldr/testlist.c @@ -0,0 +1,12 @@ +#define STANDALONE +#include + +extern void func_EnumObjects(void); +extern void func_IDataObject(void); + +const struct test winetest_testlist[] = +{ + { "EnumObjects", func_EnumObjects }, + { "IDataObject", func_IDataObject }, + { 0, 0 } +}; diff --git a/modules/rostests/apitests/zipfldr/zipfldr_apitest.rc b/modules/rostests/apitests/zipfldr/zipfldr_apitest.rc new file mode 100644 index 00000000000..6fd9ef852c0 --- /dev/null +++ b/modules/rostests/apitests/zipfldr/zipfldr_apitest.rc @@ -0,0 +1,8 @@ +#include +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +1 RCDATA "zipfldr_test_file.zip" +2 RCDATA "zipfldr_test_enum.zip" + diff --git a/modules/rostests/apitests/zipfldr/zipfldr_test_enum.zip b/modules/rostests/apitests/zipfldr/zipfldr_test_enum.zip new file mode 100644 index 0000000000000000000000000000000000000000..3e674ebf5922354507c8b864839f33c7aed99b51 GIT binary patch literal 1634 zcmWIWW@h1H0D(VCRRX{aD8UM(6M;ZK0InpQRUJb~G7umsF+o$p0W<}qM6aZx1Yr=m zT|6+m65|P2iKLQ%&HM;6lYju#0qD*|QjF*)0Jc0io(MNURTAe0Bx6w>j4i~V ziqS$0-D{v2Kv>SmB+q~=zEyxKK|laPL1LeaK>^HVWRPHx@^QRle)uXQIvwDRFcv+q zSQ(IwRR)>`b~duHlkeOAe|6+4BRY+2ET*qu#)6#Z1~nEGX2>SX3*5Q^G8vslHW@Qy zVJ3sZAqR-T0Srx<$fm=Bq3UUn`D>spFg~*B=)u8*5**VIrsE71uQS15(_x{5Y&v?V z@WV}40GfONVLB`UAUhuxD5VAv3;K4rfh{85g0~0@K+eY%`dW@5V1rxkV_JYee54pr za~iVqVc|i3__!cUr%m`2A}k;`eDJ5vc?b)@i4-|}V5t#b>O@aJ94P7M1kiMR0dp>R i8`lS5y8kkJ6_^h2W@Q7W2nHS?jALP7cn8$azyJW?`7@>f literal 0 HcmV?d00001 diff --git a/modules/rostests/apitests/zipfldr/zipfldr_test_file.zip b/modules/rostests/apitests/zipfldr/zipfldr_test_file.zip new file mode 100644 index 0000000000000000000000000000000000000000..cc9766718de281ed9cc3332e62abc6973576f4a4 GIT binary patch literal 600 zcmWIWW@Zs#U|`^2__S0dfaOQ#LTez;7KlZGxFof>Bt9)OCpA7TzbL*cvp}z;qJ;b0 zdEZbE|Fhn@r+mURJlARjo;VY%and7H7PnJ&8 zevR8`w{Qgik&r|yMq+7K7AGu68#i(TC~XMbI@0y3mKW@8E}P& z3eb8GPymxS!iNhg&B!3ZaE2lFg8AXAj4=Ay^`LjMKt9A8TtNY|MgV9{9uUI=2H9vS zAICc&qtR((A7BO_%xI7grXm}SH87FwyRXIj4rCvUesg8heGosuo0Sb{5Cbz18Zk03 J2mmny0{~LQs@VVl literal 0 HcmV?d00001 -- 2.17.1