From 83a108399ba5522a7e9dcf22a04cd93dfac5d6cd Mon Sep 17 00:00:00 2001 From: "Carl J. Bialorucki" Date: Thu, 27 Feb 2025 19:30:45 -0700 Subject: [PATCH] [0.4.15][SHELL32] Allow DnD to create shortcuts across different drives (#7607) Fixes CORE-17871 and CORE-18271. This is a cherry pick of commit: aebaa14 [SHELL32] Allow DnD to create shortcuts across different drives (#7607) Co-Authored-By: Whindmar Saksit --- .../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 00c056acd8e..385a563863a 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 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 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 56422f83946..b6dcc77fb13 100644 --- a/dll/win32/shell32/folders/CDrivesFolder.cpp +++ b/dll/win32/shell32/folders/CDrivesFolder.cpp @@ -657,7 +657,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() { @@ -711,10 +711,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]; @@ -730,7 +729,7 @@ HRESULT WINAPI CDrivesFolder::ParseDisplayName(HWND hwndOwner, LPBC pbc, LPOLEST if (!pidlTemp) return E_OUTOFMEMORY; - if (lpszDisplayName[3]) + if (lpszDisplayName[2] && lpszDisplayName[3]) { CComPtr 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 fb50c78e7ac..672f199d1ae 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 } } }