https://git.reactos.org/?p=reactos.git;a=commitdiff;h=7b081be46de966e3fe6c2aa47a5a26deb7541e8a
commit 7b081be46de966e3fe6c2aa47a5a26deb7541e8a Author: Whindmar Saksit <whinds...@proton.me> AuthorDate: Sat Sep 14 13:10:49 2024 +0200 Commit: GitHub <nore...@github.com> CommitDate: Sat Sep 14 13:10:49 2024 +0200 [SHELL32][MKSHELLLINK] Support EXP_SPECIAL_FOLDER datablock in shortcuts (#7158) CORE-19692 --- dll/win32/shell32/CShellLink.cpp | 16 +++++++ dll/win32/shell32/folders/CFSFolder.cpp | 36 +++++++++------ sdk/tools/mkshelllink/mkshelllink.c | 79 +++++++++++++++++++++++++++++++-- 3 files changed, 113 insertions(+), 18 deletions(-) diff --git a/dll/win32/shell32/CShellLink.cpp b/dll/win32/shell32/CShellLink.cpp index d438fa204ff..bf2b5be44d5 100644 --- a/dll/win32/shell32/CShellLink.cpp +++ b/dll/win32/shell32/CShellLink.cpp @@ -746,6 +746,22 @@ HRESULT STDMETHODCALLTYPE CShellLink::Load(IStream *stm) if (FAILED(hr)) // FIXME: Should we fail? 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 (NTDDI_VERSION < NTDDI_LONGHORN) diff --git a/dll/win32/shell32/folders/CFSFolder.cpp b/dll/win32/shell32/folders/CFSFolder.cpp index bde0d6dbb42..3c7bb8f2d03 100644 --- a/dll/win32/shell32/folders/CFSFolder.cpp +++ b/dll/win32/shell32/folders/CFSFolder.cpp @@ -14,8 +14,20 @@ WINE_DEFAULT_DEBUG_CHANNEL (shell); 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; @@ -45,7 +57,7 @@ HKEY OpenKeyFromFileType(LPWSTR pExtension, LPCWSTR KeyName) return hkey; } -LPWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl) +static LPCWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl, LPWSTR Buf, UINT cchMax) { if (!_ILIsValue(pidl)) { @@ -53,23 +65,17 @@ LPWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl) return NULL; } - FileStructW* pDataW = _ILGetFileStructW(pidl); - if (!pDataW) - { - ERR("Invalid pidl!\n"); - return NULL; - } - - LPWSTR pExtension = PathFindExtensionW(pDataW->wszName); + LPCWSTR name = GetItemFileName(pidl, Buf, cchMax); + LPCWSTR pExtension = name ? PathFindExtensionW(name) : NULL; if (!pExtension || *pExtension == UNICODE_NULL) { - WARN("No extension for %S!\n", pDataW->wszName); + WARN("No extension for %S!\n", name); return NULL; } return pExtension; } -HRESULT GetCLSIDForFileTypeFromExtension(LPWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid) +static HRESULT GetCLSIDForFileTypeFromExtension(LPCWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid) { HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName); if (!hkeyProgId) @@ -126,7 +132,8 @@ HRESULT GetCLSIDForFileTypeFromExtension(LPWSTR pExtension, LPCWSTR KeyName, CLS 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) return S_FALSE; @@ -289,7 +296,8 @@ HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, RE } else { - LPWSTR pExtension = ExtensionFromPidl(pidl); + WCHAR extbuf[256]; + LPCWSTR pExtension = ExtensionFromPidl(pidl, extbuf, _countof(extbuf)); HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL; if (!hkey) WARN("Could not open DefaultIcon key!\n"); diff --git a/sdk/tools/mkshelllink/mkshelllink.c b/sdk/tools/mkshelllink/mkshelllink.c index 2cb921e567b..c01ad361db8 100644 --- a/sdk/tools/mkshelllink/mkshelllink.c +++ b/sdk/tools/mkshelllink/mkshelllink.c @@ -18,8 +18,14 @@ typedef unsigned __int16 uint16_t; typedef unsigned __int32 uint32_t; #endif +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif + #define SW_SHOWNORMAL 1 #define SW_SHOWMINNOACTIVE 7 +#define CSIDL_WINDOWS 0x24 +#define CSIDL_SYSTEM 0x25 typedef struct _GUID { @@ -125,17 +131,61 @@ typedef struct _ID_LIST_DRIVE uint16_t unknown; } 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) +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 i; const char *pszOutputPath = "shortcut.lnk"; const char *pszTarget = NULL; - const char *pszDescription = "Description"; + const char *pszDescription = NULL; const char *pszWorkingDir = NULL; const char *pszCmdLineArgs = NULL; const char *pszIcon = NULL; + char targetpath[260]; int IconNr = 0; GUID Guid = CLSID_MyComputer; int bHelp = 0, bMinimized = 0; @@ -143,6 +193,7 @@ int main(int argc, const char *argv[]) LNK_HEADER Header; uint16_t uhTmp; uint32_t dwTmp; + EXP_SPECIAL_FOLDER CsidlBlock, *pCsidlBlock = NULL; for (i = 1; i < argc; ++i) { @@ -226,14 +277,26 @@ int main(int argc, const char *argv[]) ID_LIST_DRIVE IdListDrive; unsigned cbListSize = sizeof(IdListGuid) + sizeof(uint16_t), cchName; const char *pszName = pszTarget; + int index = 1, specialindex = -1; + const struct SPECIALFOLDER *special = get_special_folder(pszTarget); // ID list // It seems explorer does not accept links without id list. List is relative to desktop. - pszName = pszTarget; + if (special) + { + 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[1] == ':') + if (pszName[0] && pszName[0] != ':' && pszName[1] == ':') { + ++index; cbListSize += sizeof(IdListDrive); pszName += 2; while (*pszName == '\\' || *pszName == '/') @@ -249,6 +312,12 @@ int main(int argc, const char *argv[]) if (cchName != 1 || pszName[0] != '.') cbListSize += sizeof(IdListFile) + 2 * (cchName + 1); + if (++index == specialindex) + { + CsidlBlock.cbOffset = cbListSize - sizeof(uint16_t); + pCsidlBlock = &CsidlBlock; + } + pszName += cchName; while (*pszName == '\\' || *pszName == '/') ++pszName; @@ -309,7 +378,7 @@ int main(int argc, const char *argv[]) if (Header.Flags & LINK_DESCRIPTION) { - // Dscription + // Description uhTmp = strlen(pszDescription); fwrite(&uhTmp, sizeof(uhTmp), 1, pFile); fputs(pszDescription, pFile); @@ -348,6 +417,8 @@ int main(int argc, const char *argv[]) } // Extra stuff + if (pCsidlBlock) + fwrite(pCsidlBlock, sizeof(*pCsidlBlock), 1, pFile); dwTmp = 0; fwrite(&dwTmp, sizeof(dwTmp), 1, pFile);