808 lines
24 KiB
C++
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;
|
|
}
|