[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 <whindsaks@proton.me>
This commit is contained in:
Carl J. Bialorucki 2025-02-27 19:30:45 -07:00
parent 914a10714d
commit 83a108399b
4 changed files with 49 additions and 27 deletions

View file

@ -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;
}

View file

@ -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);

View file

@ -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<IShellFolder> pChildFolder;
hr = BindToObject(pidlTemp, pbc, IID_PPV_ARG(IShellFolder, &pChildFolder));

View file

@ -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
}
}
}