mirror of
https://github.com/reactos/reactos.git
synced 2025-04-25 08:00:24 +00:00
[SHELL32][SHELL32_APITEST] Add SHGetAttributesFromDataObject
This commit is contained in:
parent
1f44552d36
commit
e5ea6041c9
8 changed files with 503 additions and 14 deletions
|
@ -53,6 +53,7 @@ list(APPEND SOURCE
|
||||||
droptargets/CexeDropHandler.cpp
|
droptargets/CexeDropHandler.cpp
|
||||||
droptargets/CFSDropTarget.cpp
|
droptargets/CFSDropTarget.cpp
|
||||||
droptargets/CRecyclerDropTarget.cpp
|
droptargets/CRecyclerDropTarget.cpp
|
||||||
|
shldataobject.cpp
|
||||||
shlexec.cpp
|
shlexec.cpp
|
||||||
shlfileop.cpp
|
shlfileop.cpp
|
||||||
shlfolder.cpp
|
shlfolder.cpp
|
||||||
|
|
103
dll/win32/shell32/shldataobject.cpp
Normal file
103
dll/win32/shell32/shldataobject.cpp
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: shell32
|
||||||
|
* LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
|
||||||
|
* PURPOSE: SHGetAttributesFromDataObject implementation
|
||||||
|
* COPYRIGHT: Copyright 2021 Mark Jansen <mark.jansen@reactos.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "precomp.h"
|
||||||
|
|
||||||
|
WINE_DEFAULT_DEBUG_CHANNEL(shell);
|
||||||
|
|
||||||
|
|
||||||
|
static CLIPFORMAT g_DataObjectAttributes = 0;
|
||||||
|
static const DWORD dwDefaultAttributeMask = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE | SFGAO_CANRENAME |
|
||||||
|
SFGAO_CANDELETE | SFGAO_READONLY | SFGAO_STREAM | SFGAO_FOLDER;
|
||||||
|
|
||||||
|
struct DataObjectAttributes
|
||||||
|
{
|
||||||
|
DWORD dwMask;
|
||||||
|
DWORD dwAttributes;
|
||||||
|
UINT cItems;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(DataObjectAttributes) == 0xc, "Unexpected struct size!");
|
||||||
|
|
||||||
|
|
||||||
|
static
|
||||||
|
HRESULT _BindToObject(PCUIDLIST_ABSOLUTE pidl, CComPtr<IShellFolder>& spFolder)
|
||||||
|
{
|
||||||
|
CComPtr<IShellFolder> spDesktop;
|
||||||
|
HRESULT hr = SHGetDesktopFolder(&spDesktop);
|
||||||
|
if (FAILED(hr))
|
||||||
|
return hr;
|
||||||
|
|
||||||
|
return spDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &spFolder));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXTERN_C
|
||||||
|
HRESULT WINAPI SHGetAttributesFromDataObject(IDataObject* pDataObject, DWORD dwAttributeMask, DWORD* pdwAttributes, UINT* pcItems)
|
||||||
|
{
|
||||||
|
DWORD dwAttributes = 0;
|
||||||
|
DWORD cItems = 0;
|
||||||
|
HRESULT hr = S_OK;
|
||||||
|
|
||||||
|
TRACE("(%p, 0x%x, %p, %p)\n", pDataObject, dwAttributeMask, pdwAttributes, pcItems);
|
||||||
|
|
||||||
|
if (!g_DataObjectAttributes)
|
||||||
|
g_DataObjectAttributes = (CLIPFORMAT)RegisterClipboardFormatW(L"DataObjectAttributes");
|
||||||
|
|
||||||
|
if (pDataObject)
|
||||||
|
{
|
||||||
|
DataObjectAttributes data = {};
|
||||||
|
if (FAILED(DataObject_GetData(pDataObject, g_DataObjectAttributes, &data, sizeof(data))))
|
||||||
|
{
|
||||||
|
TRACE("No attributes yet, creating new\n");
|
||||||
|
memset(&data, 0, sizeof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD dwQueryAttributes = dwAttributeMask | dwDefaultAttributeMask;
|
||||||
|
|
||||||
|
if ((data.dwMask & dwQueryAttributes) != dwQueryAttributes)
|
||||||
|
{
|
||||||
|
CDataObjectHIDA hida(pDataObject);
|
||||||
|
CComPtr<IShellFolder> spFolder;
|
||||||
|
|
||||||
|
if (!FAILED_UNEXPECTEDLY(hr = hida.hr()) &&
|
||||||
|
!FAILED_UNEXPECTEDLY(hr = _BindToObject(HIDA_GetPIDLFolder(hida), spFolder)))
|
||||||
|
{
|
||||||
|
CSimpleArray<PCUIDLIST_RELATIVE> apidl;
|
||||||
|
for (UINT n = 0; n < hida->cidl; ++n)
|
||||||
|
{
|
||||||
|
apidl.Add(HIDA_GetPIDLItem(hida, n));
|
||||||
|
}
|
||||||
|
|
||||||
|
SFGAOF rgfInOut = dwQueryAttributes;
|
||||||
|
hr = spFolder->GetAttributesOf(apidl.GetSize(), apidl.GetData(), &rgfInOut);
|
||||||
|
if (!FAILED_UNEXPECTEDLY(hr))
|
||||||
|
{
|
||||||
|
data.dwMask = dwQueryAttributes;
|
||||||
|
// Only store what we asked for
|
||||||
|
data.dwAttributes = rgfInOut & dwQueryAttributes;
|
||||||
|
data.cItems = apidl.GetSize();
|
||||||
|
|
||||||
|
hr = DataObject_SetData(pDataObject, g_DataObjectAttributes, &data, sizeof(data));
|
||||||
|
FAILED_UNEXPECTEDLY(hr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only give the user what they asked for, not everything else we have!
|
||||||
|
dwAttributes = data.dwAttributes & dwAttributeMask;
|
||||||
|
cItems = data.cItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdwAttributes)
|
||||||
|
*pdwAttributes = dwAttributes;
|
||||||
|
|
||||||
|
if (pcItems)
|
||||||
|
*pcItems = cItems;
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
|
@ -1302,20 +1302,6 @@ DWORD WINAPI SHGetComputerDisplayNameW(DWORD param1, DWORD param2, DWORD param3,
|
||||||
return E_FAIL;
|
return E_FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Unimplemented
|
|
||||||
*/
|
|
||||||
EXTERN_C HRESULT
|
|
||||||
WINAPI
|
|
||||||
SHGetAttributesFromDataObject(IDataObject *pdo,
|
|
||||||
DWORD dwAttributeMask,
|
|
||||||
DWORD *pdwAttributes,
|
|
||||||
UINT *pcItems)
|
|
||||||
{
|
|
||||||
FIXME("SHGetAttributesFromDataObject() stub\n");
|
|
||||||
return E_NOTIMPL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Unimplemented
|
* Unimplemented
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -27,6 +27,7 @@ list(APPEND SOURCE
|
||||||
ShellExecuteW.cpp
|
ShellExecuteW.cpp
|
||||||
ShellHook.cpp
|
ShellHook.cpp
|
||||||
ShellState.cpp
|
ShellState.cpp
|
||||||
|
SHGetAttributesFromDataObject.cpp
|
||||||
SHLimitInputEdit.cpp
|
SHLimitInputEdit.cpp
|
||||||
menu.cpp
|
menu.cpp
|
||||||
shelltest.cpp)
|
shelltest.cpp)
|
||||||
|
|
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* PROJECT: ReactOS api tests
|
||||||
|
* LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
|
||||||
|
* PURPOSE: Test for SHGetAttributesFromDataObject
|
||||||
|
* COPYRIGHT: Copyright 2021 Mark Jansen <mark.jansen@reactos.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "shelltest.h"
|
||||||
|
#include <ndk/rtlfuncs.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <shellutils.h>
|
||||||
|
#include <shlwapi.h>
|
||||||
|
|
||||||
|
|
||||||
|
static CLIPFORMAT g_DataObjectAttributes = 0;
|
||||||
|
static const DWORD dwDefaultAttributeMask = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_STORAGE | SFGAO_CANRENAME | SFGAO_CANDELETE |
|
||||||
|
SFGAO_READONLY | SFGAO_STREAM | SFGAO_FOLDER;
|
||||||
|
static_assert(dwDefaultAttributeMask == 0x2044003B, "Unexpected default attribute mask");
|
||||||
|
|
||||||
|
|
||||||
|
struct TmpFile
|
||||||
|
{
|
||||||
|
WCHAR Buffer[MAX_PATH] = {};
|
||||||
|
|
||||||
|
void Create(LPCWSTR Folder)
|
||||||
|
{
|
||||||
|
GetTempFileNameW(Folder, L"SHG", 0, Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
~TmpFile()
|
||||||
|
{
|
||||||
|
if (Buffer[0])
|
||||||
|
{
|
||||||
|
SetFileAttributesW(Buffer, FILE_ATTRIBUTE_NORMAL);
|
||||||
|
DeleteFileW(Buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
CComPtr<IShellFolder> _BindToObject(PCUIDLIST_ABSOLUTE pidl)
|
||||||
|
{
|
||||||
|
CComPtr<IShellFolder> spDesktop, spResult;
|
||||||
|
HRESULT hr = SHGetDesktopFolder(&spDesktop);
|
||||||
|
if (FAILED_UNEXPECTEDLY(hr))
|
||||||
|
return spResult;
|
||||||
|
|
||||||
|
if (FAILED_UNEXPECTEDLY(spDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &spResult))))
|
||||||
|
{
|
||||||
|
spResult.Release();
|
||||||
|
}
|
||||||
|
return spResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void ok_attributes_(IDataObject* pDataObject, HRESULT expect_hr, DWORD expect_mask, DWORD expect_attr, UINT expect_items)
|
||||||
|
{
|
||||||
|
FORMATETC fmt = { g_DataObjectAttributes, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||||
|
STGMEDIUM medium = {};
|
||||||
|
|
||||||
|
HRESULT hr = pDataObject->GetData(&fmt, &medium);
|
||||||
|
winetest_ok(hr == expect_hr, "Unexpected result from GetData, got 0x%lx, expected 0x%lx\n", hr, expect_hr);
|
||||||
|
|
||||||
|
if (hr == expect_hr && expect_hr == S_OK)
|
||||||
|
{
|
||||||
|
LPVOID blob = GlobalLock(medium.hGlobal);
|
||||||
|
winetest_ok(blob != nullptr, "Failed to lock hGlobal\n");
|
||||||
|
if (blob)
|
||||||
|
{
|
||||||
|
SIZE_T size = GlobalSize(medium.hGlobal);
|
||||||
|
winetest_ok(size == 0xc, "Unexpected size, got %lu, expected 12\n", size);
|
||||||
|
if (size == 0xc)
|
||||||
|
{
|
||||||
|
PDWORD data = (PDWORD)blob;
|
||||||
|
winetest_ok(data[0] == expect_mask, "Unexpected mask, got 0x%lx, expected 0x%lx\n", data[0], expect_mask);
|
||||||
|
winetest_ok(data[1] == expect_attr, "Unexpected attr, got 0x%lx, expected 0x%lx\n", data[1], expect_attr);
|
||||||
|
winetest_ok(data[2] == expect_items, "Unexpected item count, got %lu, expected %u\n", data[2], expect_items);
|
||||||
|
}
|
||||||
|
GlobalUnlock(medium.hGlobal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
ReleaseStgMedium(&medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define ok_attributes (winetest_set_location(__FILE__, __LINE__), 0) ? (void)0 : ok_attributes_
|
||||||
|
#define ok_hr_ret(x, y) ok_hr(x, y); if (x != y) return
|
||||||
|
|
||||||
|
static void test_SpecialCases()
|
||||||
|
{
|
||||||
|
DWORD dwAttributeMask = 0, dwAttributes = 123;
|
||||||
|
UINT cItems = 123;
|
||||||
|
|
||||||
|
HRESULT hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_int(dwAttributes, 0);
|
||||||
|
ok_int(cItems, 0);
|
||||||
|
|
||||||
|
cItems = 123;
|
||||||
|
hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, nullptr, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_int(cItems, 0);
|
||||||
|
|
||||||
|
dwAttributes = 123;
|
||||||
|
hr = SHGetAttributesFromDataObject(nullptr, dwAttributeMask, &dwAttributes, nullptr);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_int(dwAttributes, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void test_AttributesRegistration()
|
||||||
|
{
|
||||||
|
WCHAR Buffer[MAX_PATH] = {};
|
||||||
|
|
||||||
|
GetModuleFileNameW(NULL, Buffer, _countof(Buffer));
|
||||||
|
CComHeapPtr<ITEMIDLIST_ABSOLUTE> spPath(ILCreateFromPathW(Buffer));
|
||||||
|
|
||||||
|
ok(spPath != nullptr, "Unable to create pidl from %S\n", Buffer);
|
||||||
|
if (spPath == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SFGAOF attributes = dwDefaultAttributeMask;
|
||||||
|
HRESULT hr;
|
||||||
|
{
|
||||||
|
CComPtr<IShellFolder> spFolder;
|
||||||
|
PCUITEMID_CHILD child;
|
||||||
|
hr = SHBindToParent(spPath, IID_PPV_ARG(IShellFolder, &spFolder), &child);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
hr = spFolder->GetAttributesOf(1, &child, &attributes);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
attributes &= dwDefaultAttributeMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
CComHeapPtr<ITEMIDLIST> parent(ILClone(spPath));
|
||||||
|
PCIDLIST_RELATIVE child = ILFindLastID(spPath);
|
||||||
|
ILRemoveLastID(parent);
|
||||||
|
|
||||||
|
CComPtr<IDataObject> spDataObject;
|
||||||
|
hr = CIDLData_CreateFromIDArray(parent, 1, &child, &spDataObject);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
/* Not registered yet */
|
||||||
|
ok_attributes(spDataObject, DV_E_FORMATETC, 0, 0, 0);
|
||||||
|
|
||||||
|
/* Ask for attributes, without specifying any */
|
||||||
|
DWORD dwAttributeMask = 0, dwAttributes = 0;
|
||||||
|
UINT cItems = 0;
|
||||||
|
hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
|
||||||
|
/* Now there are attributes registered */
|
||||||
|
ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes, 1);
|
||||||
|
|
||||||
|
// Now add an additional mask value (our exe should have a propsheet!)
|
||||||
|
dwAttributeMask = SFGAO_HASPROPSHEET;
|
||||||
|
dwAttributes = 0;
|
||||||
|
cItems = 0;
|
||||||
|
hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
|
||||||
|
// Observe that this is now also cached
|
||||||
|
ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask | SFGAO_HASPROPSHEET, attributes | SFGAO_HASPROPSHEET, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_MultipleFiles()
|
||||||
|
{
|
||||||
|
TmpFile TmpFile1, TmpFile2, TmpFile3;
|
||||||
|
|
||||||
|
CComHeapPtr<ITEMIDLIST> pidl_tmpfolder;
|
||||||
|
CComHeapPtr<ITEMIDLIST> pidl1, pidl2, pidl3;
|
||||||
|
|
||||||
|
ITEMIDLIST* items[3] = {};
|
||||||
|
SFGAOF attributes_first = dwDefaultAttributeMask;
|
||||||
|
SFGAOF attributes2 = dwDefaultAttributeMask;
|
||||||
|
SFGAOF attributes3 = dwDefaultAttributeMask;
|
||||||
|
SFGAOF attributes_last = dwDefaultAttributeMask;
|
||||||
|
|
||||||
|
HRESULT hr;
|
||||||
|
{
|
||||||
|
WCHAR TempFolder[MAX_PATH] = {};
|
||||||
|
GetTempPathW(_countof(TempFolder), TempFolder);
|
||||||
|
|
||||||
|
// Create temp files
|
||||||
|
TmpFile1.Create(TempFolder);
|
||||||
|
TmpFile2.Create(TempFolder);
|
||||||
|
TmpFile3.Create(TempFolder);
|
||||||
|
|
||||||
|
// Last file is read-only
|
||||||
|
SetFileAttributesW(TmpFile3.Buffer, FILE_ATTRIBUTE_READONLY);
|
||||||
|
|
||||||
|
hr = SHParseDisplayName(TempFolder, NULL, &pidl_tmpfolder, NULL, NULL);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
CComPtr<IShellFolder> spFolder = _BindToObject(pidl_tmpfolder);
|
||||||
|
ok(!!spFolder, "Unable to bind to tmp folder\n");
|
||||||
|
if (!spFolder)
|
||||||
|
return;
|
||||||
|
|
||||||
|
hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile1.Buffer), NULL, &pidl1, NULL);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile2.Buffer), NULL, &pidl2, NULL);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
hr = spFolder->ParseDisplayName(NULL, 0, PathFindFileNameW(TmpFile3.Buffer), NULL, &pidl3, NULL);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
items[0] = pidl1;
|
||||||
|
items[1] = pidl2;
|
||||||
|
items[2] = pidl3;
|
||||||
|
|
||||||
|
// Query file attributes
|
||||||
|
hr = spFolder->GetAttributesOf(1, items, &attributes_first);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
|
||||||
|
hr = spFolder->GetAttributesOf(2, items, &attributes2);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
|
||||||
|
hr = spFolder->GetAttributesOf(3, items, &attributes3);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
|
||||||
|
hr = spFolder->GetAttributesOf(1, items + 2, &attributes_last);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
|
||||||
|
// Ignore any non-default attributes
|
||||||
|
attributes_first &= dwDefaultAttributeMask;
|
||||||
|
attributes2 &= dwDefaultAttributeMask;
|
||||||
|
attributes3 &= dwDefaultAttributeMask;
|
||||||
|
attributes_last &= dwDefaultAttributeMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only 'single' files have the stream attribute set
|
||||||
|
ok(attributes_first & SFGAO_STREAM, "Expected SFGAO_STREAM on attributes_first (0x%lx)\n", attributes_first);
|
||||||
|
ok(!(attributes2 & SFGAO_STREAM), "Expected no SFGAO_STREAM on attributes2 (0x%lx)\n", attributes2);
|
||||||
|
ok(!(attributes3 & SFGAO_STREAM), "Expected no SFGAO_STREAM on attributes3 (0x%lx)\n", attributes3);
|
||||||
|
ok(attributes_last & SFGAO_STREAM, "Expected SFGAO_STREAM on attributes_last (0x%lx)\n", attributes_last);
|
||||||
|
|
||||||
|
// Only attributes common on all are returned, so only the last has the readonly bit set!
|
||||||
|
ok(!(attributes_first & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes_first (0x%lx)\n", attributes_first);
|
||||||
|
ok(!(attributes2 & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes2 (0x%lx)\n", attributes2);
|
||||||
|
ok(!(attributes3 & SFGAO_READONLY), "Expected no SFGAO_READONLY on attributes3 (0x%lx)\n", attributes3);
|
||||||
|
ok(attributes_last & SFGAO_READONLY, "Expected SFGAO_READONLY on attributes_last (0x%lx)\n", attributes_last);
|
||||||
|
|
||||||
|
// The actual tests
|
||||||
|
{
|
||||||
|
// Just the first file
|
||||||
|
CComPtr<IDataObject> spDataObject;
|
||||||
|
hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 1, items, &spDataObject);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
DWORD dwAttributeMask = 0, dwAttributes = 123;
|
||||||
|
UINT cItems = 123;
|
||||||
|
hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes_first, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// First 2 files
|
||||||
|
CComPtr<IDataObject> spDataObject;
|
||||||
|
hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 2, items, &spDataObject);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
DWORD dwAttributeMask = 0, dwAttributes = 123;
|
||||||
|
UINT cItems = 123;
|
||||||
|
hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// All 3 files
|
||||||
|
CComPtr<IDataObject> spDataObject;
|
||||||
|
hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 3, items, &spDataObject);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
DWORD dwAttributeMask = 0, dwAttributes = 123;
|
||||||
|
UINT cItems = 123;
|
||||||
|
hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Only the last file
|
||||||
|
CComPtr<IDataObject> spDataObject;
|
||||||
|
hr = CIDLData_CreateFromIDArray(pidl_tmpfolder, 1, items + 2, &spDataObject);
|
||||||
|
ok_hr_ret(hr, S_OK);
|
||||||
|
|
||||||
|
DWORD dwAttributeMask = 0, dwAttributes = 123;
|
||||||
|
UINT cItems = 123;
|
||||||
|
hr = SHGetAttributesFromDataObject(spDataObject, dwAttributeMask, &dwAttributes, &cItems);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
ok_attributes(spDataObject, S_OK, dwDefaultAttributeMask, attributes_last, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(SHGetAttributesFromDataObject)
|
||||||
|
{
|
||||||
|
HRESULT hr;
|
||||||
|
|
||||||
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
||||||
|
ok_hr(hr, S_OK);
|
||||||
|
if (!SUCCEEDED(hr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
g_DataObjectAttributes = (CLIPFORMAT)RegisterClipboardFormatW(L"DataObjectAttributes");
|
||||||
|
ok(g_DataObjectAttributes != 0, "Unable to register DataObjectAttributes\n");
|
||||||
|
|
||||||
|
test_SpecialCases();
|
||||||
|
test_AttributesRegistration();
|
||||||
|
test_MultipleFiles();
|
||||||
|
|
||||||
|
CoUninitialize();
|
||||||
|
}
|
|
@ -28,6 +28,7 @@ extern void func_ShellExecuteEx(void);
|
||||||
extern void func_ShellExecuteW(void);
|
extern void func_ShellExecuteW(void);
|
||||||
extern void func_ShellHook(void);
|
extern void func_ShellHook(void);
|
||||||
extern void func_ShellState(void);
|
extern void func_ShellState(void);
|
||||||
|
extern void func_SHGetAttributesFromDataObject(void);
|
||||||
extern void func_SHLimitInputEdit(void);
|
extern void func_SHLimitInputEdit(void);
|
||||||
extern void func_SHParseDisplayName(void);
|
extern void func_SHParseDisplayName(void);
|
||||||
|
|
||||||
|
@ -58,6 +59,7 @@ const struct test winetest_testlist[] =
|
||||||
{ "ShellExecuteW", func_ShellExecuteW },
|
{ "ShellExecuteW", func_ShellExecuteW },
|
||||||
{ "ShellHook", func_ShellHook },
|
{ "ShellHook", func_ShellHook },
|
||||||
{ "ShellState", func_ShellState },
|
{ "ShellState", func_ShellState },
|
||||||
|
{ "SHGetAttributesFromDataObject", func_SHGetAttributesFromDataObject },
|
||||||
{ "SHLimitInputEdit", func_SHLimitInputEdit },
|
{ "SHLimitInputEdit", func_SHLimitInputEdit },
|
||||||
{ "SHParseDisplayName", func_SHParseDisplayName },
|
{ "SHParseDisplayName", func_SHParseDisplayName },
|
||||||
{ 0, 0 }
|
{ 0, 0 }
|
||||||
|
|
|
@ -2476,6 +2476,18 @@ SHRunControlPanel(
|
||||||
_In_ LPCWSTR commandLine,
|
_In_ LPCWSTR commandLine,
|
||||||
_In_opt_ HWND parent);
|
_In_opt_ HWND parent);
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* SHGetAttributesFromDataObject
|
||||||
|
*/
|
||||||
|
|
||||||
|
HRESULT
|
||||||
|
WINAPI
|
||||||
|
SHGetAttributesFromDataObject(
|
||||||
|
_In_opt_ IDataObject* pdo,
|
||||||
|
DWORD dwAttributeMask,
|
||||||
|
_Out_opt_ DWORD* pdwAttributes,
|
||||||
|
_Out_opt_ UINT* pcItems);
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* SHOpenWithDialog
|
* SHOpenWithDialog
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -559,6 +559,71 @@ static inline PCUIDLIST_RELATIVE HIDA_GetPIDLItem(CIDA const* pida, SIZE_T i)
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
|
DECLSPEC_SELECTANY CLIPFORMAT g_cfHIDA = NULL;
|
||||||
|
|
||||||
|
// Allow to use the HIDA from an IDataObject without copying it
|
||||||
|
struct CDataObjectHIDA
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
STGMEDIUM m_medium;
|
||||||
|
CIDA* m_cida;
|
||||||
|
HRESULT m_hr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit CDataObjectHIDA(IDataObject* pDataObject)
|
||||||
|
: m_cida(nullptr)
|
||||||
|
{
|
||||||
|
m_medium.tymed = TYMED_NULL;
|
||||||
|
|
||||||
|
if (g_cfHIDA == NULL)
|
||||||
|
{
|
||||||
|
g_cfHIDA = (CLIPFORMAT)RegisterClipboardFormatW(CFSTR_SHELLIDLISTW);
|
||||||
|
}
|
||||||
|
FORMATETC fmt = { g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
|
||||||
|
|
||||||
|
m_hr = pDataObject->GetData(&fmt, &m_medium);
|
||||||
|
if (FAILED(m_hr))
|
||||||
|
{
|
||||||
|
m_medium.tymed = TYMED_NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_cida = (CIDA*)::GlobalLock(m_medium.hGlobal);
|
||||||
|
if (m_cida == nullptr)
|
||||||
|
{
|
||||||
|
m_hr = E_UNEXPECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~CDataObjectHIDA()
|
||||||
|
{
|
||||||
|
if (m_cida)
|
||||||
|
::GlobalUnlock(m_cida);
|
||||||
|
|
||||||
|
ReleaseStgMedium(&m_medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hr() const
|
||||||
|
{
|
||||||
|
return m_hr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator bool() const
|
||||||
|
{
|
||||||
|
return m_cida != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator const CIDA* () const
|
||||||
|
{
|
||||||
|
return m_cida;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CIDA* operator->() const
|
||||||
|
{
|
||||||
|
return m_cida;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
inline
|
inline
|
||||||
HRESULT DataObject_GetData(IDataObject* pDataObject, CLIPFORMAT clipformat, PVOID pBuffer, SIZE_T dwBufferSize)
|
HRESULT DataObject_GetData(IDataObject* pDataObject, CLIPFORMAT clipformat, PVOID pBuffer, SIZE_T dwBufferSize)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue