466 lines
13 KiB
C++
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));
|
||
|
}
|
||
|
|