Leaked source code of windows server 2003
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.

467 lines
14 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. ASSERT( 0 != _cRef );
  79. ULONG cRef = InterlockedDecrement(&_cRef);
  80. if ( 0 == cRef )
  81. {
  82. delete this;
  83. }
  84. return cRef;
  85. }
  86. // IShellReminderManager
  87. HRESULT CPostBootReminder::Add(const SHELLREMINDER* psr)
  88. {
  89. HRESULT hr = E_FAIL;
  90. // Ensure the parent key is created
  91. HKEY hkeyCurrentUser;
  92. if (ERROR_SUCCESS == RegOpenCurrentUser(KEY_WRITE, &hkeyCurrentUser))
  93. {
  94. HKEY hkeyReminders;
  95. if (ERROR_SUCCESS == RegCreateKeyEx(hkeyCurrentUser, REGPATH_POSTBOOTREMINDERS, 0, NULL, 0, KEY_WRITE, NULL, &hkeyReminders, NULL))
  96. {
  97. IPropertyBag* pPb;
  98. hr = SHCreatePropertyBagOnRegKey(hkeyReminders, psr->pszName, STGM_WRITE | STGM_CREATE, IID_PPV_ARG(IPropertyBag, &pPb));
  99. if (SUCCEEDED(hr))
  100. {
  101. // need to check the SHELLREMINDER values for null or we will RIP in SHPropertyBag_WriteStr/GUID
  102. if (psr->pszTitle)
  103. {
  104. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TITLE, psr->pszTitle);
  105. }
  106. if (psr->pszText)
  107. {
  108. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TEXT, psr->pszText);
  109. }
  110. if (psr->pszTooltip)
  111. {
  112. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TOOLTIP, psr->pszTooltip);
  113. }
  114. if (psr->pszIconResource)
  115. {
  116. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_ICONRESOURCE, psr->pszIconResource);
  117. }
  118. if (psr->pszShellExecute)
  119. {
  120. SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_SHELLEXECUTE, psr->pszShellExecute);
  121. }
  122. if (psr->pclsid)
  123. {
  124. SHPropertyBag_WriteGUID(pPb, PROP_POSTBOOT_CLSID, psr->pclsid);
  125. }
  126. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_SHOWTIME, psr->dwShowTime);
  127. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_RETRYINTERVAL, psr->dwRetryInterval);
  128. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_RETRYCOUNT, psr->dwRetryCount);
  129. SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_TYPEFLAGS, psr->dwTypeFlags);
  130. pPb->Release();
  131. }
  132. RegCloseKey(hkeyReminders);
  133. hr = S_OK;
  134. }
  135. RegCloseKey(hkeyCurrentUser);
  136. }
  137. return hr;
  138. }
  139. HRESULT CPostBootReminder::Delete(LPCWSTR pszName)
  140. {
  141. HRESULT hr = E_FAIL;
  142. HKEY hKey;
  143. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS, 0, KEY_WRITE, &hKey))
  144. {
  145. SHDeleteKey(hKey, pszName);
  146. RegCloseKey(hKey);
  147. hr = S_OK;
  148. }
  149. else
  150. {
  151. hr = S_FALSE;
  152. }
  153. return hr;
  154. }
  155. HRESULT CPostBootReminder::Enum(IEnumShellReminder** ppesr)
  156. {
  157. *ppesr = NULL;
  158. return E_NOTIMPL;
  159. }
  160. // IOleCommandTarget implementation
  161. HRESULT CPostBootReminder::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText)
  162. {
  163. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  164. if (*pguidCmdGroup == CGID_ShellServiceObject)
  165. {
  166. // We like Shell Service Object notifications...
  167. hr = S_OK;
  168. }
  169. return hr;
  170. }
  171. HRESULT CPostBootReminder::Exec(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut)
  172. {
  173. HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
  174. if (*pguidCmdGroup == CGID_ShellServiceObject)
  175. {
  176. hr = S_OK; // Any ol' notification is ok with us
  177. // Handle Shell Service Object notifications here.
  178. switch (nCmdID)
  179. {
  180. case SSOCMDID_OPEN:
  181. AddRef(); // AddRef so that this instance stays around. An equivalent Release() is in _ThreadProc
  182. if (!SHCreateThread(_ThreadProc, this, CTF_COINIT, NULL))
  183. {
  184. Release();
  185. }
  186. break;
  187. }
  188. }
  189. return hr;
  190. }
  191. // IQueryContinue implementation
  192. HRESULT CPostBootReminder::QueryContinue()
  193. {
  194. HRESULT hr = S_OK;
  195. if (_szKeyShowing[0])
  196. {
  197. HKEY hKey;
  198. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _szKeyShowing, 0, KEY_READ, &hKey))
  199. {
  200. RegCloseKey(hKey);
  201. }
  202. else
  203. {
  204. hr = S_FALSE;
  205. }
  206. }
  207. return hr;
  208. }
  209. // Function prototypes
  210. HRESULT CreateUserNotificationFromPropertyBag(IPropertyBag* pPb, IUserNotification** ppun);
  211. HRESULT _SetBalloonInfoFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun);
  212. HRESULT _SetBalloonRetryFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun);
  213. HRESULT _SetBalloonIconFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun);
  214. HRESULT _InvokeFromPropertyBag(IPropertyBag* pPb);
  215. HRESULT GetSubKeyPropertyBag(HKEY hkey, DWORD iSubKey, DWORD grfMode, IPropertyBag** ppPb, TCHAR * szKey, DWORD cchKey);
  216. HRESULT _SetBalloonInfoFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun)
  217. {
  218. TCHAR szTitle[256];
  219. HRESULT hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TITLE, szTitle, ARRAYSIZE(szTitle));
  220. if (SUCCEEDED(hr))
  221. {
  222. TCHAR szText[512];
  223. hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TEXT, szText, ARRAYSIZE(szText));
  224. if (SUCCEEDED(hr))
  225. {
  226. DWORD dwFlags = 0;
  227. hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_TYPEFLAGS, &dwFlags);
  228. if (SUCCEEDED(hr))
  229. {
  230. hr = pun->SetBalloonInfo(szTitle, szText, dwFlags);
  231. }
  232. }
  233. }
  234. return hr;
  235. }
  236. HRESULT _SetBalloonRetryFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun)
  237. {
  238. DWORD dwShowTime;
  239. HRESULT hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_SHOWTIME, &dwShowTime);
  240. if (SUCCEEDED(hr))
  241. {
  242. DWORD dwRetryInterval;
  243. hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_RETRYINTERVAL, &dwRetryInterval);
  244. if (SUCCEEDED(hr))
  245. {
  246. DWORD dwRetryCount;
  247. hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_RETRYCOUNT, &dwRetryCount);
  248. if (SUCCEEDED(hr))
  249. {
  250. hr = pun->SetBalloonRetry(dwShowTime, dwRetryInterval, dwRetryCount);
  251. }
  252. }
  253. }
  254. return hr;
  255. }
  256. HRESULT _SetBalloonIconFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun)
  257. {
  258. TCHAR szTooltip[256];
  259. HRESULT hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TOOLTIP, szTooltip, ARRAYSIZE(szTooltip));
  260. if (FAILED(hr))
  261. {
  262. *szTooltip = 0;
  263. }
  264. TCHAR szIcon[MAX_PATH + 6];
  265. hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_ICONRESOURCE, szIcon, ARRAYSIZE(szIcon));
  266. if (SUCCEEDED(hr))
  267. {
  268. int iIcon = PathParseIconLocation(szIcon);
  269. HICON hIcon;
  270. hr = (0 == ExtractIconEx(szIcon, iIcon, NULL, &hIcon, 1)) ? E_FAIL : S_OK;
  271. if (SUCCEEDED(hr))
  272. {
  273. pun->SetIconInfo(hIcon, szTooltip);
  274. DestroyIcon(hIcon);
  275. }
  276. }
  277. return hr;
  278. }
  279. HRESULT _InvokeFromPropertyBag(IPropertyBag* pPb)
  280. {
  281. // First try to use the CLSID to find a handler for the click
  282. CLSID clsid;
  283. HRESULT hr = SHPropertyBag_ReadGUID(pPb, PROP_POSTBOOT_CLSID, &clsid);
  284. if (SUCCEEDED(hr))
  285. {
  286. IContextMenu* pcm;
  287. hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm));
  288. if (SUCCEEDED(hr))
  289. {
  290. CMINVOKECOMMANDINFO ici = {0};
  291. ici.cbSize = sizeof(ici);
  292. ici.lpVerb = "open";
  293. ici.nShow = SW_SHOWNORMAL;
  294. pcm->InvokeCommand(&ici);
  295. pcm->Release();
  296. }
  297. }
  298. if (FAILED(hr))
  299. {
  300. // Second, use the shellexecute line
  301. TCHAR szExecute[MAX_PATH + 1];
  302. hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_SHELLEXECUTE, szExecute, ARRAYSIZE(szExecute));
  303. if (SUCCEEDED(hr))
  304. {
  305. // Use shellexecuteex to open a view folder
  306. SHELLEXECUTEINFO shexinfo = {0};
  307. shexinfo.cbSize = sizeof (shexinfo);
  308. shexinfo.fMask = SEE_MASK_FLAG_NO_UI;
  309. shexinfo.nShow = SW_SHOWNORMAL;
  310. shexinfo.lpFile = szExecute;
  311. ShellExecuteEx(&shexinfo);
  312. }
  313. }
  314. return hr;
  315. }
  316. DWORD CPostBootReminder::_ThreadProc(void* pv)
  317. {
  318. HKEY hkeyReminders;
  319. CPostBootReminder * ppbr = (CPostBootReminder *) pv;
  320. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS, 0, KEY_READ, &hkeyReminders))
  321. {
  322. DWORD iReminder = 0;
  323. HRESULT hr = S_OK;
  324. while (S_OK == hr)
  325. {
  326. IPropertyBag* pPb;
  327. hr = GetSubKeyPropertyBag(hkeyReminders, iReminder, STGM_READ, &pPb, ppbr->_szKeyShowing, ARRAYSIZE(ppbr->_szKeyShowing));
  328. if (S_OK == hr)
  329. {
  330. IUserNotification* pun;
  331. hr = CreateUserNotificationFromPropertyBag(pPb, &pun);
  332. if (SUCCEEDED(hr))
  333. {
  334. if (S_OK == pun->Show(SAFECAST(ppbr, IQueryContinue *), 0))
  335. {
  336. _InvokeFromPropertyBag(pPb);
  337. }
  338. pun->Release();
  339. }
  340. pPb->Release();
  341. }
  342. // No key is showing now...
  343. ppbr->_szKeyShowing[0] = 0;
  344. iReminder++;
  345. }
  346. RegCloseKey(hkeyReminders);
  347. SHDeleteKey(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS); // Recursive delete
  348. }
  349. ppbr->Release();
  350. return 0;
  351. }
  352. HRESULT CreateUserNotificationFromPropertyBag(IPropertyBag* pPb, IUserNotification** ppun)
  353. {
  354. HRESULT hr = CUserNotification_CreateInstance(NULL, IID_PPV_ARG(IUserNotification, ppun));
  355. if (SUCCEEDED(hr))
  356. {
  357. hr = _SetBalloonInfoFromPropertyBag(pPb, *ppun);
  358. if (SUCCEEDED(hr))
  359. {
  360. _SetBalloonRetryFromPropertyBag(pPb, *ppun);
  361. _SetBalloonIconFromPropertyBag(pPb, *ppun);
  362. }
  363. else
  364. {
  365. (*ppun)->Release();
  366. *ppun = NULL;
  367. }
  368. }
  369. return hr;
  370. }
  371. HRESULT GetSubKeyPropertyBag(HKEY hkey, DWORD iSubKey, DWORD grfMode, IPropertyBag** ppPb, TCHAR *pszKey, DWORD cchKey)
  372. {
  373. *ppPb = NULL;
  374. TCHAR szName[256];
  375. DWORD cchSize = ARRAYSIZE(szName);
  376. LONG lResult = RegEnumKeyEx(hkey, iSubKey, szName, &cchSize, NULL, NULL, NULL, NULL);
  377. if (ERROR_NO_MORE_ITEMS == lResult)
  378. return S_FALSE;
  379. if (ERROR_SUCCESS != lResult)
  380. return E_FAIL;
  381. StringCchCopy(pszKey, cchKey, REGPATH_POSTBOOTREMINDERS);
  382. StringCchCat(pszKey, cchKey, TEXT("\\"));
  383. StringCchCat(pszKey, cchKey, szName);
  384. return SHCreatePropertyBagOnRegKey(hkey, szName, grfMode, IID_PPV_ARG(IPropertyBag, ppPb));
  385. }