[SHELL32] Use correct PIDL and name when dropping to create a shortcut (#6999)

Ask for the editing name instead of the parsing name to get a suitable shortcut name for PIDL items.
This commit is contained in:
Whindmar Saksit 2024-07-19 13:37:53 +02:00 committed by GitHub
parent 089788a52a
commit fa95a96e9b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 86 additions and 100 deletions

View file

@ -900,7 +900,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty)
if (m_pPidl)
m_Header.dwFlags |= SLDF_HAS_ID_LIST;
if (m_sPath)
if (m_sPath && *m_sPath && !(m_Header.dwFlags & SLDF_FORCE_NO_LINKINFO))
m_Header.dwFlags |= SLDF_HAS_LINK_INFO;
if (m_sDescription && *m_sDescription)
m_Header.dwFlags |= SLDF_HAS_NAME;
@ -936,7 +936,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::Save(IStream *stm, BOOL fClearDirty)
}
}
if (m_sPath)
if (m_Header.dwFlags & SLDF_HAS_LINK_INFO)
{
hr = Stream_WriteLocationInfo(stm, m_sPath, &volume);
if (FAILED(hr))

View file

@ -24,6 +24,35 @@
WINE_DEFAULT_DEBUG_CHANNEL (shell);
static void SHELL_StripIllegalFsNameCharacters(_Inout_ LPWSTR Buf)
{
for (LPWSTR src = Buf, dst = src;;)
{
*dst = *src;
if (!*dst)
break;
else if (wcschr(INVALID_FILETITLE_CHARACTERSW, *dst))
src = CharNextW(src);
else
++src, ++dst;
}
}
static HRESULT
SHELL_LimitDropEffectToItemAttributes(_In_ IDataObject *pDataObject, _Inout_ PDWORD pdwEffect)
{
DWORD att = *pdwEffect & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK); // DROPEFFECT maps perfectly to these SFGAO bits
HRESULT hr = SHGetAttributesFromDataObject(pDataObject, att, &att, NULL);
if (FAILED(hr))
return S_FALSE;
*pdwEffect &= ~(SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK) | att;
return hr;
}
static void GetDefaultCopyMoveEffect()
{
// FIXME: When the source is on a different volume than the target, change default from move to copy
}
/****************************************************************************
* CFSDropTarget::_CopyItems
@ -166,6 +195,7 @@ BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect)
{
/* TODO Windows does different drop effects if dragging across drives.
i.e., it will copy instead of move if the directories are on different disks. */
GetDefaultCopyMoveEffect();
DWORD dwEffect = m_dwDefaultEffect;
@ -193,18 +223,20 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
HMENU hpopupmenu = GetSubMenu(hmenu, 0);
SHELL_LimitDropEffectToItemAttributes(pDataObject, &dwAvailableEffects);
if ((dwAvailableEffects & DROPEFFECT_COPY) == 0)
DeleteMenu(hpopupmenu, IDM_COPYHERE, MF_BYCOMMAND);
else if ((dwAvailableEffects & DROPEFFECT_MOVE) == 0)
if ((dwAvailableEffects & DROPEFFECT_MOVE) == 0)
DeleteMenu(hpopupmenu, IDM_MOVEHERE, MF_BYCOMMAND);
else if ((dwAvailableEffects & DROPEFFECT_LINK) == 0)
if ((dwAvailableEffects & DROPEFFECT_LINK) == 0 && (dwAvailableEffects & (DROPEFFECT_COPY | DROPEFFECT_MOVE)))
DeleteMenu(hpopupmenu, IDM_LINKHERE, MF_BYCOMMAND);
if ((*pdwEffect & DROPEFFECT_COPY))
GetDefaultCopyMoveEffect();
if (*pdwEffect & dwAvailableEffects & DROPEFFECT_COPY)
SetMenuDefaultItem(hpopupmenu, IDM_COPYHERE, FALSE);
else if ((*pdwEffect & DROPEFFECT_MOVE))
else if (*pdwEffect & dwAvailableEffects & DROPEFFECT_MOVE)
SetMenuDefaultItem(hpopupmenu, IDM_MOVEHERE, FALSE);
else if ((*pdwEffect & DROPEFFECT_LINK))
else if (dwAvailableEffects & DROPEFFECT_LINK)
SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE);
/* FIXME: We need to support shell extensions here */
@ -223,6 +255,7 @@ HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, D
NULL,
NULL);
SetForegroundWindow(hwndDummy); // Required for aborting by pressing Esc when dragging from Explorer to desktop
UINT uCommand = TrackPopupMenu(hpopupmenu,
TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY,
pt.x, pt.y, 0, hwndDummy, NULL);
@ -548,125 +581,83 @@ HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
if (bLinking)
{
WCHAR wszPath[MAX_PATH];
WCHAR wszTarget[MAX_PATH];
WCHAR wszNewLnk[MAX_PATH];
TRACE("target path = %s\n", debugstr_w(m_sPathTarget));
/* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
for (UINT i = 0; i < lpcida->cidl; i++)
{
// Find out which file we're linking.
STRRET strFile;
hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlFull;
hr = SHILCombine(pidl, apidl[i], &pidlFull);
WCHAR targetName[MAX_PATH];
if (SUCCEEDED(hr))
hr = Shell_DisplayNameOf(psfFrom, apidl[i], SHGDN_FOREDITING | SHGDN_INFOLDER, targetName, _countof(targetName));
if (FAILED_UNEXPECTEDLY(hr))
break;
hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
if (FAILED_UNEXPECTEDLY(hr))
break;
TRACE("source path = %s\n", debugstr_w(wszPath));
WCHAR wszDisplayName[MAX_PATH];
LPWSTR pwszFileName = PathFindFileNameW(wszPath);
if (PathIsRootW(wszPath)) // Drive?
{
hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_NORMAL, &strFile);
if (FAILED_UNEXPECTEDLY(hr))
break;
hr = StrRetToBufW(&strFile, apidl[i], wszDisplayName, _countof(wszDisplayName));
if (FAILED_UNEXPECTEDLY(hr))
break;
// Delete a ':' in wszDisplayName.
LPWSTR pch0 = wcschr(wszDisplayName, L':');
if (pch0)
{
do
{
*pch0 = *(pch0 + 1);
++pch0;
} while (*pch0);
}
pwszFileName = wszDisplayName; // Use wszDisplayName
}
else if (wszPath[0] == L':' && wszPath[1] == L':') // ::{GUID}?
{
CLSID clsid;
hr = ::CLSIDFromString(&wszPath[2], &clsid);
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidl = ILCreateFromPathW(wszPath);
if (pidl)
{
SHFILEINFOW fi = { NULL };
SHGetFileInfoW((LPCWSTR)pidl, 0, &fi, sizeof(fi),
SHGFI_DISPLAYNAME | SHGFI_PIDL);
if (fi.szDisplayName[0])
{
lstrcpynW(wszDisplayName, fi.szDisplayName, _countof(wszDisplayName));
pwszFileName = wszDisplayName; // Use wszDisplayName
}
ILFree(pidl);
}
}
SHELL_ErrorBox(m_hwndSite, hr);
break;
}
SHELL_StripIllegalFsNameCharacters(targetName);
// Creating a buffer to hold the combined path.
WCHAR wszCombined[MAX_PATH];
PathCombineW(wszCombined, m_sPathTarget, pwszFileName);
WCHAR wszCombined[MAX_PATH + _countof(targetName)];
PathCombineW(wszCombined, m_sPathTarget, targetName);
// Check to see if the source is a link
SFGAOF att = SHGetAttributes(psfFrom, apidl[i], SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM);
BOOL fSourceIsLink = FALSE;
if (!wcsicmp(PathFindExtensionW(wszPath), L".lnk"))
if (!wcsicmp(PathFindExtensionW(targetName), L".lnk") && (att & (SFGAO_FOLDER | SFGAO_STREAM)) != SFGAO_FOLDER)
{
fSourceIsLink = TRUE;
PathRemoveExtensionW(wszCombined);
}
// Create a pathname to save the new link.
_GetUniqueFileName(wszCombined, L".lnk", wszTarget, TRUE);
_GetUniqueFileName(wszCombined, L".lnk", wszNewLnk, TRUE);
CComPtr<IPersistFile> ppf;
if (fSourceIsLink)
{
hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf));
if (FAILED_UNEXPECTEDLY(hr))
break;
PWSTR pwszTargetFull;
hr = SHGetNameFromIDList(pidlFull, SIGDN_DESKTOPABSOLUTEPARSING, &pwszTargetFull);
if (!FAILED_UNEXPECTEDLY(hr))
{
hr = IShellLink_ConstructFromPath(pwszTargetFull, IID_PPV_ARG(IPersistFile, &ppf));
FAILED_UNEXPECTEDLY(hr);
SHFree(pwszTargetFull);
}
}
else
{
CComPtr<IShellLinkW> pLink;
hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
if (FAILED_UNEXPECTEDLY(hr))
break;
if (!FAILED_UNEXPECTEDLY(hr))
{
hr = pLink->SetIDList(pidlFull);
if (!FAILED_UNEXPECTEDLY(hr))
hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
WCHAR szDirPath[MAX_PATH], *pwszFile;
GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
if (pwszFile)
pwszFile[0] = 0;
hr = pLink->SetPath(wszPath);
if (FAILED_UNEXPECTEDLY(hr))
break;
hr = pLink->SetWorkingDirectory(szDirPath);
if (FAILED_UNEXPECTEDLY(hr))
break;
hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (FAILED_UNEXPECTEDLY(hr))
break;
PWSTR pwszPath, pSep;
if ((att & SFGAO_FILESYSTEM) && SUCCEEDED(SHGetNameFromIDList(pidlFull, SIGDN_FILESYSPATH, &pwszPath)))
{
if ((pSep = PathFindFileNameW(pwszPath)) > pwszPath)
{
pSep[-1] = UNICODE_NULL;
pLink->SetWorkingDirectory(pwszPath);
}
SHFree(pwszPath);
}
}
}
hr = ppf->Save(wszTarget, !fSourceIsLink);
if (SUCCEEDED(hr))
hr = ppf->Save(wszNewLnk, !fSourceIsLink);
if (FAILED_UNEXPECTEDLY(hr))
{
SHELL_ErrorBox(m_hwndSite, hr);
break;
SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
}
SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszNewLnk, NULL);
}
}
else

View file

@ -28,12 +28,7 @@ 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));
return SHBindToObject(NULL, pidl, IID_PPV_ARG(IShellFolder, &spFolder));
}
EXTERN_C