2020-09-30 17:12:32 +02:00

808 lines
24 KiB
C++

// 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 "shobjprv.h"
#include "recdocs.h"
#include "fstreex.h"
#include "shcombox.h"
#include "ids.h"
#include <urlhist.h>
#include <runtask.h>
#define DM_RECENTDOCS 0x80000000
#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
STDAPI RecentDocs_Install(BOOL bInstall)
{
TCHAR szPath[MAX_PATH];
// get the path to the favorites folder. Create it if it is missing and we
// are installing, otherwise don't bother.
if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_RECENT, bInstall)) {
if (bInstall) {
SHFOLDERCUSTOMSETTINGS fcs = {sizeof(fcs), FCSM_INFOTIP | FCSM_ICONFILE, 0};
TCHAR szInfoTip[128];
TCHAR szIconFile[MAX_PATH];
//Get the infotip for the recent files Folder
LoadString(HINST_THISDLL, IDS_RECENT, szInfoTip, ARRAYSIZE(szInfoTip));
fcs.pszInfoTip = szInfoTip;
// Get the IconFile and IconIndex for the Recent Files Folder
GetSystemDirectory(szIconFile, ARRAYSIZE(szIconFile));
PathAppend(szIconFile, TEXT("shdocvw.dll"));
fcs.pszIconFile = szIconFile;
fcs.iIconIndex = -20785; // IDI_HISTFOLDER
SHGetSetFolderCustomSettings(&fcs, szPath, FCS_WRITE);
}
}
return NOERROR;
}
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(HANDLE hmru, DWORD cMax, LPCTSTR pszFileName, LPCITEMIDLIST pidlItem, HANDLE hmruOther, 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;
HANDLE _hmruRecent;
DWORD _cMaxRecent;
LPITEMIDLIST _pidlTarget;
};
BOOL ShouldAddToRecentDocs(LPCITEMIDLIST pidl)
{
HKEY hk;
BOOL fRet = TRUE; // default to true
SHGetClassKey(pidl, &hk, NULL);
if (hk) {
fRet = !(GetFileTypeAttributes(hk) & FTA_NoRecentDocs);
SHCloseClassKey(hk);
}
return fRet;
}
int cdecl RecentDocsCompareName(const void* p1, const void* p2, size_t cb)
{
return lstrcmpi(GETRECNAME(p2), (LPCTSTR)p1);
}
int cdecl RecentDocsComparePidl(const void* p1, const void* p2, size_t cb)
{
// p2 is the one that is in the MRU and p1 is the one that we are passing in...
return !ILIsEqual(GETRECPIDL(p2), (LPCITEMIDLIST)p1);
}
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 _ARD {
DWORD dwOffsetPath;
DWORD dwOffsetPidl;
} XMITARD, * PXMITARD;
HRESULT CTaskAddDoc::RunInitRT(void)
{
TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::RunInitRT() running", this);
PXMITARD px = (PXMITARD)SHLockShared(_hMem, _dwProcId);
if (px) {
LPITEMIDLIST pidl = px->dwOffsetPidl ? (LPITEMIDLIST)((LPBYTE)px + px->dwOffsetPidl) : NULL;
LPTSTR pszPath = px->dwOffsetPath ? (LPTSTR)((LPBYTE)px + px->dwOffsetPath) : NULL;
ASSERT(pszPath);
_AddToRecentDocs(pidl, pszPath);
SHUnlockShared(px);
SHFreeShared(_hMem, _dwProcId);
}
return S_OK;
}
BOOL GetExtensionClassDescription(LPCTSTR lpszFile)
{
LPTSTR lpszExt = PathFindExtension(lpszFile);
if (*lpszExt) {
TCHAR szClass[128];
TCHAR szDescription[MAX_PATH];
long cchClass = SIZEOF(szClass);
if (SHRegQueryValue(HKEY_CLASSES_ROOT, lpszExt, szClass, &cchClass) != ERROR_SUCCESS) {
// if this fails, use the extension cause it might be a pseudoclass
lstrcpyn(szClass, lpszExt, ARRAYSIZE(szClass));
}
return GetClassDescription(HKEY_CLASSES_ROOT, szClass, szDescription, ARRAYSIZE(szDescription),
GCD_MUSTHAVEOPENCMD | GCD_ALLOWPSUDEOCLASSES);
}
return FALSE;
}
STDAPI_(void) FlushRunDlgMRU(void);
// _CleanRecentDocs()
// cleans out the recent docs folder and the associate registry keys.
void _CleanRecentDocs(void)
{
LPITEMIDLIST pidlTargetLocal = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
if (pidlTargetLocal) {
HKEY hkey;
TCHAR szDir[MAX_PATH];
// first, delete all the files
SHFILEOPSTRUCT sFileOp =
{
NULL,
FO_DELETE,
szDir,
NULL,
FOF_NOCONFIRMATION | FOF_SILENT,
};
SHGetPathFromIDList(pidlTargetLocal, szDir);
PathAppend(szDir, c_szStarDotStar);
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.
HANDLE hmru = CreateSharedRecentMRUList(TEXT("NetHood"), NULL, SRMLF_COMPPIDL);
if (hmru) {
IShellFolder* psf;
if (SUCCEEDED(SHBindToObject(NULL, IID_IShellFolder, pidlTargetLocal, (void**)&psf))) {
BOOL fUpdate = FALSE;
int iItem = 0;
LPITEMIDLIST pidlItem;
ASSERT(psf);
while (-1 != EnumSharedRecentMRUList(hmru, iItem++, NULL, &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();
}
FreeMRUList(hmru);
}
ILFree(pidlTargetLocal);
}
// now delete the registry stuff
hkey = SHGetExplorerHkey(HKEY_CURRENT_USER, TRUE);
if (hkey) {
SHDeleteKey(hkey, REGSTR_KEY_RECENTDOCS);
RegCloseKey(hkey);
}
// reinit the the folder.
RecentDocs_Install(TRUE);
SHChangeNotifyHandleEvents();
}
FlushRunDlgMRU();
return;
}
// WARNING - _TryDeleteMRUItem() returns an allocated string that must be freed
void CTaskAddDoc::_TryDeleteMRUItem(HANDLE hmru, DWORD cMax, LPCTSTR pszFileName, LPCITEMIDLIST pidlItem, HANDLE hmruOther, BOOL fOverwrite)
{
BYTE buf[MAX_RECMRU_BUF] = {0};
DWORD cbItem = CbFromCch(lstrlen(pszFileName) + 1);
int iItem = fOverwrite ? FindMRUData(hmru, pszFileName, cbItem, NULL) : -1;
// 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.
if (iItem == -1) {
// 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 (EnumMRUList(hmru, iItem, buf, SIZEOF(buf)) != -1) {
// 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 (hmruOther) {
// deleted a shortcut,
// need to try and remove it from the hmruOther...
iItem = FindMRUData(hmruOther, GETRECNAME(buf), CbFromCch(lstrlen(GETRECNAME(buf)) + 1), NULL);
if (iItem != -1)
DelMRUString(hmruOther, 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(LPTR, cbItem + cbLinkLast);
if (pitem) {
memcpy(pitem, pszItem, cbItem);
memcpy(pitem + cbItem, pidlLinkLast, cbLinkLast);
*pcbOut = cbItem + cbLinkLast;
}
ILFree(pidlFullLink);
}
}
return pitem;
}
int EnumSharedRecentMRUList(HANDLE hmru, int iItem, LPTSTR* ppszName, LPITEMIDLIST* ppidl)
{
BYTE buf[MAX_RECMRU_BUF] = {0};
int iRet;
// if both out params are NULL or iItem < 0 then this is a query for the size
// of the MRU list...
if (ppszName)
*ppszName = NULL;
if (ppidl)
*ppidl = NULL;
if (iItem < 0 || (!ppszName && !ppidl))
iRet = EnumMRUList(hmru, -1, NULL, 0);
else if (-1 != (iRet = EnumMRUList(hmru, iItem, buf, SIZEOF(buf)))) {
if (ppszName) {
*ppszName = StrDup(GETRECNAME(buf));
if (!*ppszName)
iRet = -1;
} else if (ppidl) {
*ppidl = ILClone(GETRECPIDL(buf));
if (!*ppidl)
iRet = -1;
}
}
return iRet;
}
#define MAXRECENT_DEFAULTDOC 10
#define MAXRECENT_MAJORDOC 20
HANDLE CreateSharedRecentMRUList(LPCTSTR pszClass, DWORD* pcMax, DWORD dwFlags)
{
if (SHRestricted(REST_NORECENTDOCSHISTORY))
return NULL;
DWORD cMax;
MRUCMPDATAPROC procCompare = RecentDocsCompareName;
TCHAR szKey[MAX_PATH];
LPCTSTR pszKey = REGSTR_PATH_EXPLORER TEXT("\\") REGSTR_KEY_RECENTDOCS;
if (pszClass) {
// want to use the pszExt as a sub key
lstrcpy(szKey, pszKey);
StrCatBuff(szKey, TEXT("\\"), SIZECHARS(szKey));
StrCatBuff(szKey, pszClass, SIZECHARS(szKey));
pszKey = szKey;
// we need to find out how many
DWORD dwType = REG_DWORD, dwMajor = 0, cbSize = SIZEOF(cbSize);
SHGetValue(HKEY_CLASSES_ROOT, pszClass, TEXT("MajorDoc"), &dwType, (LPVOID)&dwMajor, &cbSize);
cMax = dwMajor ? MAXRECENT_MAJORDOC : MAXRECENT_DEFAULTDOC;
} else {
// this the root MRU
cMax = SHRestricted(REST_MaxRecentDocs);
// default max docs...
if (cMax < 1)
cMax = MAXRECENTDOCS * MAXRECENT_DEFAULTDOC;
}
if (dwFlags & SRMLF_COMPPIDL)
procCompare = RecentDocsComparePidl;
MRUDATAINFO mi = {
SIZEOF(MRUDATAINFO),
cMax,
MRU_BINARY | MRU_CACHEWRITE,
HKEY_CURRENT_USER,
pszKey,
procCompare
};
if (pcMax)
*pcMax = cMax;
return CreateMRUList((MRUINFO*)&mi);
}
BOOL CTaskAddDoc::_AddDocToRecentAndExtRecent(LPCITEMIDLIST pidlItem, LPCTSTR pszFileName,
LPCTSTR pszExt)
{
DWORD cbItem = CbFromCch(lstrlen(pszFileName) + 1);
DWORD cMax;
HANDLE hmru = CreateSharedRecentMRUList(pszExt, &cMax, SRMLF_COMPNAME);
_TryDeleteMRUItem(_hmruRecent, _cMaxRecent, pszFileName, pidlItem, hmru, TRUE);
LPBYTE pitem = _CreateMRUItem(pidlItem, pszFileName, &cbItem, 0);
if (pitem) {
AddMRUData(_hmruRecent, pitem, cbItem);
if (hmru) {
// 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(hmru, cMax, pszFileName, pidlItem, _hmruRecent, FALSE);
// can reuse the already created item to this mru
AddMRUData(hmru, pitem, cbItem);
FreeMRUList(hmru);
}
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) {
// BUGBUG - 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;
HANDLE hmru = CreateSharedRecentMRUList(TEXT("NetHood"), &cMax, SRMLF_COMPNAME);
if (hmru) {
_TryDeleteMRUItem(hmru, cMax, pszShare, pidl, NULL, TRUE);
DWORD cbItem = CbFromCch(lstrlen(pszShare) + 1);
LPBYTE pitem = _CreateMRUItem(pidl, pszShare, &cbItem, SHCL_MAKEFOLDERSHORTCUT);
if (pitem) {
AddMRUData(hmru, pitem, cbItem);
LocalFree(pitem);
}
FreeMRUList(hmru);
}
ILFree(_pidlTarget);
_pidlTarget = NULL;
}
}
ILFree(pidl);
}
}
BOOL PathIsOneOf(const UINT rgFolders[], LPCTSTR pszFolder);
BOOL _IsPlacesFolder(LPCTSTR pszFolder)
{
static const UINT places[] = {
CSIDL_PERSONAL,
CSIDL_DESKTOPDIRECTORY,
CSIDL_COMMON_DESKTOPDIRECTORY,
CSIDL_NETHOOD,
CSIDL_FAVORITES,
(UINT)-1 // terminator
};
return PathIsOneOf(places, pszFolder);
}
void _AddToUrlHistory(LPCTSTR pszPath)
{
ASSERT(pszPath);
WCHAR szUrl[MAX_URL_STRING];
DWORD cchUrl = SIZECHARS(szUrl);
SHTCharToUnicode(pszPath, szUrl, cchUrl);
// the URL parsing APIs tolerate same in/out buffer
if (SUCCEEDED(UrlCreateFromPathW(szUrl, szUrl, &cchUrl, 0))) {
IUrlHistoryStg* puhs;
if (SUCCEEDED(CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER, IID_IUrlHistoryStg, (void**)&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) {
_hmruRecent = CreateSharedRecentMRUList(NULL, &_cMaxRecent, SRMLF_COMPNAME);
if (_hmruRecent) {
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);
}
SHChangeNotifyHandleEvents();
}
FreeMRUList(_hmruRecent);
_hmruRecent = NULL;
}
//cleanup
if (_pidlTarget) {
ILFree(_pidlTarget);
_pidlTarget = NULL;
}
}
}
STDAPI_(void) OpenWithListSoftRegisterProcess(DWORD dwFlags, LPCTSTR pszExt);
void AddToRecentDocs(LPCITEMIDLIST pidl, LPCTSTR pszItem)
{
HWND hwnd;
DWORD cbSizePidl, cbSizePath;
ASSERT(pidl && pszItem);
if (!ShouldAddToRecentDocs(pidl))
return;
OpenWithListSoftRegisterProcess(0, PathFindExtension(pszItem));
cbSizePidl = ILGetSize(pidl);
cbSizePath = CbFromCch(lstrlen(pszItem) + 1);
hwnd = GetShellWindow();
if (hwnd) {
PXMITARD px;
DWORD dwProcId, dwOffset;
HANDLE hARD;
GetWindowThreadProcessId(hwnd, &dwProcId);
hARD = SHAllocShared(NULL, SIZEOF(XMITARD) + cbSizePath + cbSizePidl, dwProcId);
if (!hARD)
return; // Well, we are going to miss one, sorry.
px = (PXMITARD)SHLockShared(hARD, dwProcId);
if (!px) {
SHFreeShared(hARD, dwProcId);
return; // Well, we are going to miss one, sorry.
}
px->dwOffsetPidl = 0;
px->dwOffsetPath = 0;
dwOffset = SIZEOF(XMITARD);
if (pszItem) {
px->dwOffsetPath = dwOffset;
memcpy((LPBYTE)px + dwOffset, pszItem, cbSizePath);
dwOffset += cbSizePath;
}
if (pidl) {
px->dwOffsetPidl = dwOffset;
memcpy((LPBYTE)px + dwOffset, pidl, cbSizePidl);
}
SHUnlockShared(px);
PostMessage(hwnd, CWM_ADDTORECENT, (WPARAM)hARD, (LPARAM)dwProcId);
}
}
// 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;
if (uFlags == SHARD_PIDL) {
// pv is a LPCITEMIDLIST (pidl)
if (SUCCEEDED(SHGetNameAndFlags((LPCITEMIDLIST)pv, SHGDN_FORPARSING, szTemp, SIZECHARS(szTemp), NULL))) {
AddToRecentDocs((LPCITEMIDLIST)pv, szTemp);
}
} else if (uFlags == SHARD_PATH) {
// pv is a LPTCSTR (path)
LPITEMIDLIST pidl = ILCreateFromPath((LPCTSTR)pv);
if (!pidl)
pidl = SHSimpleIDListFromPath((LPCTSTR)pv);
if (pidl) {
AddToRecentDocs(pidl, (LPCTSTR)pv);
ILFree(pidl);
}
}
#ifdef UNICODE
else if (uFlags == SHARD_PATHA) {
SHAnsiToUnicode((LPCSTR)pv, szTemp, ARRAYSIZE(szTemp));
SHAddToRecentDocs(SHARD_PATH, szTemp);
}
#else
else if (uFlags == SHARD_PATHW) {
SHUnicodeToAnsi((LPCWSTR)pv, szTemp, ARRAYSIZE(szTemp));
SHAddToRecentDocs(SHARD_PATH, szTemp);
}
#endif
}
WINSHELLAPI void ReceiveAddToRecentDocs(HANDLE hARD, DWORD dwProcId)
{
// NOTIMPL
ASSERT(FALSE);
}
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_IRunnableTask, (void**)pptask);
ptad->Release();
} else
hres = E_OUTOFMEMORY;
return hres;
}
STDAPI RecentDocs_GetDisplayName(LPCITEMIDLIST pidl, LPTSTR pszName, DWORD cchName)
{
HRESULT hr = E_FAIL;
HANDLE hmru = CreateSharedRecentMRUList(NULL, NULL, SRMLF_COMPPIDL);
if (hmru) {
int iItem = FindMRUData(hmru, pidl, ILGetSize(pidl), NULL);
if (-1 != iItem) {
BYTE buf[MAX_RECMRU_BUF];
if (-1 != EnumMRUList(hmru, iItem, buf, SIZEOF(buf))) {
StrCpyN(pszName, GETRECNAME(buf), cchName);
hr = S_OK;
}
}
FreeMRUList(hmru);
}
return hr;
}