[SHELL32][MKSHELLLINK] Support EXP_SPECIAL_FOLDER datablock in shortcuts (#7158)

CORE-19692
This commit is contained in:
Whindmar Saksit 2024-09-14 13:10:49 +02:00 committed by GitHub
parent 751641c2be
commit 7b081be46d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 114 additions and 19 deletions

View file

@ -746,6 +746,22 @@ HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm)
if (FAILED(hr)) // FIXME: Should we fail? if (FAILED(hr)) // FIXME: Should we fail?
return hr; return hr;
LPEXP_SPECIAL_FOLDER pSpecial = (LPEXP_SPECIAL_FOLDER)SHFindDataBlock(m_pDBList, EXP_SPECIAL_FOLDER_SIG);
if (pSpecial && pSpecial->cbSize == sizeof(*pSpecial) && ILGetSize(m_pPidl) > pSpecial->cbOffset)
{
if (LPITEMIDLIST folder = SHCloneSpecialIDList(NULL, pSpecial->idSpecialFolder, FALSE))
{
LPITEMIDLIST pidl = ILCombine(folder, (LPITEMIDLIST)((char*)m_pPidl + pSpecial->cbOffset));
if (pidl)
{
ILFree(m_pPidl);
m_pPidl = pidl;
TRACE("Replaced pidl base with CSIDL %u up to %ub.\n", pSpecial->idSpecialFolder, pSpecial->cbOffset);
}
ILFree(folder);
}
}
if (TRACE_ON(shell)) if (TRACE_ON(shell))
{ {
#if (NTDDI_VERSION < NTDDI_LONGHORN) #if (NTDDI_VERSION < NTDDI_LONGHORN)

View file

@ -14,8 +14,20 @@ WINE_DEFAULT_DEBUG_CHANNEL (shell);
static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder); static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder);
static LPCWSTR GetItemFileName(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
{
FileStructW* pDataW = _ILGetFileStructW(pidl);
if (pDataW)
return pDataW->wszName;
LPPIDLDATA pdata = _ILGetDataPointer(pidl);
if ((pdata->type & PT_VALUEW) == PT_VALUEW)
return (LPWSTR)pdata->u.file.szNames;
if (_ILSimpleGetTextW(pidl, Buf, cchMax))
return Buf;
return NULL;
}
HKEY OpenKeyFromFileType(LPWSTR pExtension, LPCWSTR KeyName) static HKEY OpenKeyFromFileType(LPCWSTR pExtension, LPCWSTR KeyName)
{ {
HKEY hkey; HKEY hkey;
@ -45,7 +57,7 @@ HKEY OpenKeyFromFileType(LPWSTR pExtension, LPCWSTR KeyName)
return hkey; return hkey;
} }
LPWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl) static LPCWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl, LPWSTR Buf, UINT cchMax)
{ {
if (!_ILIsValue(pidl)) if (!_ILIsValue(pidl))
{ {
@ -53,23 +65,17 @@ LPWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl)
return NULL; return NULL;
} }
FileStructW* pDataW = _ILGetFileStructW(pidl); LPCWSTR name = GetItemFileName(pidl, Buf, cchMax);
if (!pDataW) LPCWSTR pExtension = name ? PathFindExtensionW(name) : NULL;
{
ERR("Invalid pidl!\n");
return NULL;
}
LPWSTR pExtension = PathFindExtensionW(pDataW->wszName);
if (!pExtension || *pExtension == UNICODE_NULL) if (!pExtension || *pExtension == UNICODE_NULL)
{ {
WARN("No extension for %S!\n", pDataW->wszName); WARN("No extension for %S!\n", name);
return NULL; return NULL;
} }
return pExtension; return pExtension;
} }
HRESULT GetCLSIDForFileTypeFromExtension(LPWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid) static HRESULT GetCLSIDForFileTypeFromExtension(LPCWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid)
{ {
HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName); HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName);
if (!hkeyProgId) if (!hkeyProgId)
@ -126,7 +132,8 @@ HRESULT GetCLSIDForFileTypeFromExtension(LPWSTR pExtension, LPCWSTR KeyName, CLS
HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid) HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid)
{ {
LPWSTR pExtension = ExtensionFromPidl(pidl); WCHAR buf[256];
LPCWSTR pExtension = ExtensionFromPidl(pidl, buf, _countof(buf));
if (!pExtension) if (!pExtension)
return S_FALSE; return S_FALSE;
@ -289,7 +296,8 @@ HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, RE
} }
else else
{ {
LPWSTR pExtension = ExtensionFromPidl(pidl); WCHAR extbuf[256];
LPCWSTR pExtension = ExtensionFromPidl(pidl, extbuf, _countof(extbuf));
HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL; HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL;
if (!hkey) if (!hkey)
WARN("Could not open DefaultIcon key!\n"); WARN("Could not open DefaultIcon key!\n");

View file

@ -18,8 +18,14 @@ typedef unsigned __int16 uint16_t;
typedef unsigned __int32 uint32_t; typedef unsigned __int32 uint32_t;
#endif #endif
#ifdef _WIN32
#define strcasecmp _stricmp
#endif
#define SW_SHOWNORMAL 1 #define SW_SHOWNORMAL 1
#define SW_SHOWMINNOACTIVE 7 #define SW_SHOWMINNOACTIVE 7
#define CSIDL_WINDOWS 0x24
#define CSIDL_SYSTEM 0x25
typedef struct _GUID typedef struct _GUID
{ {
@ -125,17 +131,61 @@ typedef struct _ID_LIST_DRIVE
uint16_t unknown; uint16_t unknown;
} ID_LIST_DRIVE; } ID_LIST_DRIVE;
#define EXP_SPECIAL_FOLDER_SIG 0xA0000005
typedef struct _EXP_SPECIAL_FOLDER
{
uint32_t cbSize, dwSignature, idSpecialFolder, cbOffset;
} EXP_SPECIAL_FOLDER;
#pragma pack(pop) #pragma pack(pop)
static const struct SPECIALFOLDER {
unsigned char csidl;
const char* name;
} g_specialfolders[] = {
{ CSIDL_WINDOWS, "windows" },
{ CSIDL_SYSTEM, "system" },
{ 0, NULL}
};
static unsigned int is_path_separator(unsigned int c)
{
return c == '\\' || c == '/';
}
static const struct SPECIALFOLDER* get_special_folder(const char *target)
{
char buf[256];
strncpy(buf, target, sizeof(buf));
buf[sizeof("shell:") - 1] = '\0';
if (strcasecmp("shell:", buf))
return NULL;
target += sizeof("shell:") - 1;
for (unsigned long i = 0;; ++i)
{
unsigned long len;
const struct SPECIALFOLDER *special = &g_specialfolders[i];
if (!special->name)
return NULL;
len = strlen(special->name);
strncpy(buf, target, sizeof(buf));
buf[len] = '\0';
if (!strcasecmp(special->name, buf) && (is_path_separator(target[len]) || !target[len]))
return &g_specialfolders[i];
}
}
int main(int argc, const char *argv[]) int main(int argc, const char *argv[])
{ {
int i; int i;
const char *pszOutputPath = "shortcut.lnk"; const char *pszOutputPath = "shortcut.lnk";
const char *pszTarget = NULL; const char *pszTarget = NULL;
const char *pszDescription = "Description"; const char *pszDescription = NULL;
const char *pszWorkingDir = NULL; const char *pszWorkingDir = NULL;
const char *pszCmdLineArgs = NULL; const char *pszCmdLineArgs = NULL;
const char *pszIcon = NULL; const char *pszIcon = NULL;
char targetpath[260];
int IconNr = 0; int IconNr = 0;
GUID Guid = CLSID_MyComputer; GUID Guid = CLSID_MyComputer;
int bHelp = 0, bMinimized = 0; int bHelp = 0, bMinimized = 0;
@ -143,6 +193,7 @@ int main(int argc, const char *argv[])
LNK_HEADER Header; LNK_HEADER Header;
uint16_t uhTmp; uint16_t uhTmp;
uint32_t dwTmp; uint32_t dwTmp;
EXP_SPECIAL_FOLDER CsidlBlock, *pCsidlBlock = NULL;
for (i = 1; i < argc; ++i) for (i = 1; i < argc; ++i)
{ {
@ -226,14 +277,26 @@ int main(int argc, const char *argv[])
ID_LIST_DRIVE IdListDrive; ID_LIST_DRIVE IdListDrive;
unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName; unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName;
const char *pszName = pszTarget; const char *pszName = pszTarget;
int index = 1, specialindex = -1;
const struct SPECIALFOLDER *special = get_special_folder(pszTarget);
// ID list // ID list
// It seems explorer does not accept links without id list. List is relative to desktop. // It seems explorer does not accept links without id list. List is relative to desktop.
pszName = pszTarget; if (special)
if (pszName[0] && pszName[1] == ':')
{ {
Header.Flags &= ~LINK_RELATIVE_PATH;
CsidlBlock.cbSize = sizeof(CsidlBlock);
CsidlBlock.dwSignature = EXP_SPECIAL_FOLDER_SIG;
CsidlBlock.idSpecialFolder = special->csidl;
specialindex = 3; // Skip GUID, drive and fake windows/reactos folder
sprintf(targetpath, "x:\\reactos\\%s", pszTarget + sizeof("shell:") + strlen(special->name));
pszName = pszTarget = targetpath;
}
if (pszName[0] && pszName[0] != ':' && pszName[1] == ':')
{
++index;
cbListSize += sizeof(IdListDrive); cbListSize += sizeof(IdListDrive);
pszName += 2; pszName += 2;
while (*pszName == '\\' || *pszName == '/') while (*pszName == '\\' || *pszName == '/')
@ -249,6 +312,12 @@ int main(int argc, const char *argv[])
if (cchName != 1 || pszName[0] != '.') if (cchName != 1 || pszName[0] != '.')
cbListSize += sizeof(IdListFile) + 2 * (cchName + 1); cbListSize += sizeof(IdListFile) + 2 * (cchName + 1);
if (++index == specialindex)
{
CsidlBlock.cbOffset = cbListSize - sizeof(uint16_t);
pCsidlBlock = &CsidlBlock;
}
pszName += cchName; pszName += cchName;
while (*pszName == '\\' || *pszName == '/') while (*pszName == '\\' || *pszName == '/')
++pszName; ++pszName;
@ -309,7 +378,7 @@ int main(int argc, const char *argv[])
if (Header.Flags & LINK_DESCRIPTION) if (Header.Flags & LINK_DESCRIPTION)
{ {
// Dscription // Description
uhTmp = strlen(pszDescription); uhTmp = strlen(pszDescription);
fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile);
fputs(pszDescription, pFile); fputs(pszDescription, pFile);
@ -348,6 +417,8 @@ int main(int argc, const char *argv[])
} }
// Extra stuff // Extra stuff
if (pCsidlBlock)
fwrite(pCsidlBlock, sizeof(*pCsidlBlock), 1, pFile);
dwTmp = 0; dwTmp = 0;
fwrite(&dwTmp, sizeof(dwTmp), 1, pFile); fwrite(&dwTmp, sizeof(dwTmp), 1, pFile);