windows-nt/Source/XPSP1/NT/shell/shdocvw/isbase.cpp
2020-09-26 16:20:57 +08:00

505 lines
14 KiB
C++

/*
* isbase.cpp - IUnknown implementation for Intshcut class.
*/
#include "priv.h"
#include "sccls.h"
#include "ishcut.h"
HRESULT IsProtocolRegistered(LPCTSTR pcszProtocol)
{
HRESULT hres = S_OK;
ASSERT(IS_VALID_STRING_PTR(pcszProtocol, -1));
if (NO_ERROR != SHGetValue(HKEY_CLASSES_ROOT, pcszProtocol, TEXT("URL Protocol"),
NULL, NULL, NULL))
{
TraceMsg(TF_ERROR, "IsProtocolRegistered(): Protocol \"%s\" is not registered.",
pcszProtocol);
hres = URL_E_UNREGISTERED_PROTOCOL;
}
return hres;
}
/*----------------------------------------------------------
Purpose: Takes the given URL and returns an allocated string
containing the protocol. Also optionally returns the
parsed-url structure.
Returns: S_OK if the URL was parsed
Cond: --
*/
STDAPI
CopyURLProtocol(
IN LPCTSTR pcszURL,
OUT LPTSTR * ppszProtocol,
OUT PARSEDURL * ppu) OPTIONAL
{
HRESULT hr;
PARSEDURL pu;
ASSERT(IS_VALID_STRING_PTR(pcszURL, -1));
ASSERT(IS_VALID_WRITE_PTR(ppszProtocol, PTSTR));
if (NULL == ppu)
ppu = &pu;
*ppszProtocol = NULL;
ppu->cbSize = SIZEOF(*ppu);
hr = ParseURL(pcszURL, ppu);
if (hr == S_OK)
{
*ppszProtocol = (LPTSTR)LocalAlloc(LPTR, CbFromCch(ppu->cchProtocol + 1));
if (*ppszProtocol)
{
// Just copy the protocol portion of string
StrCpyN(*ppszProtocol, ppu->pszProtocol, ppu->cchProtocol + 1);
}
else
{
hr = E_OUTOFMEMORY;
}
}
ASSERT(FAILED(hr) ||
(hr == S_OK &&
IS_VALID_STRING_PTR(*ppszProtocol, -1)));
return(hr);
}
HRESULT ValidateURL(LPCTSTR pcszURL)
{
HRESULT hr;
LPTSTR pszProtocol;
ASSERT(IS_VALID_STRING_PTR(pcszURL, -1));
hr = CopyURLProtocol(pcszURL, &pszProtocol, NULL);
if (hr == S_OK)
{
hr = IsProtocolRegistered(pszProtocol);
LocalFree(pszProtocol);
pszProtocol = NULL;
}
return(hr);
}
HRESULT ValidateWorkingDirectory(LPCTSTR pcszWorkingDirectory)
{
ASSERT(IS_VALID_STRING_PTR(pcszWorkingDirectory, -1));
return(PathIsDirectory(pcszWorkingDirectory) ? S_OK : E_PATH_NOT_FOUND);
}
#define SUBS_DEL_TIMEOUT 3000
void DeleteSubsOnShortcutDelete(void *pData, BOOLEAN)
{
IS_SUBS_DEL_DATA *pSubsDelData = (IS_SUBS_DEL_DATA *)pData;
ASSERT(NULL != pData);
ASSERT(0 != pSubsDelData->m_szFile[0]);
ASSERT(0 != pSubsDelData->m_pwszURL[0]);
if ((((DWORD)-1) == GetFileAttributes(pSubsDelData->m_szFile)) &&
(ERROR_FILE_NOT_FOUND == GetLastError()))
{
if (SUCCEEDED(CoInitialize(NULL)))
{
ISubscriptionMgr2 *pSubsMgr2;
if (SUCCEEDED(CoCreateInstance(CLSID_SubscriptionMgr,
NULL,
CLSCTX_INPROC_SERVER,
IID_ISubscriptionMgr2,
(void **)&pSubsMgr2)))
{
pSubsMgr2->DeleteSubscription(pSubsDelData->m_pwszURL, NULL);
pSubsMgr2->Release();
}
CoUninitialize();
}
}
delete pSubsDelData;
}
#ifdef DEBUG
BOOL IsValidPCIntshcut(PCIntshcut pcintshcut)
{
return(IS_VALID_READ_PTR(pcintshcut, CIntshcut) &&
FLAGS_ARE_VALID(pcintshcut->m_dwFlags, ISF_ALL) &&
(! pcintshcut->m_pszFile ||
IS_VALID_STRING_PTR(pcintshcut->m_pszFile, -1)) &&
EVAL(! pcintshcut->m_pszFolder ||
IsValidPath(pcintshcut->m_pszFolder)) &&
EVAL(! pcintshcut->m_pprop ||
IS_VALID_STRUCT_PTR(pcintshcut->m_pprop, CIntshcutProp)) &&
EVAL(! pcintshcut->m_psiteprop ||
IS_VALID_STRUCT_PTR(pcintshcut->m_psiteprop, CIntsiteProp)));
}
#endif
Intshcut::Intshcut(void) : m_cRef(1)
{
DllAddRef();
// Intshcut objects should always be allocated
ASSERT(ISF_DEFAULT == m_dwFlags);
ASSERT(NULL == m_pszFile);
ASSERT(NULL == m_pszFolder);
ASSERT(NULL == m_pprop);
ASSERT(NULL == m_psiteprop);
ASSERT(NULL == _punkSite);
ASSERT(NULL == m_pszTempFileName);
ASSERT(NULL == m_pszDescription);
ASSERT(NULL == m_pszFileToLoad);
ASSERT(!m_fMustLoadSync);
ASSERT(!m_bCheckForDelete);
// Init our registered data formats
InitClipboardFormats();
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
return;
}
Intshcut::~Intshcut(void)
{
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
if (m_bCheckForDelete)
{
if (NULL != m_pszFile)
{
IS_SUBS_DEL_DATA *pSubsDelData = new IS_SUBS_DEL_DATA;
if (NULL != pSubsDelData)
{
HRESULT hr = GetURL(&pSubsDelData->m_pwszURL);
if (SUCCEEDED(hr))
{
StrCpyN(pSubsDelData->m_szFile, m_pszFile, ARRAYSIZE(pSubsDelData->m_szFile));
HANDLE hTimer = SHSetTimerQueueTimer(NULL,
DeleteSubsOnShortcutDelete,
pSubsDelData,
SUBS_DEL_TIMEOUT,
0,
NULL,
FALSE);
if (NULL == hTimer)
{
hr = E_FAIL;
}
}
if (FAILED(hr))
{
delete pSubsDelData;
}
}
}
else
{
ASSERT(FALSE); // m_bCheckForDelete only gets set to TRUE in the context menu code
}
}
Str_SetPtr(&m_pszFile, NULL);
Str_SetPtr(&m_pszFileToLoad, NULL);
if (m_pprop)
{
delete m_pprop;
m_pprop = NULL;
}
if (m_psiteprop)
{
delete m_psiteprop;
m_psiteprop = NULL;
}
if (m_pInitDataObject)
{
m_pInitDataObject->Release();
m_pInitDataObject = NULL;
}
SetSite(NULL);
if(m_pszTempFileName)
{
DeleteFile(m_pszTempFileName);
Str_SetPtr(&m_pszTempFileName, NULL);
}
Str_SetPtr(&m_pszFolder, NULL);
Str_SetPtr(&m_pszDescription, NULL);
ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
ATOMICRELEASE(_punkLink);
DllRelease();
return;
}
/*----------------------------------------------------------
Purpose: IUnknown::QueryInterface handler for Intshcut
Returns:
Cond: --
*/
STDMETHODIMP Intshcut::QueryInterface(REFIID riid, PVOID *ppvObj)
{
// We try and delay when we load the file specified by IPersistFile::Load
// until someone asks for an interface that actually needs that file.
// So put the "safe" interfaces that don't require this in this first
// table here, and all the "must load" interfaces in the second table:
//
static const QITAB qitDontLoad[] = {
QITABENT(Intshcut, IExtractIconW), // IID_IExtractIconW
QITABENT(Intshcut, IExtractIconA), // IID_IExtractIconA
QITABENT(Intshcut, IPersistFile), // IID_IPersistFile
QITABENTMULTI(Intshcut, IPersist, IPersistFile), // IID_IPersist
{ 0 },
};
static const QITAB qitMustLoad[] = {
QITABENT(Intshcut, IContextMenu2), // IID_IContextMenu2
QITABENTMULTI(Intshcut, IContextMenu, IContextMenu2), // IID_IContextMenu
QITABENT(Intshcut, IDataObject), // IID_IDataObject
QITABENT(Intshcut, INewShortcutHookW), // IID_INewShortcutHookW
QITABENT(Intshcut, INewShortcutHookA), // IID_INewShortcutHookA
QITABENT(Intshcut, IPersistStream), // IID_IPersistStream
QITABENT(Intshcut, IPropertySetStorage),// IID_IPropertySetStorage
QITABENT(Intshcut, IShellExtInit), // IID_IShellExtInit
QITABENT(Intshcut, IShellLinkA), // IID_IShellLinkA
QITABENT(Intshcut, IShellLinkW), // IID_IShellLinkW
QITABENT(Intshcut, IShellPropSheetExt), // IID_IShellPropSheetExt
QITABENT(Intshcut, IUniformResourceLocatorA), // IID_IUniformResourceLocatorA
QITABENT(Intshcut, IUniformResourceLocatorW), // IID_IUniformResourceLocatorW
QITABENT(Intshcut, IQueryInfo), // IID_IQueryInfo
QITABENT(Intshcut, IQueryCodePage), // IID_IQueryCodePage
QITABENT(Intshcut, INamedPropertyBag), // IID_INamedPropertyBag
QITABENT(Intshcut, IObjectWithSite), // IID_IObjectWithSite
QITABENT(Intshcut, IOleCommandTarget), // IID_IOleCommandTarget
{ 0 },
};
HRESULT hres = QISearch(this, qitDontLoad, riid, ppvObj);
if (FAILED(hres))
{
hres = QISearch(this, qitMustLoad, riid, ppvObj);
if (SUCCEEDED(hres))
{
m_fMustLoadSync = TRUE;
if (m_pszFileToLoad)
{
LoadFromAsyncFileNow();
}
}
}
return hres;
}
STDMETHODIMP_(ULONG) Intshcut::AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Intshcut::Release()
{
if (InterlockedDecrement(&m_cRef))
return m_cRef;
delete this;
return 0;
}
STDMETHODIMP Intshcut::InitProp()
{
HRESULT hres;
if (m_pprop)
hres = S_OK;
else
{
m_pprop = new IntshcutProp;
if (m_pprop)
{
// m_pszFile may be NULL here
hres = m_pprop->InitFromFile(m_pszFile);
if (FAILED(hres))
{
delete m_pprop;
m_pprop = NULL;
}
}
else
hres = E_OUTOFMEMORY;
}
return hres;
}
STDMETHODIMP Intshcut::InitSiteProp(void)
{
HRESULT hres = InitProp();
if (SUCCEEDED(hres))
{
TCHAR szURL[INTERNET_MAX_URL_LENGTH];
hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
if (NULL == m_psiteprop && SUCCEEDED( hres ))
{
m_psiteprop = new IntsiteProp;
if (m_psiteprop)
{
hres = m_psiteprop->InitFromDB(szURL, this, TRUE);
if (FAILED(hres))
{
delete m_psiteprop;
m_psiteprop = NULL;
}
}
else
hres = E_OUTOFMEMORY;
}
}
return hres;
}
/*----------------------------------------------------------
Purpose: Only copy the property if it is different. Return
TRUE if it was.
*/
BOOL CopyChangedProperty(IntshcutProp * pprop, PROPID pid,
IntsiteProp * psiteprop, PROPID pidSite,
BOOL bCopyToDB)
{
BOOL bRet = FALSE;
TCHAR szBuf[1024];
TCHAR szBufSite[1024];
pprop->GetProp(pid, szBuf, SIZECHARS(szBuf));
psiteprop->GetProp(pidSite, szBufSite, SIZECHARS(szBufSite));
StrTrim(szBuf, TEXT(" "));
StrTrim(szBufSite, TEXT(" "));
if (StrCmp(szBuf, szBufSite))
{
if (bCopyToDB)
psiteprop->SetProp(pidSite, szBuf);
else
pprop->SetProp(pid, szBufSite);
bRet = TRUE;
}
return bRet;
}
/*----------------------------------------------------------
Purpose: Mirror the following properties between FMTID_INTSHCUT
and FMTID_INTSITE:
PID_IS_WHATSNEW <----> PID_INTSITE_WHATSNEW
PID_IS_DESCRIPTION <----> PID_INTSITE_DESCRIPTION
PID_IS_AUTHOR <----> PID_INTSITE_AUTHOR
PID_IS_COMMENT <----> PID_INTSITE_COMMENT
Returns:
Cond: --
*/
STDMETHODIMP Intshcut::MirrorProperties(void)
{
HRESULT hres = InitSiteProp();
if (SUCCEEDED(hres))
{
STATPROPSETSTG statSite;
STATPROPSETSTG stat;
LONG lRet;
// Get the times that the properties were set. The later
// time becomes the source.
m_psiteprop->Stat(&statSite);
m_pprop->Stat(&stat);
// Don't do anything if the times are equal
lRet = CompareFileTime(&stat.mtime, &statSite.mtime);
if (0 != lRet)
{
BOOL bChanged = FALSE;
BOOL bCopyToDB = (0 < lRet);
bChanged |= CopyChangedProperty(m_pprop, PID_IS_WHATSNEW, m_psiteprop, PID_INTSITE_WHATSNEW, bCopyToDB);
bChanged |= CopyChangedProperty(m_pprop, PID_IS_DESCRIPTION, m_psiteprop, PID_INTSITE_DESCRIPTION, bCopyToDB);
bChanged |= CopyChangedProperty(m_pprop, PID_IS_AUTHOR, m_psiteprop, PID_INTSITE_AUTHOR, bCopyToDB);
bChanged |= CopyChangedProperty(m_pprop, PID_IS_COMMENT, m_psiteprop, PID_INTSITE_COMMENT, bCopyToDB);
if (bChanged)
{
if (bCopyToDB)
{
m_psiteprop->SetTimes(&stat.mtime, NULL, NULL);
m_psiteprop->Commit(STGC_DEFAULT);
TraceMsg(TF_INTSHCUT, "Mirroring properties of %s to the central database", Dbg_SafeStr(m_pszFile));
}
else
{
m_pprop->SetTimes(&statSite.mtime, NULL, NULL);
m_pprop->Commit(STGC_DEFAULT);
TraceMsg(TF_INTSHCUT, "Mirroring properties of %s to the .url file", Dbg_SafeStr(m_pszFile));
}
}
}
hres = S_OK;
}
return hres;
}
STDMETHODIMP_(void) Intshcut::ChangeNotify(LONG wEventId, UINT uFlags)
{
if (m_pszFile)
SHChangeNotify(wEventId, uFlags | SHCNF_PATH, m_pszFile, 0);
}
STDAPI
CIntShcut_CreateInstance(
IUnknown * punkOuter,
IUnknown ** ppunk,
LPCOBJECTINFO poi)
{
// aggregation checking is handled in class factory
HRESULT hres = E_OUTOFMEMORY;
Intshcut *pis = new Intshcut;
if (pis)
{
*ppunk = SAFECAST(pis, IDataObject *);
hres = S_OK;
}
return hres;
}