https://git.reactos.org/?p=reactos.git;a=commitdiff;h=aebaa14eba88056e15f46…
commit aebaa14eba88056e15f46adbb30767a3889f2d1d
Author: Whindmar Saksit <whindsaks(a)proton.me>
AuthorDate: Sun Jan 19 21:00:55 2025 +0100
Commit: GitHub <noreply(a)github.com>
CommitDate: Sun Jan 19 21:00:55 2025 +0100
[SHELL32] Allow DnD to create shortcuts across different drives (#7607)
CORE-17871 CORE-18271
---
dll/win32/shell32/droptargets/CFSDropTarget.cpp | 61 +++++++++++++++++--------
dll/win32/shell32/droptargets/CFSDropTarget.h | 1 +
dll/win32/shell32/folders/CDrivesFolder.cpp | 9 ++--
dll/win32/shell32/shldataobject.cpp | 5 +-
4 files changed, 49 insertions(+), 27 deletions(-)
diff --git a/dll/win32/shell32/droptargets/CFSDropTarget.cpp
b/dll/win32/shell32/droptargets/CFSDropTarget.cpp
index fce1a88d5b8..e7b5927500e 100644
--- a/dll/win32/shell32/droptargets/CFSDropTarget.cpp
+++ b/dll/win32/shell32/droptargets/CFSDropTarget.cpp
@@ -24,6 +24,11 @@
WINE_DEFAULT_DEBUG_CHANNEL (shell);
+#define D_NONE DROPEFFECT_NONE
+#define D_COPY DROPEFFECT_COPY
+#define D_MOVE DROPEFFECT_MOVE
+#define D_LINK DROPEFFECT_LINK
+
static void SHELL_StripIllegalFsNameCharacters(_Inout_ LPWSTR Buf)
{
for (LPWSTR src = Buf, dst = src;;)
@@ -38,6 +43,17 @@ static void SHELL_StripIllegalFsNameCharacters(_Inout_ LPWSTR Buf)
}
}
+static bool PathIsSameDrive(LPCWSTR Path1, LPCWSTR Path2)
+{
+ int d1 = PathGetDriveNumberW(Path1), d2 = PathGetDriveNumberW(Path2);
+ return d1 == d2 && d2 >= 0;
+}
+
+static bool PathIsDriveRoot(LPCWSTR Path)
+{
+ return PathIsRootW(Path) && PathGetDriveNumberW(Path) >= 0;
+}
+
static HRESULT
SHELL_LimitDropEffectToItemAttributes(_In_ IDataObject *pDataObject, _Inout_ PDWORD
pdwEffect)
{
@@ -134,7 +150,8 @@ CFSDropTarget::CFSDropTarget():
m_fAcceptFmt(FALSE),
m_sPathTarget(NULL),
m_hwndSite(NULL),
- m_grfKeyState(0)
+ m_grfKeyState(0),
+ m_AllowedEffects(0)
{
}
@@ -147,13 +164,7 @@ HRESULT CFSDropTarget::Initialize(LPWSTR PathTarget)
if (!m_cfShellIDList)
return E_FAIL;
- m_sPathTarget = (WCHAR *)SHAlloc((wcslen(PathTarget) + 1) * sizeof(WCHAR));
- if (!m_sPathTarget)
- return E_OUTOFMEMORY;
-
- wcscpy(m_sPathTarget, PathTarget);
-
- return S_OK;
+ return SHStrDupW(PathTarget, &m_sPathTarget);
}
CFSDropTarget::~CFSDropTarget()
@@ -202,7 +213,13 @@ BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
*pdwEffect = DROPEFFECT_NONE;
if (m_fAcceptFmt) { /* Does our interpretation of the keystate ... */
- *pdwEffect = KeyStateToDropEffect (dwKeyState);
+ *pdwEffect = KeyStateToDropEffect(dwKeyState);
+
+ // Transform disallowed move to a copy
+ if ((*pdwEffect & D_MOVE) && (m_AllowedEffects & (D_MOVE |
D_COPY)) == D_COPY)
+ *pdwEffect = D_COPY;
+
+ *pdwEffect &= m_AllowedEffects;
if (*pdwEffect == DROPEFFECT_NONE)
*pdwEffect = dwEffect;
@@ -320,6 +337,9 @@ HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject,
{
TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
+ const BOOL bAnyKeyMod = dwKeyState & (MK_SHIFT | MK_CONTROL);
+ m_AllowedEffects = *pdwEffect;
+
if (*pdwEffect == DROPEFFECT_NONE)
return S_OK;
@@ -337,11 +357,9 @@ HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject,
m_grfKeyState = dwKeyState;
-#define D_NONE DROPEFFECT_NONE
-#define D_COPY DROPEFFECT_COPY
-#define D_MOVE DROPEFFECT_MOVE
-#define D_LINK DROPEFFECT_LINK
- m_dwDefaultEffect = *pdwEffect;
+ SHELL_LimitDropEffectToItemAttributes(pDataObject, pdwEffect);
+ m_AllowedEffects = *pdwEffect;
+ m_dwDefaultEffect = m_AllowedEffects;
switch (*pdwEffect & (D_COPY | D_MOVE | D_LINK))
{
case D_COPY | D_MOVE:
@@ -378,19 +396,24 @@ HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject,
WCHAR wstrFirstFile[MAX_PATH];
if (DragQueryFileW((HDROP)medium.hGlobal, 0, wstrFirstFile,
_countof(wstrFirstFile)))
{
- /* Check if the drive letter is different */
- if (wstrFirstFile[0] != m_sPathTarget[0])
+ if (!PathIsSameDrive(wstrFirstFile, m_sPathTarget) &&
m_dwDefaultEffect != D_LINK)
{
m_dwDefaultEffect = DROPEFFECT_COPY;
}
+
+ if (!bAnyKeyMod && PathIsDriveRoot(wstrFirstFile) &&
(m_AllowedEffects & DROPEFFECT_LINK))
+ {
+ m_dwDefaultEffect = DROPEFFECT_LINK; // Don't copy a drive by
default
+ }
}
ReleaseStgMedium(&medium);
}
if (!m_fAcceptFmt)
- *pdwEffect = DROPEFFECT_NONE;
+ m_AllowedEffects = DROPEFFECT_NONE;
else
*pdwEffect = m_dwDefaultEffect;
+ *pdwEffect &= m_AllowedEffects;
return S_OK;
}
@@ -471,7 +494,7 @@ HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject,
data->pt = pt;
// Need to dereference as pdweffect gets freed.
data->pdwEffect = *pdwEffect;
- SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL);
+ SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, CTF_COINIT |
CTF_PROCESS_REF, NULL);
return S_OK;
}
}
@@ -726,7 +749,6 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter)
{
- CoInitialize(NULL);
_DoDropData *data = static_cast<_DoDropData*>(lpParameter);
CComPtr<IDataObject> pDataObject;
HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream,
IID_PPV_ARG(IDataObject, &pDataObject));
@@ -744,7 +766,6 @@ DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter)
data->This->Release();
//Release the parameter from the heap.
HeapFree(GetProcessHeap(), 0, data);
- CoUninitialize();
return 0;
}
diff --git a/dll/win32/shell32/droptargets/CFSDropTarget.h
b/dll/win32/shell32/droptargets/CFSDropTarget.h
index 706164d8f6d..bde59979c24 100644
--- a/dll/win32/shell32/droptargets/CFSDropTarget.h
+++ b/dll/win32/shell32/droptargets/CFSDropTarget.h
@@ -35,6 +35,7 @@ class CFSDropTarget :
HWND m_hwndSite;
DWORD m_grfKeyState;
DWORD m_dwDefaultEffect;
+ DWORD m_AllowedEffects;
CComPtr<IUnknown> m_site;
BOOL _QueryDrop (DWORD dwKeyState, LPDWORD pdwEffect);
diff --git a/dll/win32/shell32/folders/CDrivesFolder.cpp
b/dll/win32/shell32/folders/CDrivesFolder.cpp
index 040511f03e9..df2f591b8bc 100644
--- a/dll/win32/shell32/folders/CDrivesFolder.cpp
+++ b/dll/win32/shell32/folders/CDrivesFolder.cpp
@@ -557,7 +557,7 @@ static const DWORD dwControlPanelAttributes =
SFGAO_HASSUBFOLDER | SFGAO_FOLDER | SFGAO_CANLINK;
static const DWORD dwDriveAttributes =
SFGAO_HASSUBFOLDER | SFGAO_FILESYSTEM | SFGAO_FOLDER | SFGAO_FILESYSANCESTOR |
- SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK;
+ SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANRENAME | SFGAO_CANLINK |
SFGAO_CANCOPY;
CDrivesFolder::CDrivesFolder()
{
@@ -611,10 +611,9 @@ HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC
pbc, LPOLEST
pdwAttributes);
}
- if (lpszDisplayName[0] &&
- ((L'A' <= lpszDisplayName[0] && lpszDisplayName[0] <=
L'Z') ||
+ if (((L'A' <= lpszDisplayName[0] && lpszDisplayName[0] <=
L'Z') ||
(L'a' <= lpszDisplayName[0] && lpszDisplayName[0] <=
L'z')) &&
- lpszDisplayName[1] == L':' && lpszDisplayName[2] ==
L'\\')
+ lpszDisplayName[1] == L':' && (lpszDisplayName[2] ==
L'\\' || !lpszDisplayName[2]))
{
// "C:\..."
WCHAR szRoot[8];
@@ -630,7 +629,7 @@ HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC
pbc, LPOLEST
if (!pidlTemp)
return E_OUTOFMEMORY;
- if (lpszDisplayName[3])
+ if (lpszDisplayName[2] && lpszDisplayName[3])
{
CComPtr<IShellFolder> pChildFolder;
hr = BindToObject(pidlTemp, pbc, IID_PPV_ARG(IShellFolder,
&pChildFolder));
diff --git a/dll/win32/shell32/shldataobject.cpp b/dll/win32/shell32/shldataobject.cpp
index 728cca0dbf3..04126e930b9 100644
--- a/dll/win32/shell32/shldataobject.cpp
+++ b/dll/win32/shell32/shldataobject.cpp
@@ -77,8 +77,9 @@ HRESULT WINAPI SHGetAttributesFromDataObject(IDataObject* pDataObject,
DWORD dwA
data.dwAttributes = rgfInOut & dwQueryAttributes;
data.cItems = apidl.GetSize();
- hr = DataObject_SetData(pDataObject, g_DataObjectAttributes,
&data, sizeof(data));
- FAILED_UNEXPECTEDLY(hr);
+ HRESULT hr2;
+ hr2 = DataObject_SetData(pDataObject, g_DataObjectAttributes,
&data, sizeof(data));
+ FAILED_UNEXPECTEDLY(hr2); // Report cache failure but don't fail
the function
}
}
}