505 lines
14 KiB
C++
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;
|
||
|
}
|
||
|
|
||
|
|