mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2543 lines
78 KiB
2543 lines
78 KiB
#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;
|
|
}
|