windows-nt/Source/XPSP1/NT/shell/shell32/reminder.cpp
2020-09-26 16:20:57 +08:00

466 lines
13 KiB
C++

#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));
}