|
|
//---------------------------------------------------------------------------
//
// 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; }
|