Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
14 KiB

  1. /*
  2. * isbase.cpp - IUnknown implementation for Intshcut class.
  3. */
  4. #include "priv.h"
  5. #include "sccls.h"
  6. #include "ishcut.h"
  7. HRESULT IsProtocolRegistered(LPCTSTR pcszProtocol)
  8. {
  9. HRESULT hres = S_OK;
  10. ASSERT(IS_VALID_STRING_PTR(pcszProtocol, -1));
  11. if (NO_ERROR != SHGetValue(HKEY_CLASSES_ROOT, pcszProtocol, TEXT("URL Protocol"),
  12. NULL, NULL, NULL))
  13. {
  14. TraceMsg(TF_ERROR, "IsProtocolRegistered(): Protocol \"%s\" is not registered.",
  15. pcszProtocol);
  16. hres = URL_E_UNREGISTERED_PROTOCOL;
  17. }
  18. return hres;
  19. }
  20. /*----------------------------------------------------------
  21. Purpose: Takes the given URL and returns an allocated string
  22. containing the protocol. Also optionally returns the
  23. parsed-url structure.
  24. Returns: S_OK if the URL was parsed
  25. Cond: --
  26. */
  27. STDAPI
  28. CopyURLProtocol(
  29. IN LPCTSTR pcszURL,
  30. OUT LPTSTR * ppszProtocol,
  31. OUT PARSEDURL * ppu) OPTIONAL
  32. {
  33. HRESULT hr;
  34. PARSEDURL pu;
  35. ASSERT(IS_VALID_STRING_PTR(pcszURL, -1));
  36. ASSERT(IS_VALID_WRITE_PTR(ppszProtocol, PTSTR));
  37. if (NULL == ppu)
  38. ppu = &pu;
  39. *ppszProtocol = NULL;
  40. ppu->cbSize = SIZEOF(*ppu);
  41. hr = ParseURL(pcszURL, ppu);
  42. if (hr == S_OK)
  43. {
  44. *ppszProtocol = (LPTSTR)LocalAlloc(LPTR, CbFromCch(ppu->cchProtocol + 1));
  45. if (*ppszProtocol)
  46. {
  47. // Just copy the protocol portion of string
  48. StrCpyN(*ppszProtocol, ppu->pszProtocol, ppu->cchProtocol + 1);
  49. }
  50. else
  51. {
  52. hr = E_OUTOFMEMORY;
  53. }
  54. }
  55. ASSERT(FAILED(hr) ||
  56. (hr == S_OK &&
  57. IS_VALID_STRING_PTR(*ppszProtocol, -1)));
  58. return(hr);
  59. }
  60. HRESULT ValidateURL(LPCTSTR pcszURL)
  61. {
  62. HRESULT hr;
  63. LPTSTR pszProtocol;
  64. ASSERT(IS_VALID_STRING_PTR(pcszURL, -1));
  65. hr = CopyURLProtocol(pcszURL, &pszProtocol, NULL);
  66. if (hr == S_OK)
  67. {
  68. hr = IsProtocolRegistered(pszProtocol);
  69. LocalFree(pszProtocol);
  70. pszProtocol = NULL;
  71. }
  72. return(hr);
  73. }
  74. HRESULT ValidateWorkingDirectory(LPCTSTR pcszWorkingDirectory)
  75. {
  76. ASSERT(IS_VALID_STRING_PTR(pcszWorkingDirectory, -1));
  77. return(PathIsDirectory(pcszWorkingDirectory) ? S_OK : E_PATH_NOT_FOUND);
  78. }
  79. #define SUBS_DEL_TIMEOUT 3000
  80. void DeleteSubsOnShortcutDelete(void *pData, BOOLEAN)
  81. {
  82. IS_SUBS_DEL_DATA *pSubsDelData = (IS_SUBS_DEL_DATA *)pData;
  83. ASSERT(NULL != pData);
  84. ASSERT(0 != pSubsDelData->m_szFile[0]);
  85. ASSERT(0 != pSubsDelData->m_pwszURL[0]);
  86. if ((((DWORD)-1) == GetFileAttributes(pSubsDelData->m_szFile)) &&
  87. (ERROR_FILE_NOT_FOUND == GetLastError()))
  88. {
  89. if (SUCCEEDED(CoInitialize(NULL)))
  90. {
  91. ISubscriptionMgr2 *pSubsMgr2;
  92. if (SUCCEEDED(CoCreateInstance(CLSID_SubscriptionMgr,
  93. NULL,
  94. CLSCTX_INPROC_SERVER,
  95. IID_ISubscriptionMgr2,
  96. (void **)&pSubsMgr2)))
  97. {
  98. pSubsMgr2->DeleteSubscription(pSubsDelData->m_pwszURL, NULL);
  99. pSubsMgr2->Release();
  100. }
  101. CoUninitialize();
  102. }
  103. }
  104. delete pSubsDelData;
  105. }
  106. #ifdef DEBUG
  107. BOOL IsValidPCIntshcut(PCIntshcut pcintshcut)
  108. {
  109. return(IS_VALID_READ_PTR(pcintshcut, CIntshcut) &&
  110. FLAGS_ARE_VALID(pcintshcut->m_dwFlags, ISF_ALL) &&
  111. (! pcintshcut->m_pszFile ||
  112. IS_VALID_STRING_PTR(pcintshcut->m_pszFile, -1)) &&
  113. EVAL(! pcintshcut->m_pszFolder ||
  114. IsValidPath(pcintshcut->m_pszFolder)) &&
  115. EVAL(! pcintshcut->m_pprop ||
  116. IS_VALID_STRUCT_PTR(pcintshcut->m_pprop, CIntshcutProp)) &&
  117. EVAL(! pcintshcut->m_psiteprop ||
  118. IS_VALID_STRUCT_PTR(pcintshcut->m_psiteprop, CIntsiteProp)));
  119. }
  120. #endif
  121. Intshcut::Intshcut(void) : m_cRef(1)
  122. {
  123. DllAddRef();
  124. // Intshcut objects should always be allocated
  125. ASSERT(ISF_DEFAULT == m_dwFlags);
  126. ASSERT(NULL == m_pszFile);
  127. ASSERT(NULL == m_pszFolder);
  128. ASSERT(NULL == m_pprop);
  129. ASSERT(NULL == m_psiteprop);
  130. ASSERT(NULL == _punkSite);
  131. ASSERT(NULL == m_pszTempFileName);
  132. ASSERT(NULL == m_pszDescription);
  133. ASSERT(NULL == m_pszFileToLoad);
  134. ASSERT(!m_fMustLoadSync);
  135. ASSERT(!m_bCheckForDelete);
  136. // Init our registered data formats
  137. InitClipboardFormats();
  138. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  139. return;
  140. }
  141. Intshcut::~Intshcut(void)
  142. {
  143. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  144. if (m_bCheckForDelete)
  145. {
  146. if (NULL != m_pszFile)
  147. {
  148. IS_SUBS_DEL_DATA *pSubsDelData = new IS_SUBS_DEL_DATA;
  149. if (NULL != pSubsDelData)
  150. {
  151. HRESULT hr = GetURL(&pSubsDelData->m_pwszURL);
  152. if (SUCCEEDED(hr))
  153. {
  154. StrCpyN(pSubsDelData->m_szFile, m_pszFile, ARRAYSIZE(pSubsDelData->m_szFile));
  155. HANDLE hTimer = SHSetTimerQueueTimer(NULL,
  156. DeleteSubsOnShortcutDelete,
  157. pSubsDelData,
  158. SUBS_DEL_TIMEOUT,
  159. 0,
  160. NULL,
  161. FALSE);
  162. if (NULL == hTimer)
  163. {
  164. hr = E_FAIL;
  165. }
  166. }
  167. if (FAILED(hr))
  168. {
  169. delete pSubsDelData;
  170. }
  171. }
  172. }
  173. else
  174. {
  175. ASSERT(FALSE); // m_bCheckForDelete only gets set to TRUE in the context menu code
  176. }
  177. }
  178. Str_SetPtr(&m_pszFile, NULL);
  179. Str_SetPtr(&m_pszFileToLoad, NULL);
  180. if (m_pprop)
  181. {
  182. delete m_pprop;
  183. m_pprop = NULL;
  184. }
  185. if (m_psiteprop)
  186. {
  187. delete m_psiteprop;
  188. m_psiteprop = NULL;
  189. }
  190. if (m_pInitDataObject)
  191. {
  192. m_pInitDataObject->Release();
  193. m_pInitDataObject = NULL;
  194. }
  195. SetSite(NULL);
  196. if(m_pszTempFileName)
  197. {
  198. DeleteFile(m_pszTempFileName);
  199. Str_SetPtr(&m_pszTempFileName, NULL);
  200. }
  201. Str_SetPtr(&m_pszFolder, NULL);
  202. Str_SetPtr(&m_pszDescription, NULL);
  203. ASSERT(IS_VALID_STRUCT_PTR(this, CIntshcut));
  204. ATOMICRELEASE(_punkLink);
  205. DllRelease();
  206. return;
  207. }
  208. /*----------------------------------------------------------
  209. Purpose: IUnknown::QueryInterface handler for Intshcut
  210. Returns:
  211. Cond: --
  212. */
  213. STDMETHODIMP Intshcut::QueryInterface(REFIID riid, PVOID *ppvObj)
  214. {
  215. // We try and delay when we load the file specified by IPersistFile::Load
  216. // until someone asks for an interface that actually needs that file.
  217. // So put the "safe" interfaces that don't require this in this first
  218. // table here, and all the "must load" interfaces in the second table:
  219. //
  220. static const QITAB qitDontLoad[] = {
  221. QITABENT(Intshcut, IExtractIconW), // IID_IExtractIconW
  222. QITABENT(Intshcut, IExtractIconA), // IID_IExtractIconA
  223. QITABENT(Intshcut, IPersistFile), // IID_IPersistFile
  224. QITABENTMULTI(Intshcut, IPersist, IPersistFile), // IID_IPersist
  225. { 0 },
  226. };
  227. static const QITAB qitMustLoad[] = {
  228. QITABENT(Intshcut, IContextMenu2), // IID_IContextMenu2
  229. QITABENTMULTI(Intshcut, IContextMenu, IContextMenu2), // IID_IContextMenu
  230. QITABENT(Intshcut, IDataObject), // IID_IDataObject
  231. QITABENT(Intshcut, INewShortcutHookW), // IID_INewShortcutHookW
  232. QITABENT(Intshcut, INewShortcutHookA), // IID_INewShortcutHookA
  233. QITABENT(Intshcut, IPersistStream), // IID_IPersistStream
  234. QITABENT(Intshcut, IPropertySetStorage),// IID_IPropertySetStorage
  235. QITABENT(Intshcut, IShellExtInit), // IID_IShellExtInit
  236. QITABENT(Intshcut, IShellLinkA), // IID_IShellLinkA
  237. QITABENT(Intshcut, IShellLinkW), // IID_IShellLinkW
  238. QITABENT(Intshcut, IShellPropSheetExt), // IID_IShellPropSheetExt
  239. QITABENT(Intshcut, IUniformResourceLocatorA), // IID_IUniformResourceLocatorA
  240. QITABENT(Intshcut, IUniformResourceLocatorW), // IID_IUniformResourceLocatorW
  241. QITABENT(Intshcut, IQueryInfo), // IID_IQueryInfo
  242. QITABENT(Intshcut, IQueryCodePage), // IID_IQueryCodePage
  243. QITABENT(Intshcut, INamedPropertyBag), // IID_INamedPropertyBag
  244. QITABENT(Intshcut, IObjectWithSite), // IID_IObjectWithSite
  245. QITABENT(Intshcut, IOleCommandTarget), // IID_IOleCommandTarget
  246. { 0 },
  247. };
  248. HRESULT hres = QISearch(this, qitDontLoad, riid, ppvObj);
  249. if (FAILED(hres))
  250. {
  251. hres = QISearch(this, qitMustLoad, riid, ppvObj);
  252. if (SUCCEEDED(hres))
  253. {
  254. m_fMustLoadSync = TRUE;
  255. if (m_pszFileToLoad)
  256. {
  257. LoadFromAsyncFileNow();
  258. }
  259. }
  260. }
  261. return hres;
  262. }
  263. STDMETHODIMP_(ULONG) Intshcut::AddRef()
  264. {
  265. return InterlockedIncrement(&m_cRef);
  266. }
  267. STDMETHODIMP_(ULONG) Intshcut::Release()
  268. {
  269. if (InterlockedDecrement(&m_cRef))
  270. return m_cRef;
  271. delete this;
  272. return 0;
  273. }
  274. STDMETHODIMP Intshcut::InitProp()
  275. {
  276. HRESULT hres;
  277. if (m_pprop)
  278. hres = S_OK;
  279. else
  280. {
  281. m_pprop = new IntshcutProp;
  282. if (m_pprop)
  283. {
  284. // m_pszFile may be NULL here
  285. hres = m_pprop->InitFromFile(m_pszFile);
  286. if (FAILED(hres))
  287. {
  288. delete m_pprop;
  289. m_pprop = NULL;
  290. }
  291. }
  292. else
  293. hres = E_OUTOFMEMORY;
  294. }
  295. return hres;
  296. }
  297. STDMETHODIMP Intshcut::InitSiteProp(void)
  298. {
  299. HRESULT hres = InitProp();
  300. if (SUCCEEDED(hres))
  301. {
  302. TCHAR szURL[INTERNET_MAX_URL_LENGTH];
  303. hres = m_pprop->GetProp(PID_IS_URL, szURL, SIZECHARS(szURL));
  304. if (NULL == m_psiteprop && SUCCEEDED( hres ))
  305. {
  306. m_psiteprop = new IntsiteProp;
  307. if (m_psiteprop)
  308. {
  309. hres = m_psiteprop->InitFromDB(szURL, this, TRUE);
  310. if (FAILED(hres))
  311. {
  312. delete m_psiteprop;
  313. m_psiteprop = NULL;
  314. }
  315. }
  316. else
  317. hres = E_OUTOFMEMORY;
  318. }
  319. }
  320. return hres;
  321. }
  322. /*----------------------------------------------------------
  323. Purpose: Only copy the property if it is different. Return
  324. TRUE if it was.
  325. */
  326. BOOL CopyChangedProperty(IntshcutProp * pprop, PROPID pid,
  327. IntsiteProp * psiteprop, PROPID pidSite,
  328. BOOL bCopyToDB)
  329. {
  330. BOOL bRet = FALSE;
  331. TCHAR szBuf[1024];
  332. TCHAR szBufSite[1024];
  333. pprop->GetProp(pid, szBuf, SIZECHARS(szBuf));
  334. psiteprop->GetProp(pidSite, szBufSite, SIZECHARS(szBufSite));
  335. StrTrim(szBuf, TEXT(" "));
  336. StrTrim(szBufSite, TEXT(" "));
  337. if (StrCmp(szBuf, szBufSite))
  338. {
  339. if (bCopyToDB)
  340. psiteprop->SetProp(pidSite, szBuf);
  341. else
  342. pprop->SetProp(pid, szBufSite);
  343. bRet = TRUE;
  344. }
  345. return bRet;
  346. }
  347. /*----------------------------------------------------------
  348. Purpose: Mirror the following properties between FMTID_INTSHCUT
  349. and FMTID_INTSITE:
  350. PID_IS_WHATSNEW <----> PID_INTSITE_WHATSNEW
  351. PID_IS_DESCRIPTION <----> PID_INTSITE_DESCRIPTION
  352. PID_IS_AUTHOR <----> PID_INTSITE_AUTHOR
  353. PID_IS_COMMENT <----> PID_INTSITE_COMMENT
  354. Returns:
  355. Cond: --
  356. */
  357. STDMETHODIMP Intshcut::MirrorProperties(void)
  358. {
  359. HRESULT hres = InitSiteProp();
  360. if (SUCCEEDED(hres))
  361. {
  362. STATPROPSETSTG statSite;
  363. STATPROPSETSTG stat;
  364. LONG lRet;
  365. // Get the times that the properties were set. The later
  366. // time becomes the source.
  367. m_psiteprop->Stat(&statSite);
  368. m_pprop->Stat(&stat);
  369. // Don't do anything if the times are equal
  370. lRet = CompareFileTime(&stat.mtime, &statSite.mtime);
  371. if (0 != lRet)
  372. {
  373. BOOL bChanged = FALSE;
  374. BOOL bCopyToDB = (0 < lRet);
  375. bChanged |= CopyChangedProperty(m_pprop, PID_IS_WHATSNEW, m_psiteprop, PID_INTSITE_WHATSNEW, bCopyToDB);
  376. bChanged |= CopyChangedProperty(m_pprop, PID_IS_DESCRIPTION, m_psiteprop, PID_INTSITE_DESCRIPTION, bCopyToDB);
  377. bChanged |= CopyChangedProperty(m_pprop, PID_IS_AUTHOR, m_psiteprop, PID_INTSITE_AUTHOR, bCopyToDB);
  378. bChanged |= CopyChangedProperty(m_pprop, PID_IS_COMMENT, m_psiteprop, PID_INTSITE_COMMENT, bCopyToDB);
  379. if (bChanged)
  380. {
  381. if (bCopyToDB)
  382. {
  383. m_psiteprop->SetTimes(&stat.mtime, NULL, NULL);
  384. m_psiteprop->Commit(STGC_DEFAULT);
  385. TraceMsg(TF_INTSHCUT, "Mirroring properties of %s to the central database", Dbg_SafeStr(m_pszFile));
  386. }
  387. else
  388. {
  389. m_pprop->SetTimes(&statSite.mtime, NULL, NULL);
  390. m_pprop->Commit(STGC_DEFAULT);
  391. TraceMsg(TF_INTSHCUT, "Mirroring properties of %s to the .url file", Dbg_SafeStr(m_pszFile));
  392. }
  393. }
  394. }
  395. hres = S_OK;
  396. }
  397. return hres;
  398. }
  399. STDMETHODIMP_(void) Intshcut::ChangeNotify(LONG wEventId, UINT uFlags)
  400. {
  401. if (m_pszFile)
  402. SHChangeNotify(wEventId, uFlags | SHCNF_PATH, m_pszFile, 0);
  403. }
  404. STDAPI
  405. CIntShcut_CreateInstance(
  406. IUnknown * punkOuter,
  407. IUnknown ** ppunk,
  408. LPCOBJECTINFO poi)
  409. {
  410. // aggregation checking is handled in class factory
  411. HRESULT hres = E_OUTOFMEMORY;
  412. Intshcut *pis = new Intshcut;
  413. if (pis)
  414. {
  415. *ppunk = SAFECAST(pis, IDataObject *);
  416. hres = S_OK;
  417. }
  418. return hres;
  419. }