windows-nt/Source/XPSP1/NT/shell/shell32/shapi.cpp
2020-09-26 16:20:57 +08:00

2544 lines
78 KiB
C++

#include "shellprv.h"
#include "util.h"
#include "ids.h"
#include "ole2dup.h"
#include "datautil.h"
#include "filetbl.h"
#include "copy.h"
#include "prop.h"
#include <pif.h>
#include "fstreex.h" // GetIconOverlayManager()
#include <runtask.h>
extern void PathStripTrailingDots(LPTSTR szPath);
HRESULT IExtractIcon_Extract(IExtractIcon *pei, LPCTSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize)
{
// wrapper to let us ask an IExtractIcon for only one icon (the large one)
// since many implementations will fault if you pass NULL phiconSmall
HICON hiconDummy;
if (phiconSmall == NULL)
{
phiconSmall = &hiconDummy;
nIconSize = MAKELONG(nIconSize, nIconSize);
}
HRESULT hr = pei->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
if (hr == S_OK && phiconSmall == &hiconDummy)
{
DestroyIcon(hiconDummy);
}
return hr;
}
HRESULT IExtractIconA_Extract(IExtractIconA *peia, LPCSTR pszFile, UINT nIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize)
{
// wrapper to let us ask an IExtractIcon for only one icon (the large one)
// since many dudes don't check for NULL phiconSmall
HICON hiconDummy;
if (phiconSmall == NULL)
{
phiconSmall = &hiconDummy;
nIconSize = MAKELONG(nIconSize, nIconSize);
}
HRESULT hr = peia->Extract(pszFile, nIconIndex, phiconLarge, phiconSmall, nIconSize);
if (hr == S_OK && phiconSmall == &hiconDummy)
{
DestroyIcon(hiconDummy);
}
return hr;
}
// try to figure out if this is an icon already
// in our system image list, so that we dont re-add
BOOL _HijackOfficeIcons(HICON hLarge, int iIndex)
{
BOOL fRet = FALSE;
HIMAGELIST himl;
ASSERT(hLarge);
if (Shell_GetImageLists(NULL, &himl))
{
HICON hMaybe = ImageList_GetIcon(himl, iIndex, 0);
if (hMaybe)
{
fRet = SHAreIconsEqual(hLarge, hMaybe);
DestroyIcon(hMaybe);
}
}
#ifdef DEBUG
if (!fRet)
TraceMsg(TF_WARNING, "_HijackOfficeIcons() called in suspicious circumstance");
#endif
return fRet;
}
HRESULT _GetILIndexGivenPXIcon(IExtractIcon *pxicon, UINT uFlags, LPCITEMIDLIST pidl, int *piImage, BOOL fAnsiCrossOver)
{
TCHAR szIconFile[MAX_PATH];
CHAR szIconFileA[MAX_PATH];
IExtractIconA *pxiconA = (IExtractIconA *)pxicon;
int iIndex;
int iImage = -1;
UINT wFlags=0;
HRESULT hr;
if (fAnsiCrossOver)
{
szIconFileA[0] = 0;
hr = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL,
szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags);
SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile));
}
else
{
szIconFile[0] = '\0';
hr = pxicon->GetIconLocation(uFlags | GIL_FORSHELL,
szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);
}
//
// "*" as the file name means iIndex is already a system
// icon index, we are done.
//
// this is a hack for our own internal icon handler
//
if (SUCCEEDED(hr) && (wFlags & GIL_NOTFILENAME) &&
szIconFile[0] == TEXT('*') && szIconFile[1] == 0)
{
*piImage = iIndex;
return hr;
}
// Do not replace this with SUCCEEDED(hr). hr = S_FALSE means we need to use a default icon.
if (hr == S_OK)
{
// If we have it in shell32, don't delay the extraction
if (!(wFlags & GIL_NOTFILENAME) && lstrcmpi(PathFindFileName(szIconFile), c_szShell32Dll) == 0)
{
iImage = Shell_GetCachedImageIndex(szIconFile, iIndex, wFlags);
}
else
{
//
// if GIL_DONTCACHE was returned by the icon handler, dont
// lookup the previous icon, assume a cache miss.
//
if (!(wFlags & GIL_DONTCACHE) && *szIconFile)
{
iImage = LookupIconIndex(szIconFile, iIndex, wFlags);
}
}
}
// if we miss our cache...
if (iImage == -1 && hr != S_FALSE)
{
if (uFlags & GIL_ASYNC)
{
// If we couldn't get the final icon, try to get a good temporary one
if (fAnsiCrossOver)
{
szIconFileA[0] = 0;
hr = pxiconA->GetIconLocation(uFlags | GIL_FORSHELL | GIL_DEFAULTICON,
szIconFileA, ARRAYSIZE(szIconFileA), &iIndex, &wFlags);
SHAnsiToUnicode(szIconFileA, szIconFile, ARRAYSIZE(szIconFile));
}
else
{
hr = pxicon->GetIconLocation(uFlags | GIL_FORSHELL | GIL_DEFAULTICON,
szIconFile, ARRAYSIZE(szIconFile), &iIndex, &wFlags);
}
if (hr == S_OK)
{
iImage = LookupIconIndex(szIconFile, iIndex, wFlags);
}
// When all else fails...
if (iImage == -1)
{
iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
}
// force a lookup incase we are not in explorer.exe
*piImage = iImage;
return E_PENDING;
}
// try getting it from the ExtractIcon member fuction
HICON rghicon[ARRAYSIZE(g_rgshil)] = {0};
BOOL fHandlerOk = FALSE;
for (int i = 0; i < ARRAYSIZE(g_rgshil); i += 2)
{
// Ask for two at a time because
//
// (a) it's slightly more efficient, and
//
// (b) otherwise we break compatibility with IExtractIcon::Extract
// implementions which ignore the size parameter (the Network
// Connections folder is one). The SHIL_'s are conveniently
// arranged in large/small alternating order for this purpose.
//
HICON *phiconSmall = NULL;
HICON *phiconLarge = &rghicon[i];
UINT nIconSize = g_rgshil[i].size.cx;
if (i + 1 < ARRAYSIZE(g_rgshil))
{
phiconSmall = &rghicon[i+1];
nIconSize = MAKELONG(nIconSize, g_rgshil[i+1].size.cx);
}
if (fAnsiCrossOver)
{
hr = IExtractIconA_Extract(pxiconA, szIconFileA, iIndex,
phiconLarge, phiconSmall, nIconSize);
}
else
{
hr = IExtractIcon_Extract(pxicon, szIconFile, iIndex,
phiconLarge, phiconSmall, nIconSize);
}
// S_FALSE means, can you please do it...Thanks
if (hr == S_FALSE && !(wFlags & GIL_NOTFILENAME))
{
hr = SHDefExtractIcon(szIconFile, iIndex, wFlags,
phiconLarge, phiconSmall, nIconSize);
}
if (SUCCEEDED(hr))
{
fHandlerOk = TRUE;
}
}
// our belief knows no bounds
if (!*szIconFile && rghicon[1] && iIndex > 0 && _HijackOfficeIcons(rghicon[1], iIndex))
{
// it lives!
iImage = iIndex;
}
else
{
// if we extracted a icon add it to the cache.
iImage = SHAddIconsToCache(rghicon, szIconFile, iIndex, wFlags);
}
_DestroyIcons(rghicon, ARRAYSIZE(rghicon));
// if we failed in any way pick a default icon
if (iImage == -1)
{
if (wFlags & GIL_SIMULATEDOC)
{
iImage = II_DOCUMENT;
}
else if ((wFlags & GIL_PERINSTANCE) && PathIsExe(szIconFile))
{
iImage = II_APPLICATION;
}
else
{
iImage = II_DOCNOASSOC;
}
// force a lookup incase we are not in explorer.exe
iImage = Shell_GetCachedImageIndex(c_szShell32Dll, iImage, 0);
// if the handler failed dont cache this default icon.
// so we will try again later and maybe get the right icon.
// handlers should only fail if they cant access the file
// or something equally bad.
//
// if the handler succeeded then go ahead and assume this is
// a usable icon, we must be in some low memory situation, or
// something. So keep mapping to the same shell icon.
//
if (fHandlerOk)
{
if (iImage != -1 && *szIconFile && !(wFlags & (GIL_DONTCACHE | GIL_NOTFILENAME)))
{
AddToIconTable(szIconFile, iIndex, wFlags, iImage);
}
}
else
{
TraceMsg(TF_DEFVIEW, "not caching icon for '%s' because cant access file", szIconFile);
}
}
}
if (iImage < 0)
{
iImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
}
*piImage = iImage;
return hr;
}
// given an IShellFolder and and an Idlist that is
// contained in it, get back the index into the system image list.
STDAPI SHGetIconFromPIDL(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, int *piImage)
{
HRESULT hr;
if (psi)
{
#ifdef DEBUG
*piImage = -1;
#endif
hr = psi->GetIconOf(pidl, flags, piImage);
if (hr == S_OK)
{
ASSERT(*piImage != -1);
return hr;
}
if (hr == E_PENDING)
{
ASSERT(flags & GIL_ASYNC);
ASSERT(*piImage != -1);
return hr;
}
}
*piImage = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0);
// Be careful. Some shellfolders erroneously return S_OK when they fail
IExtractIcon *pxi = NULL;
hr = psf->GetUIObjectOf(NULL, pidl ? 1 : 0, pidl ? &pidl : NULL, IID_PPV_ARG_NULL(IExtractIcon, &pxi));
if (SUCCEEDED(hr) && pxi)
{
hr = _GetILIndexGivenPXIcon(pxi, flags, pidl, piImage, FALSE);
pxi->Release();
}
else
{
// Try the ANSI interface, see if we are dealing with an old set of code
IExtractIconA *pxiA = NULL;
hr = psf->GetUIObjectOf(NULL, pidl ? 1 : 0, pidl ? &pidl : NULL, IID_PPV_ARG_NULL(IExtractIconA, &pxiA));
if (SUCCEEDED(hr))
{
if (pxiA)
{
hr = _GetILIndexGivenPXIcon((IExtractIcon *)pxiA, flags, pidl, piImage, TRUE);
pxiA->Release();
}
else
{
// IShellFolder lied to us - returned S_OK even though it failed
hr = E_FAIL;
}
}
}
return hr;
}
// given an IShellFolder and and an Idlist that is
// contained in it, get back the index into the system image list.
STDAPI_(int) SHMapPIDLToSystemImageListIndex(IShellFolder *psf, LPCITEMIDLIST pidl, int *piIndexSel)
{
int iIndex;
if (piIndexSel)
{
SHGetIconFromPIDL(psf, NULL, pidl, GIL_OPENICON, piIndexSel);
}
SHGetIconFromPIDL(psf, NULL, pidl, 0, &iIndex);
return iIndex;
}
class CGetIconTask : public CRunnableTask
{
public:
STDMETHODIMP RunInitRT(void);
CGetIconTask(HRESULT *phr, IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon,
PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint);
protected:
~CGetIconTask();
IShellFolder *_psf;
IShellIcon *_psi;
LPITEMIDLIST _pidl;
UINT _flags;
BOOL _fGetOpenIcon;
PFNASYNCICONTASKBALLBACK _pfn;
void *_pvData;
void *_pvHint;
};
CGetIconTask::CGetIconTask(HRESULT *phr, IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon,
PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint) :
CRunnableTask(RTF_DEFAULT), _psf(psf), _psi(psi), _flags(flags), _fGetOpenIcon(fGetOpenIcon), _pfn(pfn), _pvData(pvData), _pvHint(pvHint)
{
*phr = SHILClone(pidl, &_pidl);
_psf->AddRef();
if (_psi)
_psi->AddRef();
}
CGetIconTask::~CGetIconTask()
{
ILFree(_pidl);
_psf->Release();
if (_psi)
_psi->Release();
}
STDMETHODIMP CGetIconTask::RunInitRT()
{
int iIcon = -1;
int iOpenIcon = -1;
ASSERT(_pidl);
if (_fGetOpenIcon)
{
SHGetIconFromPIDL(_psf, _psi, _pidl, _flags | GIL_OPENICON, &iOpenIcon);
}
// get the icon for this item.
SHGetIconFromPIDL(_psf, _psi, _pidl, _flags, &iIcon);
_pfn(_pidl, _pvData, _pvHint, iIcon, iOpenIcon);
return S_OK;
}
HRESULT CGetIconTask_CreateInstance(IShellFolder *psf, IShellIcon *psi, LPCITEMIDLIST pidl, UINT flags, BOOL fGetOpenIcon,
PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint, IRunnableTask **ppTask)
{
*ppTask = NULL;
HRESULT hr;
CGetIconTask * pNewTask = new CGetIconTask(&hr, psf, psi, pidl, flags, fGetOpenIcon, pfn, pvData, pvHint);
if (pNewTask)
{
if (SUCCEEDED(hr))
*ppTask = SAFECAST(pNewTask, IRunnableTask *);
else
pNewTask->Release();
}
else
hr = E_OUTOFMEMORY;
return hr;
}
// given an IShellFolder and and an Idlist that is
// contained in it, get back a -possibly temporary - index into the system image list,
// and get the final icon from the callback if necessary
STDAPI SHMapIDListToImageListIndexAsync(IShellTaskScheduler* pts, IShellFolder *psf, LPCITEMIDLIST pidl, UINT flags,
PFNASYNCICONTASKBALLBACK pfn, void *pvData, void *pvHint, int *piIndex, int *piIndexSel)
{
HRESULT hr = S_OK;
IShellIcon *psi = NULL;
psf->QueryInterface(IID_PPV_ARG(IShellIcon, &psi));
// We are doing all the ASYNC handling, not the caller.
flags &= ~GIL_ASYNC;
// Try asynchronous first
if (pfn)
{
hr = SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_ASYNC, piIndex);
if (piIndexSel)
{
HRESULT hr2 = SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_OPENICON | GIL_ASYNC, piIndexSel);
if (SUCCEEDED(hr))
{
// Don't lose the result if the first GetIcon succeeds, but the second one is E_PENDING
hr = hr2;
}
}
if (hr == E_PENDING)
{
if (pts)
{
IRunnableTask *pTask;
hr = CGetIconTask_CreateInstance(psf, psi, pidl, flags, (piIndexSel != NULL), pfn, pvData, pvHint, &pTask);
if (SUCCEEDED(hr))
{
hr = pts->AddTask(pTask, TOID_DVIconExtract, 0, ITSAT_DEFAULT_PRIORITY);
if (SUCCEEDED(hr))
{
hr = E_PENDING;
}
pTask->Release();
}
}
else
{
hr = E_POINTER;
}
}
else if (hr == S_OK)
{
goto cleanup;
}
}
// If asynchronous get failed, try synchronous
if (hr != E_PENDING)
{
if (piIndexSel)
{
SHGetIconFromPIDL(psf, psi, pidl, flags | GIL_OPENICON, piIndexSel);
}
hr = SHGetIconFromPIDL(psf, psi, pidl, flags, piIndex);
}
cleanup:
if (psi)
{
psi->Release();
}
return hr;
}
// returns the icon handle to be used to represent the specified
// file. The caller should destroy the icon eventually.
STDAPI_(HICON) SHGetFileIcon(HINSTANCE hinst, LPCTSTR pszPath, DWORD dwFileAttributes, UINT uFlags)
{
SHFILEINFO sfi;
SHGetFileInfo(pszPath, dwFileAttributes, &sfi, sizeof(sfi), uFlags | SHGFI_ICON);
return sfi.hIcon;
}
// Return 1 on success and 0 on failure.
DWORD_PTR _GetFileInfoSections(LPITEMIDLIST pidl, SHFILEINFO *psfi, UINT uFlags)
{
DWORD_PTR dwResult = 1;
IShellFolder *psf;
LPCITEMIDLIST pidlLast;
HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
if (SUCCEEDED(hr))
{
// get attributes for file
if (uFlags & SHGFI_ATTRIBUTES)
{
// [New in IE 4.0] If SHGFI_ATTR_SPECIFIED is set, we use psfi->dwAttributes as is
if (!(uFlags & SHGFI_ATTR_SPECIFIED))
psfi->dwAttributes = 0xFFFFFFFF; // get all of them
if (FAILED(psf->GetAttributesOf(1, &pidlLast, &psfi->dwAttributes)))
psfi->dwAttributes = 0;
}
//
// get icon location, place the icon path into szDisplayName
//
if (uFlags & SHGFI_ICONLOCATION)
{
IExtractIcon *pxi;
if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidlLast, IID_PPV_ARG_NULL(IExtractIcon, &pxi))))
{
UINT wFlags;
pxi->GetIconLocation(0, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName),
&psfi->iIcon, &wFlags);
pxi->Release();
// the returned location is not a filename we cant return it.
// just give then nothing.
if (wFlags & GIL_NOTFILENAME)
{
// special case one of our shell32.dll icons......
if (psfi->szDisplayName[0] != TEXT('*'))
psfi->iIcon = 0;
psfi->szDisplayName[0] = 0;
}
}
}
HIMAGELIST himlLarge, himlSmall;
// get the icon for the file.
if ((uFlags & SHGFI_SYSICONINDEX) || (uFlags & SHGFI_ICON))
{
Shell_GetImageLists(&himlLarge, &himlSmall);
if (uFlags & SHGFI_SYSICONINDEX)
dwResult = (DWORD_PTR)((uFlags & SHGFI_SMALLICON) ? himlSmall : himlLarge);
if (uFlags & SHGFI_OPENICON)
SHMapPIDLToSystemImageListIndex(psf, pidlLast, &psfi->iIcon);
else
psfi->iIcon = SHMapPIDLToSystemImageListIndex(psf, pidlLast, NULL);
}
if (uFlags & SHGFI_ICON)
{
HIMAGELIST himl;
UINT flags = 0;
int cx, cy;
if (uFlags & SHGFI_SMALLICON)
{
himl = himlSmall;
cx = GetSystemMetrics(SM_CXSMICON);
cy = GetSystemMetrics(SM_CYSMICON);
}
else
{
himl = himlLarge;
cx = GetSystemMetrics(SM_CXICON);
cy = GetSystemMetrics(SM_CYICON);
}
if (!(uFlags & SHGFI_ATTRIBUTES))
{
psfi->dwAttributes = SFGAO_LINK; // get link only
psf->GetAttributesOf(1, &pidlLast, &psfi->dwAttributes);
}
//
// check for a overlay image thing (link overlay)
//
if ((psfi->dwAttributes & SFGAO_LINK) || (uFlags & SHGFI_LINKOVERLAY))
{
IShellIconOverlayManager *psiom;
HRESULT hrT = GetIconOverlayManager(&psiom);
if (SUCCEEDED(hrT))
{
int iOverlayIndex = 0;
hrT = psiom->GetReservedOverlayInfo(NULL, -1, &iOverlayIndex, SIOM_OVERLAYINDEX, SIOM_RESERVED_LINK);
if (SUCCEEDED(hrT))
flags |= INDEXTOOVERLAYMASK(iOverlayIndex);
}
}
if ((uFlags & SHGFI_ADDOVERLAYS) || (uFlags & SHGFI_OVERLAYINDEX))
{
IShellIconOverlay * pio;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &pio))))
{
int iOverlayIndex = 0;
if (SUCCEEDED(pio->GetOverlayIndex(pidlLast, &iOverlayIndex)))
{
if (uFlags & SHGFI_ADDOVERLAYS)
{
flags |= INDEXTOOVERLAYMASK(iOverlayIndex);
}
if (uFlags & SHGFI_OVERLAYINDEX)
{
// use the upper 16 bits for the overlayindex
psfi->iIcon |= iOverlayIndex << 24;
}
}
pio->Release();
}
}
// check for selected state
if (uFlags & SHGFI_SELECTED)
flags |= ILD_BLEND50;
psfi->hIcon = ImageList_GetIcon(himl, psfi->iIcon, flags);
// if the caller does not want a "shell size" icon
// convert the icon to the "system" icon size.
if (psfi->hIcon && !(uFlags & SHGFI_SHELLICONSIZE))
psfi->hIcon = (HICON)CopyImage(psfi->hIcon, IMAGE_ICON, cx, cy, LR_COPYRETURNORG | LR_COPYDELETEORG);
}
// get display name for the path
if (uFlags & SHGFI_DISPLAYNAME)
{
DisplayNameOf(psf, pidlLast, SHGDN_NORMAL, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName));
}
if (uFlags & SHGFI_TYPENAME)
{
IShellFolder2 *psf2;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
{
VARIANT var;
VariantInit(&var);
if (SUCCEEDED(psf2->GetDetailsEx(pidlLast, &SCID_TYPE, &var)))
{
VariantToStr(&var, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName));
VariantClear(&var);
}
psf2->Release();
}
}
psf->Release();
}
else
dwResult = 0;
return dwResult;
}
//
// This function returns shell info about a given pathname.
// a app can get the following:
//
// Icon (large or small)
// Display Name
// Name of File Type
//
// this function replaces SHGetFileIcon
#define BUGGY_SHELL16_CBFILEINFO (sizeof(SHFILEINFO) - 4)
STDAPI_(DWORD_PTR) SHGetFileInfo(LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO *psfi, UINT cbFileInfo, UINT uFlags)
{
LPITEMIDLIST pidlFull;
DWORD_PTR res = 1;
TCHAR szPath[MAX_PATH];
// this was never enforced in the past
// TODDB: The 16 to 32 bit thunking layer passes in the wrong value for cbFileInfo.
// The size passed in looks to be the size of the 16 bit version of the structure
// rather than the size of the 32 bit version, as such it is 4 bytes shorter.
// TJGREEN: Special-case that size to keep the assertion from firing and party on.
//
ASSERT(!psfi || cbFileInfo == sizeof(*psfi) || cbFileInfo == BUGGY_SHELL16_CBFILEINFO);
// You can't use both SHGFI_ATTR_SPECIFIED and SHGFI_ICON.
ASSERT(uFlags & SHGFI_ATTR_SPECIFIED ? !(uFlags & SHGFI_ICON) : TRUE);
if (pszPath == NULL)
return 0;
if (uFlags == SHGFI_EXETYPE)
return GetExeType(pszPath); // funky way to get EXE type
if (psfi == NULL)
return 0;
psfi->hIcon = 0;
// Zip Pro 6.0 relies on the fact that if you don't ask for the icon,
// the iIcon field doesn't change.
//
// psfi->iIcon = 0;
psfi->szDisplayName[0] = 0;
psfi->szTypeName[0] = 0;
// do some simmple check on the input path.
if (!(uFlags & SHGFI_PIDL))
{
// If the caller wants us to give them the file attributes, we can't trust
// the attributes they gave us in the following two situations.
if (uFlags & SHGFI_ATTRIBUTES)
{
if ((dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
(dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)))
{
DebugMsg(TF_FSTREE, TEXT("SHGetFileInfo cant use caller supplied file attribs for a sys/ro directory (possible junction)"));
uFlags &= ~SHGFI_USEFILEATTRIBUTES;
}
else if (PathIsRoot(pszPath))
{
DebugMsg(TF_FSTREE, TEXT("SHGetFileInfo cant use caller supplied file attribs for a roots"));
uFlags &= ~SHGFI_USEFILEATTRIBUTES;
}
}
if (PathIsRelative(pszPath))
{
if (uFlags & SHGFI_USEFILEATTRIBUTES)
{
// get a shorter path than the current directory to support
// long pszPath names (that might get truncated in the
// long current dir case)
GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
}
else
{
GetCurrentDirectory(ARRAYSIZE(szPath), szPath);
}
PathCombine(szPath, szPath, pszPath);
pszPath = szPath;
}
}
if (uFlags & SHGFI_PIDL)
pidlFull = (LPITEMIDLIST)pszPath;
else if (uFlags & SHGFI_USEFILEATTRIBUTES)
{
WIN32_FIND_DATA fd = {0};
fd.dwFileAttributes = dwFileAttributes;
SHSimpleIDListFromFindData(pszPath, &fd, &pidlFull);
}
else
pidlFull = ILCreateFromPath(pszPath);
if (pidlFull)
{
if (uFlags & (
SHGFI_DISPLAYNAME |
SHGFI_ATTRIBUTES |
SHGFI_SYSICONINDEX |
SHGFI_ICONLOCATION |
SHGFI_ICON |
SHGFI_TYPENAME))
{
res = _GetFileInfoSections(pidlFull, psfi, uFlags);
}
if (!(uFlags & SHGFI_PIDL))
ILFree(pidlFull);
}
else
res = 0;
return res;
}
//===========================================================================
//
// SHGetFileInfoA Stub
//
// This function calls SHGetFileInfoW and then converts the returned
// information back to ANSI.
//
//===========================================================================
STDAPI_(DWORD_PTR) SHGetFileInfoA(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA *psfi, UINT cbFileInfo, UINT uFlags)
{
WCHAR szPathW[MAX_PATH];
LPWSTR pszPathW;
DWORD_PTR dwRet;
if (uFlags & SHGFI_PIDL)
{
pszPathW = (LPWSTR)pszPath; // Its a pidl, fake it as a WSTR
}
else
{
SHAnsiToUnicode(pszPath, szPathW, ARRAYSIZE(szPathW));
pszPathW = szPathW;
}
if (psfi)
{
SHFILEINFOW sfiw;
ASSERT(cbFileInfo == sizeof(*psfi));
// Zip Pro 6.0 sets SHGFI_SMALLICON | SHGFI_OPENICON but forgets to
// pass SHGFI_ICON or SHGFI_SYSICONINDEX, even though they really
// wanted the sys icon index.
//
// In Windows 95, fields of the SHFILEINFOA structure that you didn't
// query for were left unchanged. They happened to have the icon for
// a closed folder lying around there from a previous query, so they
// got away with it by mistake. They got the wrong icon, but it was
// close enough that nobody really complained.
//
// So pre-initialize the sfiw's iIcon with the app's iIcon. That
// way, if it turns out the app didn't ask for the icon, he just
// gets his old value back.
//
sfiw.iIcon = psfi->iIcon;
sfiw.dwAttributes = psfi->dwAttributes;
dwRet = SHGetFileInfoW(pszPathW, dwFileAttributes, &sfiw, sizeof(sfiw), uFlags);
psfi->hIcon = sfiw.hIcon;
psfi->iIcon = sfiw.iIcon;
psfi->dwAttributes = sfiw.dwAttributes;
SHUnicodeToAnsi(sfiw.szDisplayName, psfi->szDisplayName, ARRAYSIZE(psfi->szDisplayName));
SHUnicodeToAnsi(sfiw.szTypeName, psfi->szTypeName, ARRAYSIZE(psfi->szTypeName));
}
else
{
dwRet = SHGetFileInfoW(pszPathW, dwFileAttributes, NULL, 0, uFlags);
}
return dwRet;
}
STDAPI ThunkFindDataWToA(WIN32_FIND_DATAW *pfd, WIN32_FIND_DATAA *pfda, int cb)
{
if (cb < sizeof(WIN32_FIND_DATAA))
return DISP_E_BUFFERTOOSMALL;
memcpy(pfda, pfd, FIELD_OFFSET(WIN32_FIND_DATAA, cFileName));
SHUnicodeToAnsi(pfd->cFileName, pfda->cFileName, ARRAYSIZE(pfda->cFileName));
SHUnicodeToAnsi(pfd->cAlternateFileName, pfda->cAlternateFileName, ARRAYSIZE(pfda->cAlternateFileName));
return S_OK;
}
STDAPI ThunkNetResourceWToA(LPNETRESOURCEW pnrw, LPNETRESOURCEA pnra, UINT cb)
{
HRESULT hr;
if (cb >= sizeof(NETRESOURCEA))
{
LPSTR psza, pszDest[4] = {NULL, NULL, NULL, NULL};
CopyMemory(pnra, pnrw, FIELD_OFFSET(NETRESOURCE, lpLocalName));
psza = (LPSTR)(pnra + 1); // Point just past the structure
if (cb > sizeof(NETRESOURCE))
{
LPWSTR pszSource[4];
UINT i, cchRemaining = cb - sizeof(NETRESOURCE);
pszSource[0] = pnrw->lpLocalName;
pszSource[1] = pnrw->lpRemoteName;
pszSource[2] = pnrw->lpComment;
pszSource[3] = pnrw->lpProvider;
for (i = 0; i < 4; i++)
{
if (pszSource[i])
{
UINT cchItem;
pszDest[i] = psza;
cchItem = SHUnicodeToAnsi(pszSource[i], pszDest[i], cchRemaining);
cchRemaining -= cchItem;
psza += cchItem;
}
}
}
pnra->lpLocalName = pszDest[0];
pnra->lpRemoteName = pszDest[1];
pnra->lpComment = pszDest[2];
pnra->lpProvider = pszDest[3];
hr = S_OK;
}
else
hr = DISP_E_BUFFERTOOSMALL;
return hr;
}
STDAPI NetResourceWVariantToBuffer(const VARIANT* pvar, void* pv, UINT cb)
{
HRESULT hr;
if (cb >= sizeof(NETRESOURCEW))
{
if (pvar && pvar->vt == (VT_ARRAY | VT_UI1))
{
int i;
NETRESOURCEW* pnrw = (NETRESOURCEW*) pvar->parray->pvData;
UINT cbOffsets[4] = { 0, 0, 0, 0 };
UINT cbEnds[4] = { 0, 0, 0, 0 };
LPWSTR pszPtrs[4] = { pnrw->lpLocalName, pnrw->lpRemoteName,
pnrw->lpComment, pnrw->lpProvider };
hr = S_OK;
for (i = 0; i < ARRAYSIZE(pszPtrs); i++)
{
if (pszPtrs[i])
{
cbOffsets[i] = (UINT) ((BYTE*) pszPtrs[i] - (BYTE*) pnrw);
cbEnds[i] = cbOffsets[i] + (sizeof(WCHAR) * (lstrlenW(pszPtrs[i]) + 1));
// If any of the strings start or end too far into the buffer, then fail:
if ((cbOffsets[i] >= cb) || (cbEnds[i] > cb))
{
hr = DISP_E_BUFFERTOOSMALL;
break;
}
}
}
if (SUCCEEDED(hr))
{
hr = VariantToBuffer(pvar, pv, cb) ? S_OK : E_FAIL;
pnrw = (NETRESOURCEW*) pv;
if (SUCCEEDED(hr))
{
// Fixup pointers in structure to point into the output buffer,
// instead of the variant buffer:
LPWSTR* ppszPtrs[4] = { &(pnrw->lpLocalName), &(pnrw->lpRemoteName),
&(pnrw->lpComment), &(pnrw->lpProvider) };
for (i = 0; i < ARRAYSIZE(ppszPtrs); i++)
{
if (*ppszPtrs[i])
{
*ppszPtrs[i] = (LPWSTR) ((BYTE*) pnrw + cbOffsets[i]);
}
}
}
}
}
else
{
hr = E_FAIL;
}
}
else
{
hr = DISP_E_BUFFERTOOSMALL;
}
return hr;
}
// This function will extract information that is cached in the pidl such
// in the information that was returned from a FindFirst file. This function
// is sortof a hack as t allow outside callers to be able to get at the infomation
// without knowing how we store it in the pidl.
// a app can get the following:
STDAPI SHGetDataFromIDListW(IShellFolder *psf, LPCITEMIDLIST pidl, int nFormat, void *pv, int cb)
{
HRESULT hr = E_NOTIMPL;
SHCOLUMNID* pscid;
if (!pv || !psf || !pidl)
return E_INVALIDARG;
switch (nFormat)
{
case SHGDFIL_FINDDATA:
if (cb < sizeof(WIN32_FIND_DATAW))
return DISP_E_BUFFERTOOSMALL;
else
pscid = (SHCOLUMNID*)&SCID_FINDDATA;
break;
case SHGDFIL_NETRESOURCE:
if (cb < sizeof(NETRESOURCEW))
return DISP_E_BUFFERTOOSMALL;
else
pscid = (SHCOLUMNID*)&SCID_NETRESOURCE;
break;
case SHGDFIL_DESCRIPTIONID:
pscid = (SHCOLUMNID*)&SCID_DESCRIPTIONID;
break;
default:
return E_INVALIDARG;
}
IShellFolder2 *psf2;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
{
VARIANT var;
VariantInit(&var);
hr = psf2->GetDetailsEx(pidl, pscid, &var);
if (SUCCEEDED(hr))
{
if (SHGDFIL_NETRESOURCE == nFormat)
{
hr = NetResourceWVariantToBuffer(&var, pv, cb);
}
else
{
if (!VariantToBuffer(&var, pv, cb))
hr = E_FAIL;
}
VariantClear(&var);
}
else
{
TraceMsg(TF_WARNING, "Trying to retrieve find data from unknown PIDL %s", DumpPidl(pidl));
}
psf2->Release();
}
return hr;
}
STDAPI SHGetDataFromIDListA(IShellFolder *psf, LPCITEMIDLIST pidl, int nFormat, void *pv, int cb)
{
HRESULT hr;
WIN32_FIND_DATAW fdw;
NETRESOURCEW *pnrw = NULL;
void *pvData = pv;
int cbData = cb;
if (nFormat == SHGDFIL_FINDDATA)
{
cbData = sizeof(fdw);
pvData = &fdw;
}
else if (nFormat == SHGDFIL_NETRESOURCE)
{
cbData = cb;
pvData = pnrw = (NETRESOURCEW *)LocalAlloc(LPTR, cbData);
if (pnrw == NULL)
return E_OUTOFMEMORY;
}
hr = SHGetDataFromIDListW(psf, pidl, nFormat, pvData, cbData);
if (SUCCEEDED(hr))
{
if (nFormat == SHGDFIL_FINDDATA)
{
hr = ThunkFindDataWToA(&fdw, (WIN32_FIND_DATAA *)pv, cb);
}
else if (nFormat == SHGDFIL_NETRESOURCE)
{
hr = ThunkNetResourceWToA(pnrw, (NETRESOURCEA *)pv, cb);
}
}
if (pnrw)
LocalFree(pnrw);
return hr;
}
int g_iUseLinkPrefix = -1;
#define INITIALLINKPREFIXCOUNT 20
#define MAXLINKPREFIXCOUNT 30
void LoadUseLinkPrefixCount()
{
TraceMsg(TF_FSTREE, "LoadUseLinkPrefixCount %d", g_iUseLinkPrefix);
if (g_iUseLinkPrefix < 0)
{
DWORD cb = sizeof(g_iUseLinkPrefix);
if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, c_szLink, NULL, &g_iUseLinkPrefix, &cb))
|| g_iUseLinkPrefix < 0)
{
g_iUseLinkPrefix = INITIALLINKPREFIXCOUNT;
}
}
}
void SaveUseLinkPrefixCount()
{
if (g_iUseLinkPrefix >= 0)
{
SKSetValue(SHELLKEY_HKCU_EXPLORER, NULL, c_szLink, REG_BINARY, &g_iUseLinkPrefix, sizeof(g_iUseLinkPrefix));
}
}
#define ISDIGIT(c) ((c) >= TEXT('0') && (c) <= TEXT('9'))
// psz2 = destination
// psz1 = source
void StripNumber(LPTSTR psz2, LPCTSTR psz1)
{
// strip out the '(' and the numbers after it
// We need to verify that it is either simply () or (999) but not (A)
for (; *psz1; psz1 = CharNext(psz1), psz2 = CharNext(psz2))
{
if (*psz1 == TEXT('('))
{
LPCTSTR pszT = psz1;
do
{
psz1 = CharNext(psz1);
} while (*psz1 && ISDIGIT(*psz1));
if (*psz1 == TEXT(')'))
{
psz1 = CharNext(psz1);
if (*psz1 == TEXT(' '))
psz1 = CharNext(psz1); // skip the extra space
lstrcpy(psz2, psz1);
return;
}
// We have a (that does not match the format correctly!
psz1 = pszT; // restore pointer back to copy this char through and continue...
}
*psz2 = *psz1;
}
*psz2 = *psz1;
}
#define SHORTCUT_PREFIX_DECR 5
#define SHORTCUT_PREFIX_INCR 1
// this checks to see if you've renamed 'Shortcut #x To Foo' to 'Foo'
void CheckShortcutRename(LPCTSTR pszOldPath, LPCTSTR pszNewPath)
{
ASSERT(pszOldPath);
ASSERT(pszNewPath);
// already at 0.
if (g_iUseLinkPrefix)
{
LPCTSTR pszOldName = PathFindFileName(pszOldPath);
if (PathIsLnk(pszOldName))
{
TCHAR szBaseName[MAX_PATH];
TCHAR szLinkTo[80];
TCHAR szMockName[MAX_PATH];
LPCTSTR pszNewName = PathFindFileName(pszNewPath);
lstrcpy(szBaseName, pszNewName);
PathRemoveExtension(szBaseName);
// mock up a name using the basename and the linkto template
LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo));
wnsprintf(szMockName, ARRAYSIZE(szMockName), szLinkTo, szBaseName);
StripNumber(szMockName, szMockName);
StripNumber(szBaseName, pszOldName);
// are the remaining gunk the same?
if (!lstrcmp(szMockName, szBaseName))
{
// yes! do the link count magic
LoadUseLinkPrefixCount();
ASSERT(g_iUseLinkPrefix >= 0);
g_iUseLinkPrefix -= SHORTCUT_PREFIX_DECR;
if (g_iUseLinkPrefix < 0)
g_iUseLinkPrefix = 0;
SaveUseLinkPrefixCount();
}
}
}
}
STDAPI_(int) SHRenameFileEx(HWND hwnd, IUnknown *punkEnableModless, LPCTSTR pszDir,
LPCTSTR pszOldName, LPCTSTR pszNewName)
{
int iRet = ERROR_CANCELLED; // user saw the error, don't report again
TCHAR szOldPathName[MAX_PATH + 1]; // +1 for double nul terminating on SHFileOperation
TCHAR szTempNewPath[MAX_PATH];
BOOL bEnableUI = hwnd || punkEnableModless;
IUnknown_EnableModless(punkEnableModless, FALSE);
PathCombine(szOldPathName, pszDir, pszOldName);
szOldPathName[lstrlen(szOldPathName) + 1] = 0;
StrCpyN(szTempNewPath, pszNewName, ARRAYSIZE(szTempNewPath));
int err = PathCleanupSpec(pszDir, szTempNewPath);
if (err)
{
if (bEnableUI)
{
ShellMessageBox(HINST_THISDLL, hwnd,
err & PCS_PATHTOOLONG ?
MAKEINTRESOURCE(IDS_REASONS_INVFILES) :
IsLFNDrive(pszDir) ?
MAKEINTRESOURCE(IDS_INVALIDFN) :
MAKEINTRESOURCE(IDS_INVALIDFNFAT),
MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND);
}
}
else
{
// strip off leading and trailing blanks off of the new file name.
StrCpyN(szTempNewPath, pszNewName, ARRAYSIZE(szTempNewPath));
PathRemoveBlanks(szTempNewPath);
if (!szTempNewPath[0] || (szTempNewPath[0] == TEXT('.')))
{
if (bEnableUI)
{
ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(IDS_NONULLNAME),
MAKEINTRESOURCE(IDS_RENAME), MB_OK | MB_ICONHAND);
}
}
else
{
int idPrompt = IDYES;
TCHAR szNewPathName[MAX_PATH + 1]; // +1 for double nul terminating on SHFileOperation
PathCombine(szNewPathName, pszDir, szTempNewPath);
// if there was an old extension and the new and old don't match complain
LPTSTR pszExt = PathFindExtension(pszOldName);
if (*pszExt && lstrcmpi(pszExt, PathFindExtension(szTempNewPath)))
{
HKEY hk;
if (!PathIsDirectory(szOldPathName) &&
SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, pszExt, NULL, &hk)))
{
RegCloseKey(hk);
if (bEnableUI)
{
idPrompt = ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(IDS_WARNCHANGEEXT),
MAKEINTRESOURCE(IDS_RENAME), MB_YESNO | MB_ICONEXCLAMATION);
}
}
}
if (IDYES == idPrompt)
{
szNewPathName[lstrlen(szNewPathName) + 1] = 0; // double NULL terminate
SHFILEOPSTRUCT fo = { hwnd, FO_RENAME, szOldPathName, szNewPathName, FOF_SILENT | FOF_ALLOWUNDO, };
iRet = SHFileOperation(&fo);
if (ERROR_SUCCESS == iRet)
CheckShortcutRename(szOldPathName, szNewPathName);
}
}
}
IUnknown_EnableModless(punkEnableModless, TRUE);
return iRet;
}
HKEY SHOpenShellFolderKey(const CLSID *pclsid)
{
HKEY hkey;
return SUCCEEDED(SHRegGetCLSIDKey(*pclsid, TEXT("ShellFolder"), FALSE, FALSE, &hkey)) ? hkey : NULL;
}
BOOL SHQueryShellFolderValue(const CLSID *pclsid, LPCTSTR pszValueName)
{
BOOL bRet = FALSE; // assume no
HKEY hkey = SHOpenShellFolderKey(pclsid);
if (hkey)
{
bRet = SHQueryValueEx(hkey, pszValueName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS;
RegCloseKey(hkey);
}
return bRet;
}
//
// The SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY key contains a bunch of values,
// each named after a GUID. The data associated with each value is a
// DWORD, which is either...
//
// 0 = no restriction on this CLSID
// 1 = unconditional restriction on this CLSID
// 0xFFFFFFFF = same as 1 (in case somebody got "creative")
// any other value = pass to SHRestricted() to see what the restriction is
//
// We support 0xFFFFFFFF only out of paranoia. This flag was only 0 or 1
// in Windows 2000, and somebody might've decided that "all bits set"
// is better than "just one bit set".
//
#define SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\NonEnum")
BOOL _IsNonEnumPolicySet(const CLSID *pclsid)
{
BOOL fPolicySet = FALSE;
TCHAR szCLSID[GUIDSTR_MAX];
DWORD dwDefault = 0;
RESTRICTIONS rest = REST_NONE;
DWORD cbSize = sizeof(rest);
if (EVAL(SHStringFromGUID(*pclsid, szCLSID, ARRAYSIZE(szCLSID))) &&
(ERROR_SUCCESS == SHRegGetUSValue(SZ_REGKEY_MYCOMPUTER_NONENUM_POLICY, szCLSID, NULL, &rest, &cbSize, FALSE, &dwDefault, sizeof(dwDefault))) &&
rest)
{
fPolicySet = rest == 1 || rest == 0xFFFFFFFF || SHRestricted(rest);
}
return fPolicySet;
}
//
// This function returns the attributes (to be returned IShellFolder::
// GetAttributesOf) of the junction point specified by the class ID.
//
STDAPI_(DWORD) SHGetAttributesFromCLSID(const CLSID *pclsid, DWORD dwDefault)
{
return SHGetAttributesFromCLSID2(pclsid, dwDefault, (DWORD)-1);
}
DWORD QueryCallForAttributes(HKEY hkey, const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested)
{
DWORD dwAttr = dwDefAttrs;
DWORD dwData, cbSize = sizeof(dwAttr);
// consider caching this folder to avoid creating over and over
// mydocs.dll uses this for compat with old apps
// See if this folder has asked us specifically to call and get
// the attributes...
//
if (SHQueryValueEx(hkey, TEXT("CallForAttributes"), NULL, NULL, &dwData, &cbSize) == ERROR_SUCCESS)
{
// CallForAttributes can be a masked value. See if it's being supplied in the value.
// NOTE: MyDocs.dll registers with a NULL String, so this check works.
DWORD dwMask = (DWORD)-1;
if (sizeof(dwData) == cbSize)
{
// There is a mask, Use this.
dwMask = dwData;
}
// Is the requested bit contained in the specified mask?
if (dwMask & dwRequested)
{
// Yes. Then CoCreate and Query.
IShellFolder *psf;
if (SUCCEEDED(SHExtCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IShellFolder, &psf))))
{
dwAttr = dwRequested;
psf->GetAttributesOf(0, NULL, &dwAttr);
psf->Release();
}
else
{
dwAttr |= SFGAO_FILESYSTEM;
}
}
}
return dwAttr;
}
// dwRequested is the bits you are explicitly looking for. This is an optimization that prevents reg hits.
STDAPI_(DWORD) SHGetAttributesFromCLSID2(const CLSID *pclsid, DWORD dwDefAttrs, DWORD dwRequested)
{
DWORD dwAttr = dwDefAttrs;
HKEY hkey = SHOpenShellFolderKey(pclsid);
if (hkey)
{
DWORD dwData, cbSize = sizeof(dwAttr);
// We are looking for some attributes on a shell folder. These attributes can be in two locations:
// 1) In the "Attributes" value in the registry.
// 2) Stored in a the shell folder's GetAttributesOf.
// First, Check to see if the reqested value is contained in the registry.
if (SHQueryValueEx(hkey, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwData, &cbSize) == ERROR_SUCCESS &&
cbSize == sizeof(dwData))
{
// We have data there, but it may not contain the data we are looking for
dwAttr = dwData & dwRequested;
// Does it contain the bit we are looking for?
if (((dwAttr & dwRequested) != dwRequested) && dwRequested != 0)
{
// No. Check to see if it is in the shell folder implementation
goto CallForAttributes;
}
}
else
{
CallForAttributes:
// See if we have to talk to the shell folder.
// I'm passing dwAttr, because if the previous case did not generate any attributes, then it's
// equal to dwDefAttrs. If the call to CallForAttributes fails, then it will contain the value of
// dwDefAttrs or whatever was in the shell folder's Attributes key
dwAttr = QueryCallForAttributes(hkey, pclsid, dwAttr, dwRequested);
}
RegCloseKey(hkey);
}
if (_IsNonEnumPolicySet(pclsid))
dwAttr |= SFGAO_NONENUMERATED;
if (SHGetObjectCompatFlags(NULL, pclsid) & OBJCOMPATF_NOTAFILESYSTEM)
dwAttr &= ~SFGAO_FILESYSTEM;
return dwAttr;
}
// _BuildLinkName
//
// Used during the creation of a shortcut, this function determines an appropriate name for the shortcut.
// This is not the exact name that will be used becuase it will usually contain "() " which will either
// get removed or replaced with "(x) " where x is a number that makes the name unique. This removal is done
// elsewhere (currently in PathYetAnotherMakeUniqueName).
//
// in:
// pszName file spec part
// pszDir path part of name to know how to limit the long name...
//
// out:
// pszLinkName - Full path to link name (May fit in 8.3...). Can be the same buffer as pszName.
//
// NOTES: If pszDir + pszLinkName is greater than MAX_PATH we will fail to create the shortcut.
// In an effort to prevent
void _BuildLinkName(LPTSTR pszLinkName, LPCTSTR pszName, LPCTSTR pszDir, BOOL fLinkTo)
{
TCHAR szLinkTo[40]; // "Shortcut to %s.lnk"
TCHAR szTemp[MAX_PATH + 40];
if (fLinkTo)
{
// check to see if we're in the "don't ever say 'shortcut to' mode"
LoadUseLinkPrefixCount();
if (!g_iUseLinkPrefix)
{
fLinkTo = FALSE;
}
else if (g_iUseLinkPrefix > 0)
{
if (g_iUseLinkPrefix < MAXLINKPREFIXCOUNT)
{
g_iUseLinkPrefix += SHORTCUT_PREFIX_INCR;
SaveUseLinkPrefixCount();
}
}
}
if (!fLinkTo)
{
// Generate the title of this link ("XX.lnk")
LoadString(HINST_THISDLL, IDS_LINKEXTENSION, szLinkTo, ARRAYSIZE(szLinkTo));
}
else
{
// Generate the title of this link ("Shortcut to XX.lnk")
LoadString(HINST_THISDLL, IDS_LINKTO, szLinkTo, ARRAYSIZE(szLinkTo));
}
wnsprintf(szTemp, ARRAYSIZE(szTemp), szLinkTo, pszName);
PathCleanupSpecEx(pszDir, szTemp); // get rid of illegal chars AND ensure correct filename length
lstrcpyn(pszLinkName, szTemp, MAX_PATH);
ASSERT(PathIsLnk(pszLinkName));
}
// return a new destination path for a link
//
// in:
// fErrorSoTryDesktop we are called because there was an error saving
// the shortcut and we want to prompt to see if the
// desktop should be used.
//
// in/out:
// pszPath on input the place being tried, on output the desktop folder
//
// returns:
//
// IDYES user said yes to creating a link at new place
// IDNO user said no to creating a link at new place
// -1 error
//
int _PromptTryDesktopLinks(HWND hwnd, LPTSTR pszPath, BOOL fErrorSoTryDesktop)
{
TCHAR szPath[MAX_PATH];
if (!SHGetSpecialFolderPath(hwnd, szPath, CSIDL_DESKTOPDIRECTORY, FALSE))
return -1; // fail no desktop dir
int idOk;
if (fErrorSoTryDesktop)
{
// Fail, if pszPath already points to the desktop directory.
if (lstrcmpi(szPath, pszPath) == 0)
return -1;
idOk = ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(IDS_TRYDESKTOPLINK),
MAKEINTRESOURCE(IDS_LINKTITLE),
MB_YESNO | MB_ICONQUESTION);
}
else
{
ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(IDS_MAKINGDESKTOPLINK),
MAKEINTRESOURCE(IDS_LINKTITLE),
MB_OK | MB_ICONASTERISK);
idOk = IDYES;
}
if (idOk == IDYES)
lstrcpy(pszPath , szPath); // output
return idOk; // return yes or no
}
// in:
// pszpdlLinkTo LPCITEMIDLIST or LPCTSTR, target of link to create
// pszDir where we will put the link
// uFlags SHGNLI_ flags
//
// out:
// pszName file name to create "c:\Shortcut to Foo.lnk"
// pfMustCopy pszpdlLinkTo was a link itself, make a copy of this
STDAPI_(BOOL) SHGetNewLinkInfo(LPCTSTR pszpdlLinkTo, LPCTSTR pszDir, LPTSTR pszName,
BOOL *pfMustCopy, UINT uFlags)
{
BOOL fDosApp = FALSE;
BOOL fLongFileNames = IsLFNDrive(pszDir);
SHFILEINFO sfi;
*pfMustCopy = FALSE;
sfi.dwAttributes = SFGAO_FILESYSTEM | SFGAO_LINK | SFGAO_FOLDER;
if (uFlags & SHGNLI_PIDL)
{
if (FAILED(SHGetNameAndFlags((LPCITEMIDLIST)pszpdlLinkTo, SHGDN_NORMAL,
pszName, MAX_PATH, &sfi.dwAttributes)))
return FALSE;
}
else
{
if (SHGetFileInfo(pszpdlLinkTo, 0, &sfi, sizeof(sfi),
SHGFI_DISPLAYNAME | SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED |
((uFlags & SHGNLI_PIDL) ? SHGFI_PIDL : 0)))
lstrcpy(pszName, sfi.szDisplayName);
else
return FALSE;
}
if (PathCleanupSpecEx(pszDir, pszName) & PCS_FATAL)
return FALSE;
//
// WARNING: From this point on, sfi.szDisplayName may be re-used to
// contain the file path of the PIDL we are linking to. Don't rely on
// it containing the display name.
//
if (sfi.dwAttributes & SFGAO_FILESYSTEM)
{
LPTSTR pszPathSrc;
if (uFlags & SHGNLI_PIDL)
{
pszPathSrc = sfi.szDisplayName;
SHGetPathFromIDList((LPCITEMIDLIST)pszpdlLinkTo, pszPathSrc);
}
else
{
pszPathSrc = (LPTSTR)pszpdlLinkTo;
}
fDosApp = (lstrcmpi(PathFindExtension(pszPathSrc), TEXT(".pif")) == 0) ||
(LOWORD(GetExeType(pszPathSrc)) == 0x5A4D); // 'MZ'
if (sfi.dwAttributes & SFGAO_LINK)
{
*pfMustCopy = TRUE;
if (!(sfi.dwAttributes & SFGAO_FOLDER))
{
uFlags &= ~SHGNLI_NOLNK; // if copying the file then don't trim the extension
}
lstrcpy(pszName, PathFindFileName(pszPathSrc));
}
else
{
//
// when making a link to a drive root. special case a few things
//
// if we are not on a LFN drive, dont use the full name, just
// use the drive letter. "C.LNK" not "Label (C).LNK"
//
// if we are making a link to removable media, we dont want the
// label as part of the name, we want the media type.
//
// CD-ROM drives are currently the only removable media we
// show the volume label for, so we only need to special case
// cdrom drives here.
//
if (PathIsRoot(pszPathSrc) && !PathIsUNC(pszPathSrc))
{
if (!fLongFileNames)
lstrcpy(pszName, pszPathSrc);
else if (IsCDRomDrive(DRIVEID(pszPathSrc)))
LoadString(HINST_THISDLL, IDS_DRIVES_CDROM, pszName, MAX_PATH);
}
}
if (fLongFileNames && fDosApp)
{
HANDLE hPif = PifMgr_OpenProperties(pszPathSrc, NULL, 0, OPENPROPS_INHIBITPIF);
if (hPif)
{
PROPPRG PP = {0};
if (PifMgr_GetProperties(hPif, (LPCSTR)MAKELP(0, GROUP_PRG), &PP, sizeof(PP), 0) &&
((PP.flPrgInit & PRGINIT_INFSETTINGS) ||
((PP.flPrgInit & (PRGINIT_NOPIF | PRGINIT_DEFAULTPIF)) == 0)))
{
SHAnsiToTChar(PP.achTitle, pszName, MAX_PATH);
}
PifMgr_CloseProperties(hPif, 0);
}
}
}
if (!*pfMustCopy)
{
// create full dest path name. only use template iff long file names
// can be created and the caller requested it. _BuildLinkName will
// truncate files on non-lfn drives and clean up any invalid chars.
_BuildLinkName(pszName, pszName, pszDir,
(!(*pfMustCopy) && fLongFileNames && (uFlags & SHGNLI_PREFIXNAME)));
}
if (fDosApp)
PathRenameExtension(pszName, TEXT(".pif"));
if (uFlags & SHGNLI_NOLNK)
{
// Don't do PathRemoveExtension because pszName might contain
// internal dots ("Windows 3.1") and passing that to
// PathYetAnotherMakeUniqueName will result in
// "Windows 3 (2).1" which is wrong. We leave the dot at the
// end so we get "Windows 3.1 (2)." back. We will strip off the
// final dot later.
PathRenameExtension(pszName, TEXT("."));
}
// make sure the name is unique
// NOTE: PathYetAnotherMakeUniqueName will return the directory+filename in the pszName buffer.
// It returns FALSE if the name is not unique or the dir+filename is too long. If it returns
// false then this function should return false because creation will fail.
BOOL fSuccess;
if (!(uFlags & SHGNLI_NOUNIQUE))
fSuccess = PathYetAnotherMakeUniqueName(pszName, pszDir, pszName, pszName);
else
fSuccess = TRUE;
// Strip off any trailing dots that may have been generated by SHGNI_NOLNK
PathStripTrailingDots(pszName);
return fSuccess;
}
STDAPI_(BOOL) SHGetNewLinkInfoA(LPCSTR pszpdlLinkTo, LPCSTR pszDir, LPSTR pszName,
BOOL *pfMustCopy, UINT uFlags)
{
ThunkText * pThunkText;
BOOL bResult = FALSE;
if (uFlags & SHGNLI_PIDL)
{
// 1 string (pszpdlLinkTo is a pidl)
pThunkText = ConvertStrings(2, NULL, pszDir);
if (pThunkText)
pThunkText->m_pStr[0] = (LPWSTR)pszpdlLinkTo;
}
else
{
// 2 strings
pThunkText = ConvertStrings(2, pszpdlLinkTo, pszDir);
}
if (pThunkText)
{
WCHAR wszName[MAX_PATH];
bResult = SHGetNewLinkInfoW(pThunkText->m_pStr[0], pThunkText->m_pStr[1],
wszName, pfMustCopy, uFlags);
LocalFree(pThunkText);
if (bResult)
{
if (0 == WideCharToMultiByte(CP_ACP, 0, wszName, -1,
pszName, MAX_PATH, NULL, NULL))
{
SetLastError((DWORD)E_FAIL);
bResult = FALSE;
}
}
}
return bResult;
}
//
// in:
// pidlTo
STDAPI CreateLinkToPidl(LPCITEMIDLIST pidlTo, LPCTSTR pszDir, LPITEMIDLIST *ppidl, UINT uFlags)
{
HRESULT hr = E_FAIL;
TCHAR szPathDest[MAX_PATH];
BOOL fCopyLnk;
BOOL fUseLinkTemplate = (SHCL_USETEMPLATE & uFlags);
UINT uSHGNLI = fUseLinkTemplate ? SHGNLI_PIDL | SHGNLI_PREFIXNAME : SHGNLI_PIDL;
if (uFlags & SHCL_MAKEFOLDERSHORTCUT)
{
// Don't add ".lnk" to the folder shortcut name; that's just stupid
uSHGNLI |= SHGNLI_NOLNK;
}
if (uFlags & SHCL_NOUNIQUE)
{
uSHGNLI |= SHGNLI_NOUNIQUE;
}
if (SHGetNewLinkInfo((LPTSTR)pidlTo, pszDir, szPathDest, &fCopyLnk, uSHGNLI))
{
TCHAR szPathSrc[MAX_PATH];
IShellLink *psl = NULL;
// If we passed SHGNLI_NOUNIQUE then we need to do the PathCombine ourselves
// because SHGetNewLinkInfo won't
if (uFlags & SHCL_NOUNIQUE)
{
PathCombine(szPathDest, pszDir, szPathDest);
}
DWORD dwAttributes = SFGAO_FILESYSTEM | SFGAO_FOLDER;
SHGetNameAndFlags(pidlTo, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szPathSrc, ARRAYSIZE(szPathSrc), &dwAttributes);
if (fCopyLnk)
{
// if it is file system and not a folder (CopyFile does not work on folders)
// just copy it.
if (((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM) &&
CopyFile(szPathSrc, szPathDest, TRUE))
{
TouchFile(szPathDest);
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, szPathDest, NULL);
SHChangeNotify(SHCNE_FREESPACE, SHCNF_PATH, szPathDest, NULL);
hr = S_OK;
}
else
{
// load the source object that will be "copied" below (with the ::Save call)
hr = SHGetUIObjectFromFullPIDL(pidlTo, NULL, IID_PPV_ARG(IShellLink, &psl));
}
}
else
{
hr = SHCoCreateInstance(NULL, uFlags & SHCL_MAKEFOLDERSHORTCUT ?
&CLSID_FolderShortcut : &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl));
if (SUCCEEDED(hr))
{
hr = psl->SetIDList(pidlTo);
// set the working directory to the same path
// as the file we are linking too
if (szPathSrc[0] && ((dwAttributes & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM))
{
PathRemoveFileSpec(szPathSrc);
psl->SetWorkingDirectory(szPathSrc);
}
}
}
if (psl)
{
if (SUCCEEDED(hr))
{
IPersistFile *ppf;
hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hr))
{
USES_CONVERSION;
hr = ppf->Save(T2CW(szPathDest), TRUE);
if (SUCCEEDED(hr))
{
// in case ::Save translated the name of the
// file (.LNK -> .PIF, or Folder Shortcut)
WCHAR *pwsz;
if (SUCCEEDED(ppf->GetCurFile(&pwsz)) && pwsz)
{
SHUnicodeToTChar(pwsz, szPathDest, ARRAYSIZE(szPathDest));
SHFree(pwsz);
}
}
ppf->Release();
}
}
psl->Release();
}
}
if (ppidl)
{
*ppidl = SUCCEEDED(hr) ? SHSimpleIDListFromPath(szPathDest) : NULL;
}
return hr;
}
// in/out:
// pszDir inital folder to try, output new folder (desktop)
// out:
// ppidl optional output PIDL of thing created
HRESULT _CreateLinkRetryDesktop(HWND hwnd, LPCITEMIDLIST pidlTo, LPTSTR pszDir, UINT fFlags, LPITEMIDLIST *ppidl)
{
HRESULT hr;
if (ppidl)
*ppidl = NULL; // assume error
if (*pszDir && (fFlags & SHCL_CONFIRM))
{
hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags);
}
else
{
hr = E_FAIL;
}
// if we were unable to save, ask user if they want us to
// try it again but change the path to the desktop.
if (FAILED(hr))
{
int id;
if (hr == STG_E_MEDIUMFULL)
{
DebugMsg(TF_ERROR, TEXT("failed to create link because disk is full"));
id = IDYES;
}
else
{
if (fFlags & SHCL_CONFIRM)
{
id = _PromptTryDesktopLinks(hwnd, pszDir, (fFlags & SHCL_CONFIRM));
}
else
{
id = (SHGetSpecialFolderPath(hwnd, pszDir, CSIDL_DESKTOPDIRECTORY, FALSE)) ? IDYES : IDNO;
}
if (id == IDYES && *pszDir)
{
hr = CreateLinkToPidl(pidlTo, pszDir, ppidl, fFlags);
}
}
// we failed to create the link complain to the user.
if (FAILED(hr) && id != IDNO)
{
ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(IDS_CANNOTCREATELINK),
MAKEINTRESOURCE(IDS_LINKTITLE),
MB_OK | MB_ICONASTERISK);
}
}
#ifdef DEBUG
if (FAILED(hr) && ppidl)
ASSERT(*ppidl == NULL);
#endif
return hr;
}
//
// This function creates links to the stuff in the IDataObject
//
// Arguments:
// hwnd for any UI
// pszDir optional target directory (where to create links)
// pDataObj data object describing files (array of idlist)
// ppidl optional pointer to an array that receives pidls pointing to the new links
// or NULL if not interested
STDAPI SHCreateLinks(HWND hwnd, LPCTSTR pszDir, IDataObject *pDataObj, UINT fFlags, LPITEMIDLIST* ppidl)
{
DECLAREWAITCURSOR;
STGMEDIUM medium;
HRESULT hr;
SetWaitCursor();
LPIDA pida = DataObj_GetHIDA(pDataObj, &medium);
if (pida)
{
TCHAR szTargetDir[MAX_PATH];
hr = S_OK; // In case hida contains zero elements
szTargetDir[0] = 0;
if (pszDir)
lstrcpyn(szTargetDir, pszDir, ARRAYSIZE(szTargetDir));
if (!(fFlags & SHCL_USEDESKTOP))
fFlags |= SHCL_CONFIRM;
for (UINT i = 0; i < pida->cidl; i++)
{
LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
if (pidlTo)
{
hr = _CreateLinkRetryDesktop(hwnd, pidlTo, szTargetDir, fFlags, ppidl ? &ppidl[i] : NULL);
ILFree(pidlTo);
if (FAILED(hr))
break;
}
}
HIDA_ReleaseStgMedium(pida, &medium);
}
else
hr = E_OUTOFMEMORY;
SHChangeNotifyHandleEvents();
ResetWaitCursor();
return hr;
}
#if 1
HRESULT SelectPidlInSFV(IShellFolderViewDual *psfv, LPCITEMIDLIST pidl, DWORD dwOpts)
{
VARIANT var;
HRESULT hr = InitVariantFromIDList(&var, pidl);
if (SUCCEEDED(hr))
{
hr = psfv->SelectItem(&var, dwOpts);
VariantClear(&var);
}
return hr;
}
HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, IShellFolderViewDual **ppsfv)
{
*ppsfv = NULL;
IWebBrowserApp *pauto;
HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
if (SUCCEEDED(hr))
{
HWND hwnd;
if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd)))
{
// Make sure we make this the active window
SetForegroundWindow(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
}
IDispatch *pdoc;
hr = pauto->get_Document(&pdoc);
if (S_OK == hr) // careful, automation returns S_FALSE
{
hr = pdoc->QueryInterface(IID_PPV_ARG(IShellFolderViewDual, ppsfv));
pdoc->Release();
}
else
hr = E_FAIL;
pauto->Release();
}
return hr;
}
// pidlFolder - fully qualified pidl to the folder to open
// cidl/apidl - array of items in that folder to select
//
// if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's
// folder is opened and it is selected.
//
// dwFlags - optional flags, pass 0 for now
SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags)
{
HRESULT hr;
if (0 == cidl)
{
// overload the 0 item case to mean pidlFolder is the full pidl to the item
LPITEMIDLIST pidlTemp;
hr = SHILClone(pidlFolder, &pidlTemp);
if (SUCCEEDED(hr))
{
ILRemoveLastID(pidlTemp); // strip to the folder
LPCITEMIDLIST pidl = ILFindLastID(pidlFolder);
hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse
ILFree(pidlTemp);
}
}
else
{
IShellFolderViewDual *psfv;
hr = OpenFolderAndGetView(pidlFolder, &psfv);
if (SUCCEEDED(hr))
{
DWORD dwSelFlags = SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE;
for (UINT i = 0; i < cidl; i++)
{
hr = SelectPidlInSFV(psfv, apidl[i], dwSelFlags);
dwSelFlags = SVSI_SELECT; // second items append to sel
}
psfv->Release();
}
}
return hr;
}
#else
HRESULT OpenFolderAndGetView(LPCITEMIDLIST pidlFolder, REFIID riid, void **ppv)
{
*ppv = NULL;
IWebBrowserApp *pauto;
HRESULT hr = SHGetIDispatchForFolder(pidlFolder, &pauto);
if (SUCCEEDED(hr))
{
HWND hwnd;
if (SUCCEEDED(pauto->get_HWND((LONG_PTR*)&hwnd)))
{
// Make sure we make this the active window
SetForegroundWindow(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
}
IShellBrowser* psb;
hr = IUnknown_QueryService(pauto, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb));
if (SUCCEEDED(hr))
{
IShellView* psv;
hr = psb->QueryActiveShellView(&psv);
if (SUCCEEDED(hr))
{
hr = psv->QueryInterface(riid, ppv);
psv->Release();
}
psb->Release();
}
pauto->Release();
}
return hr;
}
// pidlFolder - fully qualified pidl to the folder to open
// cidl/apidl - array of items in that folder to select
//
// if cild == 0 then pidlFolder is the fully qualified pidl to a single item, it's
// folder is opened and it is selected.
//
// dwFlags - optional flags, pass 0 for now
SHSTDAPI SHOpenFolderAndSelectItems(LPCITEMIDLIST pidlFolder, UINT cidl, LPCITEMIDLIST *apidl, DWORD dwFlags)
{
HRESULT hr;
if (0 == cidl)
{
// overload the 0 item case to mean pidlFolder is the full pidl to the item
LPITEMIDLIST pidlTemp;
hr = SHILClone(pidlFolder, &pidlTemp);
if (SUCCEEDED(hr))
{
ILRemoveLastID(pidlTemp); // strip to the folder
LPCITEMIDLIST pidl = ILFindLastID(pidlFolder);
hr = SHOpenFolderAndSelectItems(pidlTemp, 1, &pidl, 0); // recurse
ILFree(pidlTemp);
}
}
else
{
IFolderView *pfv;
hr = OpenFolderAndGetView(pidlFolder, IID_PPV_ARG(IFolderView, &pfv));
if (SUCCEEDED(hr))
{
pfv->SelectAndPositionItems(1, apidl, NULL, SVSI_SELECT | SVSI_FOCUSED | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE);
if (cidl > 1)
pfv->SelectAndPositionItems(cidl - 1, apidl + 1, NULL, SVSI_SELECT);
pfv->Release();
}
}
return hr;
}
#endif
SHSTDAPI SHCreateShellItem(LPCITEMIDLIST pidlParent, IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
{
*ppsi = NULL;
IShellItem *psi;
HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellItem, NULL, IID_PPV_ARG(IShellItem, &psi));
if (SUCCEEDED(hr))
{
if (pidlParent || psfParent)
{
IParentAndItem *pinit;
ASSERT(pidl);
hr = psi->QueryInterface(IID_PPV_ARG(IParentAndItem, &pinit));
if (SUCCEEDED(hr))
{
hr = pinit->SetParentAndItem(pidlParent, psfParent, pidl);
pinit->Release();
}
}
else
{
IPersistIDList *pinit;
hr = psi->QueryInterface(IID_PPV_ARG(IPersistIDList, &pinit));
if (SUCCEEDED(hr))
{
hr = pinit->SetIDList(pidl);
pinit->Release();
}
}
if (SUCCEEDED(hr))
*ppsi = psi;
else
psi->Release();
}
return hr;
}
STDAPI SHCreateShellItemFromParent(IShellItem *psiParent, LPCWSTR pszName, IShellItem **ppsi)
{
*ppsi = NULL;
IShellFolder *psf;
HRESULT hr = psiParent->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidl;
hr = SHGetIDListFromUnk(psiParent, &pidl);
if (SUCCEEDED(hr))
{
ULONG cchEaten;
LPITEMIDLIST pidlChild;
hr = psf->ParseDisplayName(NULL, NULL, (LPWSTR)pszName, &cchEaten, &pidlChild, NULL);
if (SUCCEEDED(hr))
{
hr = SHCreateShellItem(pidl, psf, pidlChild, ppsi);
ILFree(pidlChild);
}
ILFree(pidl);
}
psf->Release();
}
return hr;
}
SHSTDAPI SHSetLocalizedName(LPWSTR pszPath, LPCWSTR pszResModule, int idsRes)
{
IShellFolder *psfDesktop;
HRESULT hrInit = SHCoInitialize();
HRESULT hr = hrInit;
if (SUCCEEDED(hrInit))
{
hr = SHGetDesktopFolder(&psfDesktop);
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidl;
hr = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL);
if (SUCCEEDED(hr))
{
LPCITEMIDLIST pidlChild;
IShellFolder *psf;
hr = SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
if (SUCCEEDED(hr))
{
// WARNING - this is a stack sensitive function - ZekeL 29-Jan-2001
// since this function is called by winlogon/userenv
// we need to be sensitive to the stack limitations of those callers
// the shortname will be no larger than the long name
DWORD cchShort = lstrlenW(pszResModule) + 1;
WCHAR *pszShort = (WCHAR *)alloca(CbFromCchW(cchShort));
DWORD cch = GetShortPathName(pszResModule, pszShort, cchShort);
if (cch)
{
pszResModule = pszShort;
}
else
{
// GSPN() fails when the module passed in is a relative path
cch = cchShort;
}
cch += 14; // 11 for id + ',' + '@' + null
WCHAR *pszName = (WCHAR *)alloca(CbFromCchW(cch));
wnsprintfW(pszName, cch, L"@%s,%d", pszResModule, (idsRes * -1));
hr = psf->SetNameOf(NULL, pidlChild, pszName, SHGDN_NORMAL, NULL);
psf->Release();
}
SHFree(pidl);
}
psfDesktop->Release();
}
}
SHCoUninitialize(hrInit);
return hr;
}
// ShellHookProc was mistakenly exported in the original NT SHELL32.DLL when
// it didn't need to be (hookproc's, like wndproc's don't need to be exported
// in the 32-bit world). In order to maintain loadability of a app
// which might have linked to it, we stub it here. If some app ended up really
// using it, then we'll look into a specific fix for that app.
STDAPI_(LONG) ShellHookProc(int code, WPARAM wParam, LPARAM lParam)
{
return 0;
}
// RegisterShellHook - wrapper around RegisterShellHookWindow()/DeregisterShellHookWindow()
// the GetTaskmanWindow() stuff is legacy that I don't think is really needed
HWND g_hwndTaskMan = NULL;
STDAPI_(BOOL) RegisterShellHook(HWND hwnd, BOOL fInstall)
{
BOOL fOk = TRUE;
switch (fInstall)
{
case 0:
// un-installation of shell hooks
g_hwndTaskMan = GetTaskmanWindow();
if (hwnd == g_hwndTaskMan)
{
SetTaskmanWindow(NULL);
}
DeregisterShellHookWindow(hwnd);
return TRUE;
case 3:
// explorer.exe Tray uses this
if (g_hwndTaskMan != NULL)
{
SetTaskmanWindow(NULL);
g_hwndTaskMan = NULL;
}
fOk = SetTaskmanWindow(hwnd);
if (fOk)
{
g_hwndTaskMan = hwnd;
}
RegisterShellHookWindow(hwnd); // install
break;
}
return TRUE;
}
EXTERN_C DWORD g_dwThreadBindCtx;
class CThreadBindCtx : public IBindCtx
{
public:
CThreadBindCtx(IBindCtx *pbc) : _cRef(1) { _pbc = pbc; _pbc->AddRef(); }
~CThreadBindCtx();
// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// *** IBindCtx methods ***
STDMETHODIMP RegisterObjectBound(IUnknown *punk)
{ return _pbc->RegisterObjectBound(punk); }
STDMETHODIMP RevokeObjectBound(IUnknown *punk)
{ return _pbc->RevokeObjectBound(punk); }
STDMETHODIMP ReleaseBoundObjects(void)
{ return _pbc->ReleaseBoundObjects(); }
STDMETHODIMP SetBindOptions(BIND_OPTS *pbindopts)
{ return _pbc->SetBindOptions(pbindopts); }
STDMETHODIMP GetBindOptions(BIND_OPTS *pbindopts)
{ return _pbc->GetBindOptions(pbindopts); }
STDMETHODIMP GetRunningObjectTable(IRunningObjectTable **pprot)
{ return _pbc->GetRunningObjectTable(pprot); }
STDMETHODIMP RegisterObjectParam(LPOLESTR pszKey, IUnknown *punk)
{ return _pbc->RegisterObjectParam(pszKey, punk); }
STDMETHODIMP GetObjectParam(LPOLESTR pszKey, IUnknown **ppunk)
{ return _pbc->GetObjectParam(pszKey, ppunk); }
STDMETHODIMP EnumObjectParam(IEnumString **ppenum)
{ return _pbc->EnumObjectParam(ppenum); }
STDMETHODIMP RevokeObjectParam(LPOLESTR pszKey)
{ return _pbc->RevokeObjectParam(pszKey); }
private:
LONG _cRef;
IBindCtx * _pbc;
};
CThreadBindCtx::~CThreadBindCtx()
{
ATOMICRELEASE(_pbc);
}
HRESULT CThreadBindCtx::QueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CThreadBindCtx, IBindCtx),
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
ULONG CThreadBindCtx::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CThreadBindCtx::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
// clear ourselves out
TlsSetValue(g_dwThreadBindCtx, NULL);
return 0;
}
STDAPI TBCGetBindCtx(BOOL fCreate, IBindCtx **ppbc)
{
HRESULT hr = E_UNEXPECTED;
*ppbc = NULL;
if ((DWORD) -1 != g_dwThreadBindCtx)
{
CThreadBindCtx *ptbc = (CThreadBindCtx *)TlsGetValue(g_dwThreadBindCtx);
if (ptbc)
{
ptbc->AddRef();
*ppbc = SAFECAST(ptbc, IBindCtx *);
hr = S_OK;
}
else if (fCreate)
{
IBindCtx *pbcInner;
hr = CreateBindCtx(0, &pbcInner);
if (SUCCEEDED(hr))
{
hr = E_OUTOFMEMORY;
ptbc = new CThreadBindCtx(pbcInner);
if (ptbc)
{
if (TlsSetValue(g_dwThreadBindCtx, ptbc))
{
*ppbc = SAFECAST(ptbc, IBindCtx *);
hr = S_OK;
}
else
delete ptbc;
}
pbcInner->Release();
}
}
}
return hr;
}
STDAPI TBCRegisterObjectParam(LPCOLESTR pszKey, IUnknown *punk, IBindCtx **ppbcLifetime)
{
IBindCtx *pbc;
HRESULT hr = TBCGetBindCtx(TRUE, &pbc);
if (SUCCEEDED(hr))
{
hr = BindCtx_RegisterObjectParam(pbc, pszKey, punk, ppbcLifetime);
pbc->Release();
}
else
*ppbcLifetime = 0;
return hr;
}
STDAPI TBCGetObjectParam(LPCOLESTR pszKey, REFIID riid, void **ppv)
{
IBindCtx *pbc;
HRESULT hr = TBCGetBindCtx(FALSE, &pbc);
if (SUCCEEDED(hr))
{
IUnknown *punk;
hr = pbc->GetObjectParam((LPOLESTR)pszKey, &punk);
if (SUCCEEDED(hr) )
{
if (ppv)
hr = punk->QueryInterface(riid, ppv);
punk->Release();
}
pbc->Release();
}
return hr;
}
#define TBCENVOBJECT L"ThreadEnvironmentVariables"
STDAPI TBCGetEnvironmentVariable(LPCWSTR pszVar, LPWSTR pszValue, DWORD cchValue)
{
IPropertyBag *pbag;
HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag));
if (SUCCEEDED(hr))
{
hr = SHPropertyBag_ReadStr(pbag, pszVar, pszValue, cchValue);
pbag->Release();
}
return hr;
}
STDAPI TBCSetEnvironmentVariable(LPCWSTR pszVar, LPCWSTR pszValue, IBindCtx **ppbcLifetime)
{
*ppbcLifetime = 0;
IPropertyBag *pbag;
HRESULT hr = TBCGetObjectParam(TBCENVOBJECT, IID_PPV_ARG(IPropertyBag, &pbag));
if (FAILED(hr))
hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &pbag));
if (SUCCEEDED(hr))
{
hr = SHPropertyBag_WriteStr(pbag, pszVar, pszValue);
if (SUCCEEDED(hr))
hr = TBCRegisterObjectParam(TBCENVOBJECT, pbag, ppbcLifetime);
pbag->Release();
}
return hr;
}
// create a stock IExtractIcon handler for a thing that is file like. this is typically
// used by name space extensiosn that display things that are like files in the
// file system. that is the extension, file attributes decrive all that is needed
// for a simple icon extractor
STDAPI SHCreateFileExtractIconW(LPCWSTR pszFile, DWORD dwFileAttributes, REFIID riid, void **ppv)
{
*ppv = NULL;
HRESULT hr = E_FAIL;
SHFILEINFO sfi = {0};
if (SHGetFileInfo(pszFile, dwFileAttributes, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_LARGEICON | SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES))
{
hr = SHCreateDefExtIcon(TEXT("*"), sfi.iIcon, sfi.iIcon, GIL_PERCLASS | GIL_NOTFILENAME, -1, riid, ppv);
DestroyIcon(sfi.hIcon);
}
return hr;
}