|
|
#include "shellprv.h"
#include "clsobj.h"
#include "ole2dup.h"
class CPostBootReminder : public IShellReminderManager, public IOleCommandTarget, public IQueryContinue { public: // IUnknown
STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppv); STDMETHOD_(ULONG, AddRef)(); STDMETHOD_(ULONG, Release)();
// IShellReminderManager
STDMETHOD(Add)(const SHELLREMINDER* psr); STDMETHOD(Delete)(LPCWSTR pszName); STDMETHOD(Enum)(IEnumShellReminder** ppesr);
// IOleCommandTarget Implementation (used to display the PostBootReminders as a shell service object)
STDMETHOD(QueryStatus)(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText); STDMETHOD(Exec)(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut);
// IQueryContinue
STDMETHOD(QueryContinue)(void);
CPostBootReminder(); private: static DWORD _ThreadProc(void* pv);
LONG _cRef; TCHAR _szKeyShowing[MAX_PATH]; };
HRESULT CPostBootReminder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { if (NULL != punkOuter) { return CLASS_E_NOAGGREGATION; }
CPostBootReminder* pPbr = new CPostBootReminder();
if (!pPbr) { return E_OUTOFMEMORY; }
HRESULT hr = pPbr->QueryInterface(riid, ppv); pPbr->Release();
return hr; }
// Per-user (HKCU)
#define REGPATH_POSTBOOTREMINDERS REGSTR_PATH_EXPLORER TEXT("\\PostBootReminders")
#define REGPATH_POSTBOOTTODO REGSTR_PATH_EXPLORER TEXT("\\PostBootToDo")
#define PROP_POSTBOOT_TITLE TEXT("Title") // REG_SZ
#define PROP_POSTBOOT_TEXT TEXT("Text") // REG_SZ
#define PROP_POSTBOOT_TOOLTIP TEXT("ToolTip") // REG_SZ
#define PROP_POSTBOOT_CLSID TEXT("Clsid") // REG_SZ
#define PROP_POSTBOOT_SHELLEXECUTE TEXT("ShellExecute") // REG_SZ
#define PROP_POSTBOOT_ICONRESOURCE TEXT("IconResource") // REG_SZ "module,-resid"
#define PROP_POSTBOOT_SHOWTIME TEXT("ShowTime") // REG_DWORD
#define PROP_POSTBOOT_RETRYINTERVAL TEXT("RetryInterval") // REG_DWORD
#define PROP_POSTBOOT_RETRYCOUNT TEXT("RetryCount") // REG_DWORD
#define PROP_POSTBOOT_TYPEFLAGS TEXT("TypeFlags") // REG_DWORD (NIIF_WARNING, NIIF_INFO, NIIF_ERROR)
CPostBootReminder::CPostBootReminder() { _cRef = 1; }
// IUnknown
HRESULT CPostBootReminder::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CPostBootReminder, IShellReminderManager), QITABENT(CPostBootReminder, IOleCommandTarget), QITABENT(CPostBootReminder, IQueryContinue), { 0 }, }; return QISearch(this, qit, riid, ppv); }
ULONG CPostBootReminder::AddRef() { return InterlockedIncrement(&_cRef); }
ULONG CPostBootReminder::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
// IShellReminderManager
HRESULT CPostBootReminder::Add(const SHELLREMINDER* psr) { HRESULT hr = E_FAIL;
// Ensure the parent key is created
HKEY hkeyCurrentUser; if (ERROR_SUCCESS == RegOpenCurrentUser(KEY_WRITE, &hkeyCurrentUser)) { HKEY hkeyReminders; if (ERROR_SUCCESS == RegCreateKeyEx(hkeyCurrentUser, REGPATH_POSTBOOTREMINDERS, 0, NULL, 0, KEY_WRITE, NULL, &hkeyReminders, NULL)) { IPropertyBag* pPb; hr = SHCreatePropertyBagOnRegKey(hkeyReminders, psr->pszName, STGM_WRITE | STGM_CREATE, IID_PPV_ARG(IPropertyBag, &pPb));
if (SUCCEEDED(hr)) { // need to check the SHELLREMINDER values for null or we will RIP in SHPropertyBag_WriteStr/GUID
if (psr->pszTitle) { SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TITLE, psr->pszTitle); }
if (psr->pszText) { SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TEXT, psr->pszText); } if (psr->pszTooltip) { SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_TOOLTIP, psr->pszTooltip); }
if (psr->pszIconResource) { SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_ICONRESOURCE, psr->pszIconResource); }
if (psr->pszShellExecute) { SHPropertyBag_WriteStr(pPb, PROP_POSTBOOT_SHELLEXECUTE, psr->pszShellExecute); }
if (psr->pclsid) { SHPropertyBag_WriteGUID(pPb, PROP_POSTBOOT_CLSID, psr->pclsid); }
SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_SHOWTIME, psr->dwShowTime); SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_RETRYINTERVAL, psr->dwRetryInterval); SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_RETRYCOUNT, psr->dwRetryCount); SHPropertyBag_WriteDWORD(pPb, PROP_POSTBOOT_TYPEFLAGS, psr->dwTypeFlags);
pPb->Release(); }
RegCloseKey(hkeyReminders); hr = S_OK; } RegCloseKey(hkeyCurrentUser); }
return hr; }
HRESULT CPostBootReminder::Delete(LPCWSTR pszName) { HRESULT hr = E_FAIL;
HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS, 0, KEY_WRITE, &hKey)) { SHDeleteKey(hKey, pszName); RegCloseKey(hKey); hr = S_OK; } else { hr = S_FALSE; }
return hr; }
HRESULT CPostBootReminder::Enum(IEnumShellReminder** ppesr) { *ppesr = NULL; return E_NOTIMPL; }
// IOleCommandTarget implementation
HRESULT CPostBootReminder::QueryStatus(const GUID* pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT* pCmdText) { HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
if (*pguidCmdGroup == CGID_ShellServiceObject) { // We like Shell Service Object notifications...
hr = S_OK; }
return hr; }
HRESULT CPostBootReminder::Exec(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdExecOpt, VARIANTARG* pvaIn, VARIANTARG* pvaOut) { HRESULT hr = OLECMDERR_E_UNKNOWNGROUP;
if (*pguidCmdGroup == CGID_ShellServiceObject) { hr = S_OK; // Any ol' notification is ok with us
// Handle Shell Service Object notifications here.
switch (nCmdID) { case SSOCMDID_OPEN: AddRef(); // AddRef so that this instance stays around. An equivalent Release() is in _ThreadProc
if (!SHCreateThread(_ThreadProc, this, CTF_COINIT, NULL)) { Release(); } break; } }
return hr; }
// IQueryContinue implementation
HRESULT CPostBootReminder::QueryContinue() { HRESULT hr = S_OK;
if (_szKeyShowing[0]) { HKEY hKey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, _szKeyShowing, 0, KEY_READ, &hKey)) { RegCloseKey(hKey); } else { hr = S_FALSE; } }
return hr; }
// Function prototypes
HRESULT CreateUserNotificationFromPropertyBag(IPropertyBag* pPb, IUserNotification** ppun); HRESULT _SetBalloonInfoFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun); HRESULT _SetBalloonRetryFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun); HRESULT _SetBalloonIconFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun); HRESULT _InvokeFromPropertyBag(IPropertyBag* pPb);
HRESULT GetSubKeyPropertyBag(HKEY hkey, DWORD iSubKey, DWORD grfMode, IPropertyBag** ppPb, TCHAR * szKey, DWORD cbKey);
HRESULT _SetBalloonInfoFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun) { TCHAR szTitle[256]; HRESULT hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TITLE, szTitle, ARRAYSIZE(szTitle)); if (SUCCEEDED(hr)) { TCHAR szText[512]; hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TEXT, szText, ARRAYSIZE(szText)); if (SUCCEEDED(hr)) { DWORD dwFlags = 0; hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_TYPEFLAGS, &dwFlags); if (SUCCEEDED(hr)) { hr = pun->SetBalloonInfo(szTitle, szText, dwFlags); } } }
return hr; }
HRESULT _SetBalloonRetryFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun) { DWORD dwShowTime; HRESULT hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_SHOWTIME, &dwShowTime); if (SUCCEEDED(hr)) { DWORD dwRetryInterval; hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_RETRYINTERVAL, &dwRetryInterval); if (SUCCEEDED(hr)) { DWORD dwRetryCount; hr = SHPropertyBag_ReadDWORD(pPb, PROP_POSTBOOT_RETRYCOUNT, &dwRetryCount); if (SUCCEEDED(hr)) { hr = pun->SetBalloonRetry(dwShowTime, dwRetryInterval, dwRetryCount); } } }
return hr; }
HRESULT _SetBalloonIconFromPropertyBag(IPropertyBag* pPb, IUserNotification* pun) { TCHAR szTooltip[256]; HRESULT hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_TOOLTIP, szTooltip, ARRAYSIZE(szTooltip));
if (FAILED(hr)) { *szTooltip = 0; }
TCHAR szIcon[MAX_PATH + 6]; hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_ICONRESOURCE, szIcon, ARRAYSIZE(szIcon)); if (SUCCEEDED(hr)) { int iIcon = PathParseIconLocation(szIcon); HICON hIcon; hr = (0 == ExtractIconEx(szIcon, iIcon, NULL, &hIcon, 1)) ? E_FAIL : S_OK; if (SUCCEEDED(hr)) { pun->SetIconInfo(hIcon, szTooltip);
DestroyIcon(hIcon); } }
return hr; }
HRESULT _InvokeFromPropertyBag(IPropertyBag* pPb) { // First try to use the CLSID to find a handler for the click
CLSID clsid; HRESULT hr = SHPropertyBag_ReadGUID(pPb, PROP_POSTBOOT_CLSID, &clsid);
if (SUCCEEDED(hr)) { IContextMenu* pcm; hr = SHExtCoCreateInstance(NULL, &clsid, NULL, IID_PPV_ARG(IContextMenu, &pcm)); if (SUCCEEDED(hr)) { CMINVOKECOMMANDINFO ici = {0}; ici.cbSize = sizeof(ici); ici.lpVerb = "open"; ici.nShow = SW_SHOWNORMAL;
pcm->InvokeCommand(&ici); pcm->Release(); } }
if (FAILED(hr)) { // Second, use the shellexecute line
TCHAR szExecute[MAX_PATH + 1]; hr = SHPropertyBag_ReadStr(pPb, PROP_POSTBOOT_SHELLEXECUTE, szExecute, ARRAYSIZE(szExecute)); if (SUCCEEDED(hr)) { // Use shellexecuteex to open a view folder
SHELLEXECUTEINFO shexinfo = {0}; shexinfo.cbSize = sizeof (shexinfo); shexinfo.fMask = SEE_MASK_FLAG_NO_UI; shexinfo.nShow = SW_SHOWNORMAL; shexinfo.lpFile = szExecute;
ShellExecuteEx(&shexinfo); } }
return hr; }
DWORD CPostBootReminder::_ThreadProc(void* pv) { HKEY hkeyReminders; CPostBootReminder * ppbr = (CPostBootReminder *) pv;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS, 0, KEY_READ, &hkeyReminders)) { DWORD iReminder = 0; HRESULT hr = S_OK; while (S_OK == hr) { IPropertyBag* pPb; hr = GetSubKeyPropertyBag(hkeyReminders, iReminder, STGM_READ, &pPb, ppbr->_szKeyShowing, ARRAYSIZE(ppbr->_szKeyShowing)); if (S_OK == hr) { IUserNotification* pun; hr = CreateUserNotificationFromPropertyBag(pPb, &pun); if (SUCCEEDED(hr)) { if (S_OK == pun->Show(SAFECAST(ppbr, IQueryContinue *), 0)) { _InvokeFromPropertyBag(pPb); } pun->Release(); }
pPb->Release(); }
// No key is showing now...
ppbr->_szKeyShowing[0] = 0;
iReminder++; }
RegCloseKey(hkeyReminders);
SHDeleteKey(HKEY_CURRENT_USER, REGPATH_POSTBOOTREMINDERS); // Recursive delete
} ppbr->Release();
return 0; }
HRESULT CreateUserNotificationFromPropertyBag(IPropertyBag* pPb, IUserNotification** ppun) { HRESULT hr = CUserNotification_CreateInstance(NULL, IID_PPV_ARG(IUserNotification, ppun)); if (SUCCEEDED(hr)) { hr = _SetBalloonInfoFromPropertyBag(pPb, *ppun);
if (SUCCEEDED(hr)) { _SetBalloonRetryFromPropertyBag(pPb, *ppun); _SetBalloonIconFromPropertyBag(pPb, *ppun); } else { (*ppun)->Release(); *ppun = NULL; } } return hr; }
HRESULT GetSubKeyPropertyBag(HKEY hkey, DWORD iSubKey, DWORD grfMode, IPropertyBag** ppPb, TCHAR *pszKey, DWORD cbKey) { *ppPb = NULL;
TCHAR szName[256]; DWORD cchSize = ARRAYSIZE(szName); LONG lResult = RegEnumKeyEx(hkey, iSubKey, szName, &cchSize, NULL, NULL, NULL, NULL);
if (ERROR_NO_MORE_ITEMS == lResult) return S_FALSE;
if (ERROR_SUCCESS != lResult) return E_FAIL;
StrCpyN(pszKey, REGPATH_POSTBOOTREMINDERS, cbKey); StrCatBuff(pszKey, TEXT("\\"), cbKey); StrCatBuff(pszKey, szName, cbKey);
return SHCreatePropertyBagOnRegKey(hkey, szName, grfMode, IID_PPV_ARG(IPropertyBag, ppPb)); }
|