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.

465 lines
13 KiB

  1. #include "shellprv.h"
  2. #include "clsobj.h"
  3. #include "ole2dup.h"
  4. class CPostBootReminder : public IShellReminderManager,
  5. public IOleCommandTarget,
  6. public IQueryContinue
  7. {
  8. public:
  9. // IUnknown
  10. STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppv);
  11. STDMETHOD_(ULONG, AddRef)();
  12. STDMETHOD_(ULONG, Release)();
  13. // IShellReminderManager
  14. STDMETHOD(Add)(const SHELLREMINDER* psr);
  15. STDMETHOD(Delete)(LPCWSTR pszName);
  16. STDMETHOD(Enum)(IEnumShellReminder** ppesr);
  17. // IOleCommandTarget Implementation (used to display the PostBootReminders as a shell service object)
  18. STDMETHOD(QueryStatus)(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText);
  19. STDMETHOD(Exec)(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut);
  20. // IQueryContinue
  21. STDMETHOD(QueryContinue)(void);
  22. CPostBootReminder();
  23. private:
  24. static DWORD _ThreadProc(void* pv);
  25. LONG _cRef;
  26. TCHAR _szKeyShowing[MAX_PATH];
  27. };
  28. HRESULT CPostBootReminder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  29. {
  30. if (NULL != punkOuter)
  31. {
  32. return CLASS_E_NOAGGREGATION;
  33. }
  34. CPostBootReminder* pPbr = new CPostBootReminder();
  35. if (!pPbr)
  36. {
  37. return E_OUTOFMEMORY;
  38. }
  39. HRESULT hr = pPbr->QueryInterface(riid, ppv);
  40. pPbr->Release();
  41. return hr;
  42. }
  43. // Per-user (HKCU)
  44. #define REGPATH_POSTBOOTREMINDERS REGSTR_PATH_EXPLORER TEXT("\\PostBootReminders")
  45. #define REGPATH_POSTBOOTTODO REGSTR_PATH_EXPLORER TEXT("\\PostBootToDo")
  46. #define PROP_POSTBOOT_TITLE TEXT("Title") // REG_SZ
  47. #define PROP_POSTBOOT_TEXT TEXT("Text") // REG_SZ
  48. #define PROP_POSTBOOT_TOOLTIP TEXT("ToolTip") // REG_SZ
  49. #define PROP_POSTBOOT_CLSID TEXT("Clsid") // REG_SZ
  50. #define PROP_POSTBOOT_SHELLEXECUTE TEXT("ShellExecute") // REG_SZ
  51. #define PROP_POSTBOOT_ICONRESOURCE TEXT("IconResource") // REG_SZ "module,-resid"
  52. #define PROP_POSTBOOT_SHOWTIME TEXT("ShowTime") // REG_DWORD
  53. #define PROP_POSTBOOT_RETRYINTERVAL TEXT("RetryInterval") // REG_DWORD
  54. #define PROP_POSTBOOT_RETRYCOUNT TEXT("RetryCount") // REG_DWORD
  55. #define PROP_POSTBOOT_TYPEFLAGS TEXT("TypeFlags") // REG_DWORD (NIIF_WARNING, NIIF_INFO, NIIF_ERROR)
  56. CPostBootReminder::CPostBootReminder()
  57. {
  58. _cRef = 1;
  59. }
  60. // IUnknown
  61. HRESULT CPostBootReminder::QueryInterface(REFIID riid, void **ppv)
  62. {
  63. static const QITAB qit[] =
  64. {
  65. QITABENT(CPostBootReminder, IShellReminderManager),
  66. QITABENT(CPostBootReminder, IOleCommandTarget),
  67. QITABENT(CPostBootReminder, IQueryContinue),
  68. { 0 },
  69. };
  70. return QISearch(this, qit, riid, ppv);
  71. }
  72. ULONG CPostBootReminder::AddRef()
  73. {
  74. return InterlockedIncrement(&_cRef);
  75. }
  76. ULONG CPostBootReminder::Release()
  77. {
  78. if (InterlockedDecrement(&_cRef))
  79. return _cRef;
  80. delete this;
  81. return 0;
  82. }
  83. // IShellReminderManager
  84. HRESULT CPostBootReminder::Add(const SHELLREMINDER* psr)
  85. {
  86. HRESULT hr = E_FAIL;
  87. // Ensure the parent key is created
  88. HKEY hkeyCurrentUser;
  89. if (ERROR_SUCCESS == RegOpenCurrentUser(KEY_WRITE, &hkeyCurrentUser))
  90. {
  91. HKEY hkeyReminders;
  92. if (ERROR_SUCCESS == RegCreateKeyEx(hkeyCurrentUser, REGPATH_POSTBOOTREMINDERS, 0, NULL, 0, KEY_WRITE, NULL, &hkeyReminders, NULL))
  93. {
  94. IPropertyBag* pPb;
  95. hr = SHCreatePropertyBagOnRegKey(hkeyReminders, psr->pszName, STGM_WRITE | STGM_CREATE, IID_PPV_ARG(IPropertyBag, &pPb));
  96. if (SUCCEEDED(hr))
  97. {
  98. // need to check the SHELLREMINDER values for null or we will RIP in SHPropertyBag_WriteStr/GUID
  99. if (psr->pszTitle)
  100. {
  101. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TITLE, psr->pszTitle);
  102. }
  103. if (psr->pszText)
  104. {
  105. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TEXT, psr->pszText);
  106. }
  107. if (psr->pszTooltip)
  108. {
  109. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TOOLTIP, psr->pszTooltip);
  110. }
  111. if (psr->pszIconResource)
  112. {
  113. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_ICONRESOURCE, psr->pszIconResource);
  114. }
  115. if (psr->pszShellExecute)
  116. {
  117. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_SHELLEXECUTE, psr->pszShellExecute);
  118. }
  119. if (psr->pclsid)
  120. {
  121. SHPropertyBag_WriteGUID(pPb, PROP_POSTBOOT_CLSID, psr->pclsid);
  122. }
  123. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_SHOWTIME, psr->dwShowTime);
  124. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_RETRYINTERVAL, psr->dwRetryInterval);
  125. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_RETRYCOUNT, psr->dwRetryCount);
  126. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_TYPEFLAGS, psr->dwTypeFlags);
  127. pPb->Release();
  128. }
  129. RegCloseKey(hkeyReminders);
  130. hr = S_OK;
  131. }
  132. RegCloseKey(hkeyCurrentUser);
  133. }
  134. return hr;
  135. }
  136. HRESULT CPostBootReminder::Delete(LPCWSTR pszName)
  137. {
  138. HRESULT hr = E_FAIL;
  139. HKEY hKey;
  140. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS, 0, KEY_WRITE, &hKey))
  141. {
  142. SHDeleteKey(hKey, pszName);
  143. RegCloseKey(hKey);
  144. hr = S_OK;
  145. }
  146. else
  147. {
  148. hr = S_FALSE;
  149. }
  150. return hr;
  151. }
  152. HRESULT CPostBootReminder::Enum(IEnumShellReminder** ppesr)
  153. {
  154. *ppesr = NULL;
  155. return E_NOTIMPL;
  156. }
  157. // IOleCommandTarget implementation
  158. HRESULT CPostBootReminder::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText)
  159. {
  160. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  161. if (*pguidCmdGroup == CGID_ShellServiceObject)
  162. {
  163. // We like Shell Service Object notifications...
  164. hr = S_OK;
  165. }
  166. return hr;
  167. }
  168. HRESULT CPostBootReminder::Exec(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
  169. {
  170. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  171. if (*pguidCmdGroup == CGID_ShellServiceObject)
  172. {
  173. hr = S_OK; // Any ol' notification is ok with us
  174. // Handle Shell Service Object notifications here.
  175. switch (nCmdID)
  176. {
  177. case SSOCMDID_OPEN:
  178. AddRef(); // AddRef so that this instance stays around. An equivalent Release() is in _ThreadProc
  179. if (!SHCreateThread(_ThreadProc, this, CTF_COINIT, NULL))
  180. {
  181. Release();
  182. }
  183. break;
  184. }
  185. }
  186. return hr;
  187. }
  188. // IQueryContinue implementation
  189. HRESULT CPostBootReminder::QueryContinue()
  190. {
  191. HRESULT hr = S_OK;
  192. if (_szKeyShowing[0])
  193. {
  194. HKEY hKey;
  195. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _szKeyShowing, 0, KEY_READ, &hKey))
  196. {
  197. RegCloseKey(hKey);
  198. }
  199. else
  200. {
  201. hr = S_FALSE;
  202. }
  203. }
  204. return hr;
  205. }
  206. // Function prototypes
  207. HRESULT CreateUserNotificationFromPropertyBag(IPropertyBag* pPb, IUserNotification** ppun);
  208. HRESULT _SetBalloonInfoFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun);
  209. HRESULT _SetBalloonRetryFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun);
  210. HRESULT _SetBalloonIconFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun);
  211. HRESULT _InvokeFromPropertyBag(IPropertyBag* pPb);
  212. HRESULT GetSubKeyPropertyBag(HKEY hkey, DWORD iSubKey, DWORD grfMode, IPropertyBag** ppPb, TCHAR * szKey, DWORD cbKey);
  213. HRESULT _SetBalloonInfoFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun)
  214. {
  215. TCHAR szTitle[256];
  216. HRESULT hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TITLE, szTitle, ARRAYSIZE(szTitle));
  217. if (SUCCEEDED(hr))
  218. {
  219. TCHAR szText[512];
  220. hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TEXT, szText, ARRAYSIZE(szText));
  221. if (SUCCEEDED(hr))
  222. {
  223. DWORD dwFlags = 0;
  224. hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_TYPEFLAGS, &dwFlags);
  225. if (SUCCEEDED(hr))
  226. {
  227. hr = pun->SetBalloonInfo(szTitle, szText, dwFlags);
  228. }
  229. }
  230. }
  231. return hr;
  232. }
  233. HRESULT _SetBalloonRetryFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun)
  234. {
  235. DWORD dwShowTime;
  236. HRESULT hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_SHOWTIME, &dwShowTime);
  237. if (SUCCEEDED(hr))
  238. {
  239. DWORD dwRetryInterval;
  240. hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_RETRYINTERVAL, &dwRetryInterval);
  241. if (SUCCEEDED(hr))
  242. {
  243. DWORD dwRetryCount;
  244. hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_RETRYCOUNT, &dwRetryCount);
  245. if (SUCCEEDED(hr))
  246. {
  247. hr = pun->SetBalloonRetry(dwShowTime, dwRetryInterval, dwRetryCount);
  248. }
  249. }
  250. }
  251. return hr;
  252. }
  253. HRESULT _SetBalloonIconFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun)
  254. {
  255. TCHAR szTooltip[256];
  256. HRESULT hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TOOLTIP, szTooltip, ARRAYSIZE(szTooltip));
  257. if (FAILED(hr))
  258. {
  259. *szTooltip = 0;
  260. }
  261. TCHAR szIcon[MAX_PATH + 6];
  262. hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_ICONRESOURCE, szIcon, ARRAYSIZE(szIcon));
  263. if (SUCCEEDED(hr))
  264. {
  265. int iIcon = PathParseIconLocation(szIcon);
  266. HICON hIcon;
  267. hr = (0 == ExtractIconEx(szIcon, iIcon, NULL, &hIcon, 1)) ? E_FAIL : S_OK;
  268. if (SUCCEEDED(hr))
  269. {
  270. pun->SetIconInfo(hIcon, szTooltip);
  271. DestroyIcon(hIcon);
  272. }
  273. }
  274. return hr;
  275. }
  276. HRESULT _InvokeFromPropertyBag(IPropertyBag* pPb)
  277. {
  278. // First try to use the CLSID to find a handler for the click
  279. CLSID clsid;
  280. HRESULT hr = SHPropertyBag_ReadGUID(pPb, PROP_POSTBOOT_CLSID, &clsid);
  281. if (SUCCEEDED(hr))
  282. {
  283. IContextMenu* pcm;
  284. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
  285. if (SUCCEEDED(hr))
  286. {
  287. CMINVOKECOMMANDINFO ici = {0};
  288. ici.cbSize = sizeof(ici);
  289. ici.lpVerb = "open";
  290. ici.nShow = SW_SHOWNORMAL;
  291. pcm->InvokeCommand(&ici);
  292. pcm->Release();
  293. }
  294. }
  295. if (FAILED(hr))
  296. {
  297. // Second, use the shellexecute line
  298. TCHAR szExecute[MAX_PATH + 1];
  299. hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_SHELLEXECUTE, szExecute, ARRAYSIZE(szExecute));
  300. if (SUCCEEDED(hr))
  301. {
  302. // Use shellexecuteex to open a view folder
  303. SHELLEXECUTEINFO shexinfo = {0};
  304. shexinfo.cbSize = sizeof (shexinfo);
  305. shexinfo.fMask = SEE_MASK_FLAG_NO_UI;
  306. shexinfo.nShow = SW_SHOWNORMAL;
  307. shexinfo.lpFile = szExecute;
  308. ShellExecuteEx(&shexinfo);
  309. }
  310. }
  311. return hr;
  312. }
  313. DWORD CPostBootReminder::_ThreadProc(void* pv)
  314. {
  315. HKEY hkeyReminders;
  316. CPostBootReminder * ppbr = (CPostBootReminder *) pv;
  317. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS, 0, KEY_READ, &hkeyReminders))
  318. {
  319. DWORD iReminder = 0;
  320. HRESULT hr = S_OK;
  321. while (S_OK == hr)
  322. {
  323. IPropertyBag* pPb;
  324. hr = GetSubKeyPropertyBag(hkeyReminders, iReminder, STGM_READ, &pPb, ppbr->_szKeyShowing, ARRAYSIZE(ppbr->_szKeyShowing));
  325. if (S_OK == hr)
  326. {
  327. IUserNotification* pun;
  328. hr = CreateUserNotificationFromPropertyBag(pPb, &pun);
  329. if (SUCCEEDED(hr))
  330. {
  331. if (S_OK == pun->Show(SAFECAST(ppbr, IQueryContinue *), 0))
  332. {
  333. _InvokeFromPropertyBag(pPb);
  334. }
  335. pun->Release();
  336. }
  337. pPb->Release();
  338. }
  339. // No key is showing now...
  340. ppbr->_szKeyShowing[0] = 0;
  341. iReminder++;
  342. }
  343. RegCloseKey(hkeyReminders);
  344. SHDeleteKey(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS); // Recursive delete
  345. }
  346. ppbr->Release();
  347. return 0;
  348. }
  349. HRESULT CreateUserNotificationFromPropertyBag(IPropertyBag* pPb, IUserNotification** ppun)
  350. {
  351. HRESULT hr = CUserNotification_CreateInstance(NULL, IID_PPV_ARG(IUserNotification, ppun));
  352. if (SUCCEEDED(hr))
  353. {
  354. hr = _SetBalloonInfoFromPropertyBag(pPb, *ppun);
  355. if (SUCCEEDED(hr))
  356. {
  357. _SetBalloonRetryFromPropertyBag(pPb, *ppun);
  358. _SetBalloonIconFromPropertyBag(pPb, *ppun);
  359. }
  360. else
  361. {
  362. (*ppun)->Release();
  363. *ppun = NULL;
  364. }
  365. }
  366. return hr;
  367. }
  368. HRESULT GetSubKeyPropertyBag(HKEY hkey, DWORD iSubKey, DWORD grfMode, IPropertyBag** ppPb, TCHAR *pszKey, DWORD cbKey)
  369. {
  370. *ppPb = NULL;
  371. TCHAR szName[256];
  372. DWORD cchSize = ARRAYSIZE(szName);
  373. LONG lResult = RegEnumKeyEx(hkey, iSubKey, szName, &cchSize, NULL, NULL, NULL, NULL);
  374. if (ERROR_NO_MORE_ITEMS == lResult)
  375. return S_FALSE;
  376. if (ERROR_SUCCESS != lResult)
  377. return E_FAIL;
  378. StrCpyN(pszKey, REGPATH_POSTBOOTREMINDERS, cbKey);
  379. StrCatBuff(pszKey, TEXT("\\"), cbKey);
  380. StrCatBuff(pszKey, szName, cbKey);
  381. return SHCreatePropertyBagOnRegKey(hkey, szName, grfMode, IID_PPV_ARG(IPropertyBag, ppPb));
  382. }