/* * 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); } #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)) { hr = StringCchCopy(pSubsDelData->m_szFile, ARRAYSIZE(pSubsDelData->m_szFile), m_pszFile); if (SUCCEEDED(hr)) { 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() { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } 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; }