windows-nt/Source/XPSP1/NT/shell/shell32/recdocs.cpp

945 lines
28 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1996
//
// File: recdocs.cpp
//
// History - created from recent.c in explorer - ZekeL - 5-MAR-98
// combining functionality in to one place
// now that the desktop lives here.
//---------------------------------------------------------------------------
#include "shellprv.h"
#include "recdocs.h"
#include "fstreex.h"
#include "shcombox.h"
#include "ids.h"
#include <urlhist.h>
#include <runtask.h>
#define DM_RECENTDOCS 0x00000000
#define GETRECNAME(p) ((LPCTSTR)(p))
#define GETRECPIDL(p) ((LPCITEMIDLIST) (((LPBYTE) (p)) + CbFromCch(lstrlen(GETRECNAME(p)) +1)))
#define REGSTR_KEY_RECENTDOCS TEXT("RecentDocs")
#define MAX_RECMRU_BUF (CbFromCch(3 * MAX_PATH)) // Max MRUBuf size
// Used to blow off adding the same file multiple times
TCHAR g_szLastFile[MAX_URL_STRING] = {0};
FILETIME g_ftLastFileCacheUpdate = {0};
STDAPI_(BOOL) SetFolderString(BOOL fCreate, LPCTSTR pszFolder, LPCTSTR pszProvider, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszData);
STDAPI_(void) OpenWithListSoftRegisterProcess(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszProcess);
class CTaskAddDoc : public CRunnableTask
{
public:
CTaskAddDoc();
HRESULT Init(HANDLE hMem, DWORD dwProcId);
// *** pure virtuals ***
virtual STDMETHODIMP RunInitRT(void);
private:
virtual ~CTaskAddDoc();
void _AddToRecentDocs(LPCITEMIDLIST pidlItem, LPCTSTR pszPath);
void _TryDeleteMRUItem(IMruDataList *pmru, DWORD cMax, LPCTSTR pszFileName, LPCITEMIDLIST pidlItem, IMruDataList *pmruOther, BOOL fOverwrite);
LPBYTE _CreateMRUItem(LPCITEMIDLIST pidlItem, LPCTSTR pszItem, DWORD *pcbItem, UINT uFlags);
BOOL _AddDocToRecentAndExtRecent(LPCITEMIDLIST pidlItem, LPCTSTR pszFileName, LPCTSTR pszExt);
void _TryUpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszFolder);
void _UpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszShare);
// private members
HANDLE _hMem;
DWORD _dwProcId;
IMruDataList *_pmruRecent;
DWORD _cMaxRecent;
LPITEMIDLIST _pidlTarget;
};
BOOL ShouldAddToRecentDocs(LPCITEMIDLIST pidl)
{
BOOL fRet = TRUE; // default to true
IQueryAssociations *pqa;
if (SUCCEEDED(SHGetAssociations(pidl, (void **)&pqa)))
{
DWORD dwAttributes, dwSize = sizeof(dwAttributes);
if (SUCCEEDED(pqa->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwAttributes, &dwSize)))
{
fRet = !(dwAttributes & FTA_NoRecentDocs);
}
pqa->Release();
}
return fRet;
}
int RecentDocsComparePidl(const BYTE * p1, const BYTE *p2, int cb)
{
int iRet;
LPCIDFOLDER pidf1 = CFSFolder_IsValidID((LPCITEMIDLIST)p1);
LPCIDFOLDER pidf2 = CFSFolder_IsValidID(GETRECPIDL(p2));
if (pidf1 && pidf2)
{
iRet = CFSFolder_CompareNames(pidf1, pidf2);
}
else
{
ASSERTMSG(0, "Caller shouldn't be passing in bogus data");
// return 0 (equal) if they're both NULL.
iRet = (pidf1 != pidf2);
}
return iRet;
}
CTaskAddDoc::~CTaskAddDoc(void)
{
TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc destroyed", this);
}
CTaskAddDoc::CTaskAddDoc(void) : CRunnableTask(RTF_DEFAULT)
{
TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc created", this);
}
HRESULT CTaskAddDoc::Init( HANDLE hMem, DWORD dwProcId)
{
if (hMem)
{
_hMem = hMem;
_dwProcId = dwProcId;
return S_OK;
}
return E_FAIL;
}
typedef struct {
DWORD dwOffsetPath;
DWORD dwOffsetPidl;
DWORD dwOffsetProcess;
} XMITARD;
LPCTSTR _OffsetToStrValidate(void *px, DWORD dw)
{
LPCTSTR psz = dw ? (LPTSTR)((LPBYTE)px + dw) : NULL;
if (psz && IsBadStringPtr(psz, MAX_PATH))
psz = NULL;
return psz;
}
HRESULT CTaskAddDoc::RunInitRT(void)
{
TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::RunInitRT() running", this);
XMITARD *px = (XMITARD *)SHLockShared(_hMem, _dwProcId);
if (px)
{
LPITEMIDLIST pidl = px->dwOffsetPidl ? (LPITEMIDLIST)((LPBYTE)px+px->dwOffsetPidl) : NULL;
LPCTSTR pszPath = _OffsetToStrValidate(px, px->dwOffsetPath);
LPCTSTR pszProcess = _OffsetToStrValidate(px, px->dwOffsetProcess);
ASSERT(pszPath);
if (pszPath && pszProcess)
OpenWithListSoftRegisterProcess(0, PathFindExtension(pszPath), pszProcess);
_AddToRecentDocs(pidl, pszPath);
SHUnlockShared(px);
SHFreeShared(_hMem, _dwProcId);
}
return S_OK;
}
BOOL GetExtensionClassDescription(LPCTSTR pszFile)
{
LPTSTR pszExt = PathFindExtension(pszFile);
HKEY hk;
if (*pszExt && SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, pszExt, NULL, &hk)))
{
RegCloseKey(hk);
return TRUE;
}
return FALSE;
}
STDAPI_(void) FlushRunDlgMRU(void);
#define MAXRECENT_DEFAULTDOC 10
#define MAXRECENT_MAJORDOC 20
// SRMLF_* flags to pass into CreateSharedRecentMRUList()
#define SRMLF_COMPNAME 0x00000000 // default: compare using the name of the recent file
#define SRMLF_COMPPIDL 0x00000001 // use the pidl in the recent folder
IMruDataList *CreateSharedRecentMRUList(LPCTSTR pszClass, DWORD *pcMax, DWORD dwFlags)
{
IMruDataList *pmru = NULL;
if (SHRestricted(REST_NORECENTDOCSHISTORY))
return NULL;
HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, REGSTR_KEY_RECENTDOCS, TRUE);
if (hk)
{
DWORD cMax;
if (pszClass)
{
// we need to find out how many
if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszClass, TEXT("MajorDoc"), NULL, NULL, NULL))
cMax = MAXRECENT_MAJORDOC;
else
cMax = MAXRECENT_DEFAULTDOC;
}
else
{
// this the root MRU
cMax = SHRestricted(REST_MaxRecentDocs);
// default max docs...
if (cMax < 1)
cMax = MAXRECENTDOCS * MAXRECENT_DEFAULTDOC;
}
if (pcMax)
*pcMax = cMax;
if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_MruLongList, NULL, IID_PPV_ARG(IMruDataList, &pmru))))
{
if (FAILED(pmru->InitData(cMax, MRULISTF_USE_STRCMPIW, hk, pszClass, dwFlags & SRMLF_COMPPIDL ? RecentDocsComparePidl : NULL)))
{
pmru->Release();
pmru = NULL;
}
}
RegCloseKey(hk);
}
return pmru;
}
HRESULT CreateRecentMRUList(IMruDataList **ppmru)
{
*ppmru = CreateSharedRecentMRUList(NULL, NULL, SRMLF_COMPPIDL);
return *ppmru ? S_OK : E_OUTOFMEMORY;
}
//
// _CleanRecentDocs()
// cleans out the recent docs folder and the associate registry keys.
//
void _CleanRecentDocs(void)
{
LPITEMIDLIST pidlTargetLocal = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
if (pidlTargetLocal)
{
TCHAR szDir[MAX_PATH];
// first, delete all the files
SHFILEOPSTRUCT sFileOp =
{
NULL,
FO_DELETE,
szDir,
NULL,
FOF_NOCONFIRMATION | FOF_SILENT,
};
SHGetPathFromIDList(pidlTargetLocal, szDir);
szDir[lstrlen(szDir) +1] = 0; // double null terminate
SHFileOperation(&sFileOp);
ILFree(pidlTargetLocal);
pidlTargetLocal = SHCloneSpecialIDList(NULL, CSIDL_NETHOOD, TRUE);
if (pidlTargetLocal)
{
// now we take care of cleaning out the nethood
// we have to more careful, cuz we let other people
// add their own stuff in here.
IMruDataList *pmru = CreateSharedRecentMRUList(TEXT("NetHood"), NULL, SRMLF_COMPPIDL);
if (pmru)
{
IShellFolder* psf;
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlTargetLocal, &psf))))
{
BOOL fUpdate = FALSE;
int iItem = 0;
LPITEMIDLIST pidlItem;
ASSERT(psf);
while (SUCCEEDED(RecentDocs_Enum(pmru, iItem++, &pidlItem)))
{
ASSERT(pidlItem);
STRRET str;
if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &str))
&& SUCCEEDED(StrRetToBuf(&str, pidlItem, szDir, ARRAYSIZE(szDir))))
{
szDir[lstrlen(szDir) +1] = 0; // double null terminate
SHFileOperation(&sFileOp);
}
ILFree(pidlItem);
}
if (fUpdate)
SHChangeNotify(SHCNE_UPDATEDIR, 0, (void *)pidlTargetLocal, NULL);
psf->Release();
}
pmru->Release();
}
ILFree(pidlTargetLocal);
}
// force the recreation of the recent folder.
SHGetFolderPath(NULL, CSIDL_RECENT | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szDir);
// now delete the registry stuff
HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, FALSE);
if (hk)
{
SHDeleteKey(hk, REGSTR_KEY_RECENTDOCS);
RegCloseKey(hk);
}
SHChangeNotifyHandleEvents();
}
FlushRunDlgMRU();
ENTERCRITICAL;
g_szLastFile[0] = 0;
g_ftLastFileCacheUpdate.dwLowDateTime = 0;
g_ftLastFileCacheUpdate.dwHighDateTime = 0;
LEAVECRITICAL;
return;
}
//
// WARNING - _TryDeleteMRUItem() returns an allocated string that must be freed
//
void CTaskAddDoc::_TryDeleteMRUItem(IMruDataList *pmru, DWORD cMax, LPCTSTR pszFileName, LPCITEMIDLIST pidlItem, IMruDataList *pmruOther, BOOL fOverwrite)
{
BYTE buf[MAX_RECMRU_BUF] = {0};
DWORD cbItem = CbFromCch(lstrlen(pszFileName) + 1);
int iItem;
if (!fOverwrite || FAILED(pmru->FindData((BYTE *)pszFileName, cbItem, &iItem)))
{
//
// if iItem is not -1 then it is already existing item that we will replace.
// if it is -1 then we need to point iItem to the last in the list.
// torch the last one if we have the max number of items in the list.
// default to success, cuz if we dont find it we dont need to delete it
iItem = cMax - 1;
}
// if we cannot get it in order to delete it,
// then we will not overwrite the item.
if (SUCCEEDED(pmru->GetData(iItem, buf, sizeof(buf))))
{
// convert the buf into the last segment of the pidl
LPITEMIDLIST pidlFullLink = ILCombine(_pidlTarget, GETRECPIDL(buf));
if (pidlFullLink)
{
// This is semi-gross, but some link types like calling cards are the
// actual data. If we delete and recreate they lose their info for the
// run. We will detect this by knowing that their pidl will be the
// same as the one we are deleting...
if (!ILIsEqual(pidlFullLink, pidlItem))
{
TCHAR sz[MAX_PATH];
// now remove out link to it
SHGetPathFromIDList(pidlFullLink, sz);
Win32DeleteFile(sz);
TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::_TryDeleteMRUItem() deleting '%s'", this, sz);
if (pmruOther)
{
// deleted a shortcut,
// need to try and remove it from the pmruOther...
if (SUCCEEDED(pmruOther->FindData((BYTE *)GETRECNAME(buf), CbFromCch(lstrlen(GETRECNAME(buf)) +1), &iItem)))
pmruOther->Delete(iItem);
}
}
ILFree(pidlFullLink);
}
}
}
// in:
// pidlItem - full IDList for the item being added
// pszItem - name (file spec) of the item (used in the display to the user)
// uFlags - SHCL_ flags
LPBYTE CTaskAddDoc::_CreateMRUItem(LPCITEMIDLIST pidlItem, LPCTSTR pszItem,
DWORD *pcbOut, UINT uFlags)
{
TCHAR sz[MAX_PATH];
LPBYTE pitem = NULL;
// create the new one
if (SHGetPathFromIDList(_pidlTarget, sz))
{
LPITEMIDLIST pidlFullLink;
if (SUCCEEDED(CreateLinkToPidl(pidlItem, sz, &pidlFullLink, uFlags)) &&
pidlFullLink)
{
LPCITEMIDLIST pidlLinkLast = ILFindLastID(pidlFullLink);
int cbLinkLast = ILGetSize(pidlLinkLast);
DWORD cbItem = CbFromCch(lstrlen(pszItem) + 1);
pitem = (LPBYTE) LocalAlloc(NONZEROLPTR, cbItem + cbLinkLast);
if (pitem)
{
memcpy( pitem, pszItem, cbItem );
memcpy( pitem + cbItem, pidlLinkLast, cbLinkLast);
*pcbOut = cbItem + cbLinkLast;
}
ILFree(pidlFullLink);
}
}
return pitem;
}
HRESULT RecentDocs_Enum(IMruDataList *pmru, int iItem, LPITEMIDLIST *ppidl)
{
BYTE buf[MAX_RECMRU_BUF] = {0};
*ppidl = NULL;
if (SUCCEEDED(pmru->GetData(iItem, buf, sizeof(buf))))
{
*ppidl = ILClone(GETRECPIDL(buf));
}
return *ppidl ? S_OK : E_FAIL;
}
BOOL CTaskAddDoc::_AddDocToRecentAndExtRecent(LPCITEMIDLIST pidlItem, LPCTSTR pszFileName,
LPCTSTR pszExt)
{
DWORD cbItem = CbFromCch(lstrlen(pszFileName) + 1);
DWORD cMax;
IMruDataList *pmru = CreateSharedRecentMRUList(pszExt, &cMax, SRMLF_COMPNAME);
_TryDeleteMRUItem(_pmruRecent, _cMaxRecent, pszFileName, pidlItem, pmru, TRUE);
LPBYTE pitem = _CreateMRUItem(pidlItem, pszFileName, &cbItem, 0);
if (pitem)
{
_pmruRecent->AddData(pitem, cbItem, NULL);
if (pmru)
{
// we dont want to delete the file if it already existed, because
// the TryDelete on the RecentMRU would have already done that
// we only want to delete if we have some overflow from the ExtMRU
_TryDeleteMRUItem(pmru, cMax, pszFileName, pidlItem, _pmruRecent, FALSE);
// can reuse the already created item to this mru
pmru->AddData(pitem, cbItem, NULL);
pmru->Release();
}
LocalFree(pitem);
}
// its been freed but not nulled out...
return (pitem != NULL);
}
//
// WARNING: UpdateNetHood() changes _pidlTarget to the NetHood then frees it!
//
void CTaskAddDoc::_UpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszShare)
{
if (SHRestricted(REST_NORECENTDOCSNETHOOD))
return;
// need to add this boy to the Network Places
LPITEMIDLIST pidl = ILCreateFromPath(pszShare);
if (pidl)
{
//
// NOTE - must verify parentage here - ZekeL - 27-MAY-99
// http servers exist in both the webfolders namespace
// and the Internet namespace. thus we must make sure
// that what ever parent the folder had, the share has
// the same one.
//
if (ILIsParent(pidl, pidlFolder, FALSE))
{
ASSERT(_pidlTarget);
ILFree(_pidlTarget);
_pidlTarget = SHCloneSpecialIDList(NULL, CSIDL_NETHOOD, TRUE);
if (_pidlTarget)
{
DWORD cMax;
IMruDataList *pmru = CreateSharedRecentMRUList(TEXT("NetHood"), &cMax, SRMLF_COMPNAME);
if (pmru)
{
_TryDeleteMRUItem(pmru, cMax, pszShare, pidl, NULL, TRUE);
DWORD cbItem = CbFromCch(lstrlen(pszShare) + 1);
// SHCL_NOUNIQUE - if there is already a shortcut with the same name,
// just overwrite it; this avoids pointless duplicates in nethood
LPBYTE pitem = _CreateMRUItem(pidl, pszShare, &cbItem, SHCL_MAKEFOLDERSHORTCUT | SHCL_NOUNIQUE);
if (pitem)
{
pmru->AddData(pitem, cbItem, NULL);
LocalFree(pitem);
}
pmru->Release();
}
ILFree(_pidlTarget);
_pidlTarget = NULL;
}
}
ILFree(pidl);
}
}
BOOL _IsPlacesFolder(LPCTSTR pszFolder)
{
static const UINT places[] = {
CSIDL_PERSONAL,
CSIDL_DESKTOPDIRECTORY,
CSIDL_COMMON_DESKTOPDIRECTORY,
CSIDL_NETHOOD,
CSIDL_FAVORITES,
};
return PathIsOneOf(pszFolder, places, ARRAYSIZE(places));
}
void _AddToUrlHistory(LPCTSTR pszPath)
{
ASSERT(pszPath);
WCHAR szUrl[MAX_URL_STRING];
DWORD cchUrl = ARRAYSIZE(szUrl);
// the URL parsing APIs tolerate same in/out buffer
if (SUCCEEDED(UrlCreateFromPathW(pszPath, szUrl, &cchUrl, 0)))
{
IUrlHistoryStg *puhs;
if (SUCCEEDED(CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IUrlHistoryStg, &puhs))))
{
ASSERT(puhs);
puhs->AddUrl(szUrl, NULL, 0);
puhs->Release();
}
}
}
void CTaskAddDoc::_TryUpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszFolder)
{
TCHAR sz[MAX_URL_STRING];
DWORD cch = SIZECHARS(sz);
BOOL fUpdate = FALSE;
// changing szFolder, and changing _pidlTarget here...
// if this is an URL or a UNC share add it to the nethood
if (UrlIs(pszFolder, URLIS_URL)
&& !UrlIs(pszFolder, URLIS_OPAQUE)
&& SUCCEEDED(UrlCombine(pszFolder, TEXT("/"), sz, &cch, 0)))
fUpdate = TRUE;
else if (PathIsUNC(pszFolder)
&& StrCpyN(sz, pszFolder, cch)
&& PathStripToRoot(sz))
fUpdate = TRUE;
if (fUpdate)
_UpdateNetHood(pidlFolder, sz);
}
//-----------------------------------------------------------------
//
// Add the named file to the Recently opened MRU list, that is used
// by the shell to display the recent menu of the tray.
// this registry will hold two pidls: the target pointing to followed by
// the pidl of the link created pointing it. In both cases,
// only the last item id is stored. (we may want to change this... but
// then again, we may not)
void CTaskAddDoc::_AddToRecentDocs(LPCITEMIDLIST pidlItem, LPCTSTR pszItem)
{
TCHAR szUnescaped[MAX_PATH];
LPTSTR pszFileName;
// if these are NULL the caller meant to call _CleanRecentDocs()
ASSERT(pszItem && *pszItem);
TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::_AddToRecentDocs() called for '%s'", this, pszItem);
// allow only classes with default commands
//
// dont add if:
// it is RESTRICTED
// it is in the temporary directory
// it actually has a file name
// it can be shell exec'd with "open" verb
//
if ( (SHRestricted(REST_NORECENTDOCSHISTORY)) ||
(PathIsTemporary(pszItem)) ||
(!(pszFileName = PathFindFileName(pszItem))) ||
(!*pszFileName) ||
(!GetExtensionClassDescription(pszFileName))
)
return;
// pretty up the URL file names.
if (UrlIs(pszItem, URLIS_URL))
{
StrCpyN(szUnescaped, pszFileName, SIZECHARS(szUnescaped));
UrlUnescapeInPlace(szUnescaped, 0);
pszFileName = szUnescaped;
}
// otherwise we try our best.
ASSERT(!_pidlTarget);
_pidlTarget = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
if (_pidlTarget)
{
_pmruRecent = CreateSharedRecentMRUList(NULL, &_cMaxRecent, SRMLF_COMPNAME);
if (_pmruRecent)
{
if (_AddDocToRecentAndExtRecent(pidlItem, pszFileName, PathFindExtension(pszFileName)))
{
_AddToUrlHistory(pszItem);
// get the folder and do it to the folder
LPITEMIDLIST pidlFolder = ILClone(pidlItem);
if (pidlFolder)
{
ILRemoveLastID(pidlFolder);
// if it is a folder we already have quick
// access to from the shell, dont put it in here
TCHAR szFolder[MAX_URL_STRING];
if (SUCCEEDED(SHGetNameAndFlags(pidlFolder, SHGDN_FORPARSING, szFolder, SIZECHARS(szFolder), NULL))
&& !_IsPlacesFolder(szFolder))
{
// get the friendly name for the folder
TCHAR szTitle[MAX_PATH];
if (FAILED(SHGetNameAndFlags(pidlFolder, SHGDN_NORMAL, szTitle, SIZECHARS(szTitle), NULL)))
StrCpyN(szTitle, PathFindFileName(szFolder), ARRAYSIZE(szTitle));
_AddDocToRecentAndExtRecent(pidlFolder, szTitle, TEXT("Folder"));
_TryUpdateNetHood(pidlFolder, szFolder);
}
ILFree(pidlFolder);
}
}
_pmruRecent->Release();
_pmruRecent = NULL;
}
//cleanup
if (_pidlTarget)
{
ILFree(_pidlTarget);
_pidlTarget = NULL;
}
}
SHChangeNotifyHandleEvents();
}
// This cache helps winstone!
// The 1 minute timeout is incase another process cleared recent docs, or filled it
// to capacity & scrolled out our cached item.
#define FT_ONEMINUTE (10000000*60)
BOOL CheckIfFileIsCached(LPCTSTR pszItem)
{
BOOL bRet = FALSE;
ENTERCRITICAL;
if (StrCmp(pszItem, g_szLastFile) == 0)
{
FILETIME ftNow;
GetSystemTimeAsFileTime(&ftNow);
// Pull one minute off the current time, then compare to cache time
DecrementFILETIME(&ftNow, FT_ONEMINUTE);
// if the cache'd time is greater than 1 minute ago, use cache
if (CompareFileTime(&g_ftLastFileCacheUpdate, &ftNow) >= 0)
bRet = TRUE;
}
LEAVECRITICAL;
return bRet;
}
void AddToRecentDocs(LPCITEMIDLIST pidl, LPCTSTR pszItem)
{
HWND hwnd = GetShellWindow();
// Check to see if we just added the same file to recent docs.
// or this is an executeable
// or something else that shouldnt be added
if (!CheckIfFileIsCached(pszItem)
&& (!PathIsExe(pszItem))
&& (ShouldAddToRecentDocs(pidl))
&& (hwnd))
{
DWORD cbSizePidl = ILGetSize(pidl);
DWORD cbSizePath = CbFromCch(lstrlen(pszItem) + 1);
XMITARD *px;
DWORD dwProcId, dwOffset;
HANDLE hARD;
TCHAR szApp[MAX_PATH]; // name of the app which is calling us
DWORD cbSizeApp;
DWORD cbSizePidlRound, cbSizePathRound, cbSizeAppRound;
GetWindowThreadProcessId(hwnd, &dwProcId);
if (GetModuleFileName(NULL, szApp, ARRAYSIZE(szApp)) && szApp[0])
cbSizeApp = CbFromCch(1 + lstrlen(szApp));
else
cbSizeApp = 0;
cbSizePidlRound = ROUNDUP(cbSizePidl,4);
cbSizePathRound = ROUNDUP(cbSizePath,4);
cbSizeAppRound = ROUNDUP(cbSizeApp,4);
hARD = SHAllocShared(NULL, sizeof(XMITARD)+cbSizePathRound+cbSizePidlRound+cbSizeAppRound, dwProcId);
if (!hARD)
return; // Well, we are going to miss one, sorry.
px = (XMITARD *)SHLockShared(hARD,dwProcId);
if (!px)
{
SHFreeShared(hARD,dwProcId);
return; // Well, we are going to miss one, sorry.
}
px->dwOffsetPidl = 0;
px->dwOffsetPath = 0;
px->dwOffsetProcess = 0;
dwOffset = sizeof(XMITARD);
{
px->dwOffsetPath = dwOffset;
memcpy((LPBYTE)px + dwOffset, pszItem, cbSizePath);
dwOffset += cbSizePathRound;
}
{
px->dwOffsetPidl = dwOffset;
memcpy((LPBYTE)px + dwOffset, pidl, cbSizePidl);
dwOffset += cbSizePidlRound;
}
if (cbSizeApp)
{
px->dwOffsetProcess = dwOffset;
memcpy((LPBYTE)px + dwOffset, szApp, cbSizeApp);
}
SHUnlockShared(px);
PostMessage(hwnd, CWM_ADDTORECENT, (WPARAM)hARD, (LPARAM)dwProcId);
ENTERCRITICAL;
StrCpyN(g_szLastFile, pszItem, ARRAYSIZE(g_szLastFile));
GetSystemTimeAsFileTime(&g_ftLastFileCacheUpdate);
LEAVECRITICAL;
}
}
#if 0
void DisplayRecentDebugMessage(LPTSTR psz, BOOL bPidl)
{
TCHAR szTmp[1024];
TCHAR szFN[MAX_PATH];
GetModuleFileName(NULL, szFN, ARRAYSIZE(szFN));
wsprintf(szTmp, TEXT("[%d], process %s: AddToRecentDocs(%s) by %s\n"),
GetTickCount(), szFN, psz,
(bPidl)?TEXT("pidl"):TEXT("path"));
OutputDebugString(szTmp);
}
#else
#define DisplayRecentDebugMessage(x,y)
#endif
HRESULT _ParseRecentDoc(LPCWSTR psz, LPITEMIDLIST *ppidl)
{
BINDCTX_PARAM rgParams[] =
{
{ STR_PARSE_TRANSLATE_ALIASES, NULL},
{ STR_PARSE_PREFER_FOLDER_BROWSING, NULL},
};
IBindCtx *pbc;
HRESULT hr = BindCtx_RegisterObjectParams(NULL, rgParams, ARRAYSIZE(rgParams), &pbc);
if (SUCCEEDED(hr))
{
hr = SHParseDisplayName(psz, pbc, ppidl, 0, 0);
pbc->Release();
if (FAILED(hr))
{
// we need to fallback to a simple parsing
IBindCtx *pbcSimple;
hr = SHCreateFileSysBindCtx(NULL, &pbcSimple);
if (SUCCEEDED(hr))
{
hr = BindCtx_RegisterObjectParams(pbcSimple, rgParams, ARRAYSIZE(rgParams), &pbc);
if (SUCCEEDED(hr))
{
hr = SHParseDisplayName(psz, pbc, ppidl, 0, 0);
pbc->Release();
}
pbcSimple->Release();
}
}
}
return hr;
}
//
// put things in the shells recent docs list for the start menu
//
// in:
// uFlags SHARD_ (shell add recent docs) flags
// pv LPCSTR or LPCITEMIDLIST (path or pidl indicated by uFlags)
// may be NULL, meaning clear the recent list
//
STDAPI_(void) SHAddToRecentDocs(UINT uFlags, LPCVOID pv)
{
TCHAR szTemp[MAX_URL_STRING]; // for double null
TraceMsg(DM_RECENTDOCS, "SHAddToRecentDocs() called with %d, [%X]", uFlags, pv);
if (pv == NULL) // we should nuke all recent docs.
{
// we do this synchronously
_CleanRecentDocs();
return;
}
if (SHRestricted(REST_NORECENTDOCSHISTORY))
// Don't bother tracking recent documents if restriction is set
// for privacy.
return;
switch (uFlags)
{
case SHARD_PIDL:
// pv is a LPCITEMIDLIST (pidl)
if (SUCCEEDED(SHGetNameAndFlags((LPCITEMIDLIST)pv, SHGDN_FORPARSING, szTemp, SIZECHARS(szTemp), NULL)))
{
DisplayRecentDebugMessage((LPTSTR)pv, TRUE);
AddToRecentDocs((LPCITEMIDLIST)pv, szTemp);
}
break;
case SHARD_PATHA:
// pv is an ANSI path
SHAnsiToUnicode((LPCSTR)pv, szTemp, ARRAYSIZE(szTemp));
pv = szTemp;
// fall through to SHARD_PATHW;
case SHARD_PATHW:
{
// pv is a UNICODE path
LPITEMIDLIST pidl;
if (SUCCEEDED(_ParseRecentDoc((LPCWSTR)pv, &pidl)))
{
DisplayRecentDebugMessage((LPTSTR)pv, FALSE);
AddToRecentDocs(pidl, (LPCTSTR)pv);
ILFree(pidl);
}
break;
}
default:
ASSERTMSG(FALSE, "SHAddToRecent() called with invalid params");
break;
}
}
STDAPI CTaskAddDoc_Create(HANDLE hMem, DWORD dwProcId, IRunnableTask **pptask)
{
HRESULT hres;
CTaskAddDoc *ptad = new CTaskAddDoc();
if (ptad)
{
hres = ptad->Init(hMem, dwProcId);
if (SUCCEEDED(hres))
hres = ptad->QueryInterface(IID_PPV_ARG(IRunnableTask, pptask));
ptad->Release();
}
else
hres = E_OUTOFMEMORY;
return hres;
}
STDAPI RecentDocs_GetDisplayName(LPCITEMIDLIST pidl, LPTSTR pszName, DWORD cchName)
{
IMruDataList *pmru;
HRESULT hr = CreateRecentMRUList(&pmru);
if (SUCCEEDED(hr))
{
int iItem;
hr = pmru->FindData((BYTE *)pidl, ILGetSize(pidl), &iItem);
if (SUCCEEDED(hr))
{
BYTE buf[MAX_RECMRU_BUF];
hr = pmru->GetData(iItem, buf, sizeof(buf));
if (SUCCEEDED(hr))
{
StrCpyN(pszName, GETRECNAME(buf), cchName);
}
}
pmru->Release();
}
return hr;
}