#include "private.h" #include "subitem.h" #include "subsmgrp.h" #include "helper.h" #include "offl_cpp.h" // Yech. Pidl stuff. const TCHAR c_szSubscriptionInfoValue[] = TEXT("~SubsInfo"); #define c_wszSubscriptionInfoValue c_szSubscriptionInfoValue // pragmas are for compatibility with the notification manager // registry data structures which were not pack 8 #pragma pack(push, RegVariantBlob, 1) //#pragma pack(8) struct NT32PACKAGE { unsigned _int16 vt; /* VARTYPE *//* unsigned short int */ unsigned _int16 wReserved1; /* WORD *//* unsigned short int */ unsigned _int16 wReserved2; /* WORD *//* unsigned short int */ unsigned _int16 wReserved3; /* WORD *//* unsigned short int */ _int64 llVal; /* LONGLONG *//* __int64 */ }; // Q: What's going on here? // A: Not a whole lot. // We used to store a variant in the registry. Unfortunately, variants are // 16 bytes on Win32 (8 byte header + 8 bytes of data) // 24 bytes on Win64 (8 byte header + 16 bytes of data) // Unfortunately, Win64 webcheck and Win32 webcheck both read from // the same registry location, i.e. the same blob, and won't understand one another. // Thus, boom! At least, for BSTRs that are stored inline // Fortunately, we care only about only the first 16 bytes on both platforms. // Ergo, it's sufficient to store only the top half of the Win64 variant. struct SimpleVariantBlob { NT32PACKAGE var; }; struct BSTRVariantBlob : public SimpleVariantBlob { DWORD dwSize; // WCHAR wsz[]; // Variable length string }; struct OldBSTRVariantBlob { DWORD dwSize; VARIANT var; }; struct SignatureSimpleBlob { DWORD dwSignature; SimpleVariantBlob svb; }; struct SignatureBSTRBlob { DWORD dwSignature; BSTRVariantBlob bvb; }; #pragma pack(pop, RegVariantBlob) #define BLOB_SIGNATURE 0x4b434f4d // We need fStream to indicate when we're upgrading IE4-style streams-of-blobs. (IE6 24398) HRESULT BlobToVariant(BYTE *pData, DWORD cbData, VARIANT *pVar, DWORD *pcbUsed, BOOL fStream) { HRESULT hr = S_OK; SimpleVariantBlob *pBlob = (SimpleVariantBlob *)pData; ASSERT(NULL != pBlob); ASSERT(cbData >= sizeof(SimpleVariantBlob)); ASSERT(NULL != pVar); if ((NULL != pBlob) && (cbData >= sizeof(SimpleVariantBlob)) && (NULL != pVar)) { memcpy(pVar, &pBlob->var, sizeof(NT32PACKAGE)); switch (pVar->vt) { case VT_I4: // LONG case VT_UI1: // BYTE case VT_I2: // SHORT case VT_R4: // FLOAT case VT_R8: // DOUBLE case VT_BOOL: // VARIANT_BOOL case VT_ERROR: // SCODE case VT_CY: // CY case VT_DATE: // DATE case VT_I1: // CHAR case VT_UI2: // USHORT case VT_UI4: // ULONG case VT_INT: // INT case VT_UINT: // UINT if (pcbUsed) { *pcbUsed = sizeof(SimpleVariantBlob); } break; case VT_BSTR: // BSTR hr = E_UNEXPECTED; ASSERT(cbData >= sizeof(BSTRVariantBlob)); if (cbData >= sizeof(BSTRVariantBlob)) { BSTRVariantBlob *pbstrBlob = (BSTRVariantBlob *)pData; DWORD dwSize = pbstrBlob->dwSize; #ifdef WIN64 ASSERT((cbData==(sizeof(BSTRVariantBlob) + dwSize)) || (cbData==(sizeof(OldBSTRVariantBlob) + dwSize))); #else ASSERT((cbData==(sizeof(BSTRVariantBlob) + dwSize)) || (fStream && (cbData>=(sizeof(BSTRVariantBlob) + dwSize)))); #endif if ((cbData==(sizeof(BSTRVariantBlob) + dwSize)) || (fStream && (cbData>=(sizeof(BSTRVariantBlob) + dwSize)))) { pVar->bstrVal = SysAllocStringByteLen(NULL, dwSize); if (NULL != pVar->bstrVal) { if (pcbUsed) { *pcbUsed = sizeof(BSTRVariantBlob) + pbstrBlob->dwSize; } memcpy(pVar->bstrVal, ((BYTE *)pbstrBlob) + (FIELD_OFFSET(BSTRVariantBlob, dwSize) + sizeof(dwSize)), dwSize); hr = S_OK; } else { hr = E_OUTOFMEMORY; } } } if (FAILED(hr)) { pVar->vt = VT_EMPTY; } break; default: hr = E_NOTIMPL; break; } } else { hr = E_UNEXPECTED; } return hr; } HRESULT SignatureBlobToVariant(BYTE *pData, DWORD cbData, VARIANT *pVar) { HRESULT hr; SignatureSimpleBlob *pBlob = (SignatureSimpleBlob *)pData; ASSERT(NULL != pBlob); ASSERT(cbData >= sizeof(SignatureSimpleBlob)); ASSERT(NULL != pVar); ASSERT(BLOB_SIGNATURE == pBlob->dwSignature); if ((NULL != pBlob) && (cbData >= sizeof(SignatureSimpleBlob)) && (NULL != pVar) && (BLOB_SIGNATURE == pBlob->dwSignature)) { hr = BlobToVariant((BYTE *)&pBlob->svb, cbData - (FIELD_OFFSET(SignatureSimpleBlob, svb)), pVar, NULL); } else { hr = E_UNEXPECTED; } return hr; } HRESULT VariantToSignatureBlob(const VARIANT *pVar, BYTE **ppData, DWORD *pdwSize) { HRESULT hr; ASSERT(NULL != pVar); ASSERT(NULL != ppData); ASSERT(NULL != pdwSize); if ((NULL != pVar) && (NULL != ppData) && (NULL != pdwSize)) { DWORD dwSize; DWORD dwBstrLen = 0; hr = S_OK; switch (pVar->vt) { case VT_I4: // LONG case VT_UI1: // BYTE case VT_I2: // SHORT case VT_R4: // FLOAT case VT_R8: // DOUBLE case VT_BOOL: // VARIANT_BOOL case VT_ERROR: // SCODE case VT_CY: // CY case VT_DATE: // DATE case VT_I1: // CHAR case VT_UI2: // USHORT case VT_UI4: // ULONG case VT_INT: // INT case VT_UINT: // UINT dwSize = sizeof(SignatureSimpleBlob); break; case VT_BSTR: // BSTR if (NULL != pVar->bstrVal) dwBstrLen = SysStringByteLen(pVar->bstrVal); dwSize = sizeof(SignatureBSTRBlob) + dwBstrLen; break; default: hr = E_NOTIMPL; dwSize = 0; break; } if (SUCCEEDED(hr)) { SignatureSimpleBlob *pSignatureBlob = (SignatureSimpleBlob *)new BYTE[dwSize]; if (NULL != pSignatureBlob) { *ppData = (BYTE *)pSignatureBlob; *pdwSize = dwSize; pSignatureBlob->dwSignature = BLOB_SIGNATURE; switch (pVar->vt) { case VT_I4: // LONG case VT_UI1: // BYTE case VT_I2: // SHORT case VT_R4: // FLOAT case VT_R8: // DOUBLE case VT_BOOL: // VARIANT_BOOL case VT_ERROR: // SCODE case VT_CY: // CY case VT_DATE: // DATE case VT_I1: // CHAR case VT_UI2: // USHORT case VT_UI4: // ULONG case VT_INT: // INT case VT_UINT: // UINT { SimpleVariantBlob *pBlob = &pSignatureBlob->svb; memcpy(&pBlob->var, pVar, sizeof(NT32PACKAGE)); break; } case VT_BSTR: // BSTR { BSTRVariantBlob *pbstrBlob = &((SignatureBSTRBlob *)pSignatureBlob)->bvb; memcpy(&pbstrBlob->var, pVar, sizeof(NT32PACKAGE)); pbstrBlob->dwSize = dwBstrLen; memcpy(((BYTE *)pbstrBlob) + (FIELD_OFFSET(BSTRVariantBlob, dwSize) + sizeof(dwSize)), pVar->bstrVal, dwBstrLen); break; } default: ASSERT(0); // Default case should have been eliminated! break; } } else { hr = E_OUTOFMEMORY; } } } else { hr = E_INVALIDARG; } return hr; } CEnumItemProperties::CEnumItemProperties() { ASSERT(0 == m_nCurrent); ASSERT(0 == m_nCount); ASSERT(NULL == m_pItemProps); m_cRef = 1; DllAddRef(); } CEnumItemProperties::~CEnumItemProperties() { if (NULL != m_pItemProps) { for (ULONG i = 0; i < m_nCount; i++) { VariantClear(&m_pItemProps[i].variantValue); if (NULL != m_pItemProps[i].pwszName) { CoTaskMemFree(m_pItemProps[i].pwszName); } } delete [] m_pItemProps; } DllRelease(); } HRESULT CEnumItemProperties::Initialize(const SUBSCRIPTIONCOOKIE *pCookie, ISubscriptionItem *psi) { HRESULT hr = E_FAIL; HKEY hkey; ASSERT(NULL != pCookie); if (OpenItemKey(pCookie, FALSE, KEY_READ, &hkey)) { DWORD dwMaxValNameSize; DWORD dwMaxDataSize; DWORD dwCount; if (RegQueryInfoKey(hkey, NULL, // address of buffer for class string NULL, // address of size of class string buffer NULL, // reserved NULL, // address of buffer for number of subkeys NULL, // address of buffer for longest subkey name length NULL, // address of buffer for longest class string length &dwCount, // address of buffer for number of value entries &dwMaxValNameSize, // address of buffer for longest value name length &dwMaxDataSize, // address of buffer for longest value data length NULL, // address of buffer for security descriptor length NULL // address of buffer for last write time ) == ERROR_SUCCESS) { // This allocates enough for Password as well m_pItemProps = new ITEMPROP[dwCount]; dwMaxValNameSize++; // Need room for NULL // alloca candidates: TCHAR *pszValName = new TCHAR[dwMaxValNameSize]; BYTE *pData = new BYTE[dwMaxDataSize]; if ((NULL != m_pItemProps) && (NULL != pData) && (NULL != pszValName) ) { hr = S_OK; for (ULONG i = 0; i < dwCount; i++) { DWORD dwType; DWORD dwSize = dwMaxDataSize; DWORD dwNameSize = dwMaxValNameSize; if (SHEnumValue(hkey, i, pszValName, &dwNameSize, &dwType, pData, &dwSize) != ERROR_SUCCESS) { hr = E_UNEXPECTED; break; } // Skip the default value and our subscription info structure if ((NULL == *pszValName) || (0 == StrCmp(pszValName, c_szSubscriptionInfoValue)) ) { continue; } if (dwType != REG_BINARY) { hr = E_UNEXPECTED; break; } hr = SignatureBlobToVariant(pData, dwSize, &m_pItemProps[m_nCount].variantValue); if (SUCCEEDED(hr)) { WCHAR *pwszName; pwszName = pszValName; ULONG ulSize = (lstrlenW(pwszName) + 1) * sizeof(WCHAR); m_pItemProps[m_nCount].pwszName = (WCHAR *)CoTaskMemAlloc(ulSize); if (NULL != m_pItemProps[m_nCount].pwszName) { StrCpyNW(m_pItemProps[m_nCount].pwszName, pwszName, ulSize / sizeof(WCHAR)); } else { hr = E_OUTOFMEMORY; break; } m_nCount++; } else { break; } } if (SUCCEEDED(ReadPassword(psi, &m_pItemProps[m_nCount].variantValue.bstrVal))) { ULONG ulSize = sizeof(L"Password"); m_pItemProps[m_nCount].pwszName = (WCHAR *)CoTaskMemAlloc(ulSize); if (NULL != m_pItemProps[m_nCount].pwszName) { StrCpyNW(m_pItemProps[m_nCount].pwszName, L"Password", ulSize / sizeof(WCHAR)); m_pItemProps[m_nCount].variantValue.vt = VT_BSTR; m_nCount++; } else { SysFreeString(m_pItemProps[m_nCount].variantValue.bstrVal); } } } else { hr = E_OUTOFMEMORY; } SAFEDELETE(pszValName); SAFEDELETE(pData); } RegCloseKey(hkey); } return hr; } // IUnknown members STDMETHODIMP CEnumItemProperties::QueryInterface(REFIID riid, void **ppv) { HRESULT hr; if (NULL == ppv) { return E_INVALIDARG; } if ((IID_IUnknown == riid) || (IID_IEnumItemProperties == riid)) { *ppv = (IEnumItemProperties *)this; AddRef(); hr = S_OK; } else { *ppv = NULL; hr = E_NOINTERFACE; } return hr; } STDMETHODIMP_(ULONG) CEnumItemProperties::AddRef() { return ++m_cRef; } STDMETHODIMP_(ULONG) CEnumItemProperties::Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } HRESULT CEnumItemProperties::CopyItem(ITEMPROP *pip, WCHAR *pwszName, VARIANT *pVar) { HRESULT hr; ASSERT(NULL != pwszName); if (NULL != pwszName) { ULONG cb = (lstrlenW(pwszName) + 1) * sizeof(WCHAR); pip->pwszName = (WCHAR *)CoTaskMemAlloc(cb); if (NULL != pip->pwszName) { StrCpyNW(pip->pwszName, pwszName, cb /sizeof(WCHAR)); pip->variantValue.vt = VT_EMPTY; // is this a good idea? hr = VariantCopy(&pip->variantValue, pVar); } else { hr = E_OUTOFMEMORY; } } else { hr = E_UNEXPECTED; } return hr; } HRESULT CEnumItemProperties::CopyRange(ULONG nStart, ULONG nCount, ITEMPROP *ppip, ULONG *pnCopied) { HRESULT hr = S_OK; ULONG n = 0; ULONG i; ASSERT((NULL != ppip) && (NULL != pnCopied)); for (i = nStart; (S_OK == hr) && (i < m_nCount) && (n < nCount); i++, n++) { hr = CopyItem(&ppip[n], m_pItemProps[i].pwszName, &m_pItemProps[i].variantValue); } *pnCopied = n; if (SUCCEEDED(hr)) { hr = (n == nCount) ? S_OK : S_FALSE; } return hr; } // IEnumItemProperties STDMETHODIMP CEnumItemProperties::Next( /* [in] */ ULONG celt, /* [length_is][size_is][out] */ ITEMPROP *rgelt, /* [out] */ ULONG *pceltFetched) { HRESULT hr; if ((0 == celt) || ((celt > 1) && (NULL == pceltFetched)) || (NULL == rgelt)) { return E_INVALIDARG; } DWORD nFetched; hr = CopyRange(m_nCurrent, celt, rgelt, &nFetched); m_nCurrent += nFetched; if (pceltFetched) { *pceltFetched = nFetched; } return hr; } STDMETHODIMP CEnumItemProperties::Skip( /* [in] */ ULONG celt) { HRESULT hr; m_nCurrent += celt; if (m_nCurrent > (m_nCount - 1)) { m_nCurrent = m_nCount; // Passed the last one hr = S_FALSE; } else { hr = S_OK; } return hr; } STDMETHODIMP CEnumItemProperties::Reset() { m_nCurrent = 0; return S_OK; } STDMETHODIMP CEnumItemProperties::Clone( /* [out] */ IEnumItemProperties **ppenum) { HRESULT hr = E_OUTOFMEMORY; *ppenum = NULL; CEnumItemProperties *peip = new CEnumItemProperties; if (NULL != peip) { peip->m_pItemProps = new ITEMPROP[m_nCount]; if (NULL != peip->m_pItemProps) { ULONG nFetched; hr = E_FAIL; peip->m_nCount = m_nCount; hr = CopyRange(0, m_nCount, peip->m_pItemProps, &nFetched); if (SUCCEEDED(hr)) { ASSERT(m_nCount == nFetched); if (m_nCount == nFetched) { hr = peip->QueryInterface(IID_IEnumItemProperties, (void **)ppenum); } } } peip->Release(); } return hr; } STDMETHODIMP CEnumItemProperties::GetCount( /* [out] */ ULONG *pnCount) { if (NULL == pnCount) { return E_INVALIDARG; } *pnCount = m_nCount; return S_OK; } CSubscriptionItem::CSubscriptionItem(const SUBSCRIPTIONCOOKIE *pCookie, HKEY hkey) { ASSERT(NULL != pCookie); ASSERT(0 == m_dwFlags); m_cRef = 1; if (NULL != pCookie) { m_Cookie = *pCookie; } SUBSCRIPTIONITEMINFO sii; sii.cbSize = sizeof(SUBSCRIPTIONITEMINFO); if ((hkey != NULL) && SUCCEEDED(Read(hkey, c_wszSubscriptionInfoValue, (BYTE *)&sii, sizeof(SUBSCRIPTIONITEMINFO)))) { m_dwFlags = sii.dwFlags; } DllAddRef(); } CSubscriptionItem::~CSubscriptionItem() { if (m_dwFlags & SI_TEMPORARY) { TCHAR szKey[MAX_PATH]; if (ItemKeyNameFromCookie(&m_Cookie, szKey, ARRAYSIZE(szKey))) { SHDeleteKey(HKEY_CURRENT_USER, szKey); } } DllRelease(); } HRESULT CSubscriptionItem::Read(HKEY hkeyIn, const WCHAR *pwszValueName, BYTE *pData, DWORD dwDataSize) { HRESULT hr = E_FAIL; HKEY hkey = hkeyIn; ASSERT((NULL != pwszValueName) && (NULL != pData) && (0 != dwDataSize)); if ((NULL != hkey) || OpenItemKey(&m_Cookie, FALSE, KEY_READ, &hkey)) { DWORD dwType; DWORD dwSize = dwDataSize; if ((RegQueryValueExW(hkey, pwszValueName, NULL, &dwType, pData, &dwSize) == ERROR_SUCCESS) && (dwSize == dwDataSize) && (REG_BINARY == dwType)) { hr = S_OK; } if (NULL == hkeyIn) { RegCloseKey(hkey); } } return hr; } HRESULT CSubscriptionItem::ReadWithAlloc(HKEY hkeyIn, const WCHAR *pwszValueName, BYTE **ppData, DWORD *pdwDataSize) { HRESULT hr = E_FAIL; HKEY hkey = hkeyIn; ASSERT((NULL != pwszValueName) && (NULL != ppData) && (NULL != pdwDataSize)); if ((NULL != hkey) || OpenItemKey(&m_Cookie, FALSE, KEY_READ, &hkey)) { DWORD dwType; DWORD dwSize = 0; if (RegQueryValueExW(hkey, pwszValueName, NULL, &dwType, NULL, &dwSize) == ERROR_SUCCESS) { BYTE *pData = new BYTE[dwSize]; *pdwDataSize = dwSize; if (NULL != pData) { if ((RegQueryValueExW(hkey, pwszValueName, NULL, &dwType, pData, pdwDataSize) == ERROR_SUCCESS) && (dwSize == *pdwDataSize) && (REG_BINARY == dwType)) { *ppData = pData; hr = S_OK; } else { delete [] pData; } } else { hr = E_OUTOFMEMORY; } } if (NULL == hkeyIn) { RegCloseKey(hkey); } } return hr; } HRESULT CSubscriptionItem::Write(HKEY hkeyIn, const WCHAR *pwszValueName, BYTE *pData, DWORD dwDataSize) { HRESULT hr = E_FAIL; HKEY hkey = hkeyIn; ASSERT((NULL != pwszValueName) && (NULL != pData) && (0 != dwDataSize)); if ((NULL != hkey) || OpenItemKey(&m_Cookie, FALSE, KEY_WRITE, &hkey)) { if (RegSetValueExW(hkey, pwszValueName, 0, REG_BINARY, pData, dwDataSize) == ERROR_SUCCESS) { hr = S_OK; } if (NULL == hkeyIn) { RegCloseKey(hkey); } } return hr; } STDMETHODIMP CSubscriptionItem::QueryInterface(REFIID riid, void **ppv) { HRESULT hr; if (NULL == ppv) { return E_INVALIDARG; } if ((IID_IUnknown == riid) || (IID_ISubscriptionItem == riid)) { *ppv = (ISubscriptionItem *)this; AddRef(); hr = S_OK; } else { *ppv = NULL; hr = E_NOINTERFACE; } return hr; } STDMETHODIMP_(ULONG) CSubscriptionItem::AddRef() { return ++m_cRef; } STDMETHODIMP_(ULONG) CSubscriptionItem::Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } STDMETHODIMP CSubscriptionItem::GetCookie(SUBSCRIPTIONCOOKIE *pCookie) { if (NULL == pCookie) { return E_INVALIDARG; } *pCookie = m_Cookie; return S_OK; } STDMETHODIMP CSubscriptionItem::GetSubscriptionItemInfo( /* [out] */ SUBSCRIPTIONITEMINFO *pSubscriptionItemInfo) { HRESULT hr; if ((NULL == pSubscriptionItemInfo) || (pSubscriptionItemInfo->cbSize < sizeof(SUBSCRIPTIONITEMINFO))) { return E_INVALIDARG; } hr = Read(NULL, c_wszSubscriptionInfoValue, (BYTE *)pSubscriptionItemInfo, sizeof(SUBSCRIPTIONITEMINFO)); ASSERT(sizeof(SUBSCRIPTIONITEMINFO) == pSubscriptionItemInfo->cbSize); if (SUCCEEDED(hr) && (sizeof(SUBSCRIPTIONITEMINFO) != pSubscriptionItemInfo->cbSize)) { hr = E_UNEXPECTED; } return hr; } STDMETHODIMP CSubscriptionItem::SetSubscriptionItemInfo( /* [in] */ const SUBSCRIPTIONITEMINFO *pSubscriptionItemInfo) { if ((NULL == pSubscriptionItemInfo) || (pSubscriptionItemInfo->cbSize < sizeof(SUBSCRIPTIONITEMINFO))) { return E_INVALIDARG; } m_dwFlags = pSubscriptionItemInfo->dwFlags; return Write(NULL, c_wszSubscriptionInfoValue, (BYTE *)pSubscriptionItemInfo, sizeof(SUBSCRIPTIONITEMINFO)); } STDMETHODIMP CSubscriptionItem::ReadProperties( ULONG nCount, /* [size_is][in] */ const LPCWSTR rgwszName[], /* [size_is][out] */ VARIANT rgValue[]) { HRESULT hr = S_OK; if ((0 == nCount) || (NULL == rgwszName) || (NULL == rgValue)) { return E_INVALIDARG; } HKEY hkey; if (OpenItemKey(&m_Cookie, FALSE, KEY_READ, &hkey)) { for (ULONG i = 0; (i < nCount) && (S_OK == hr); i++) { BYTE *pData; DWORD dwDataSize; if (StrCmpIW(rgwszName[i], c_szPropPassword) == 0) { if (SUCCEEDED(ReadPassword(this, &rgValue[i].bstrVal))) { rgValue[i].vt = VT_BSTR; } else { rgValue[i].vt = VT_EMPTY; } } else { HRESULT hrRead = ReadWithAlloc(hkey, rgwszName[i], &pData, &dwDataSize); if (SUCCEEDED(hrRead)) { hr = SignatureBlobToVariant(pData, dwDataSize, &rgValue[i]); delete [] pData; } else { rgValue[i].vt = VT_EMPTY; } } } RegCloseKey(hkey); } else { hr = E_FAIL; } return hr; } STDMETHODIMP CSubscriptionItem::WriteProperties( ULONG nCount, /* [size_is][in] */ const LPCWSTR rgwszName[], /* [size_is][in] */ const VARIANT rgValue[]) { HRESULT hr = S_OK; if ((0 == nCount) || (NULL == rgwszName) || (NULL == rgValue)) { return E_INVALIDARG; } HKEY hkey; if (OpenItemKey(&m_Cookie, FALSE, KEY_WRITE, &hkey)) { for (ULONG i = 0; (i < nCount) && (S_OK == hr); i++) { if (rgValue[i].vt == VT_EMPTY) { // We don't actually care if this fails since it is // meant to delete the property anyhow RegDeleteValueW(hkey, rgwszName[i]); } else { BYTE *pData; DWORD dwDataSize; // Special case the name property for easy viewing if ((VT_BSTR == rgValue[i].vt) && (StrCmpIW(rgwszName[i], c_szPropName) == 0)) { RegSetValueExW(hkey, NULL, 0, REG_SZ, (LPBYTE)rgValue[i].bstrVal, (lstrlenW(rgValue[i].bstrVal) + 1) * sizeof(WCHAR)); } if (StrCmpIW(rgwszName[i], c_szPropPassword) == 0) { if (VT_BSTR == rgValue[i].vt) { hr = WritePassword(this, rgValue[i].bstrVal); } else { hr = E_INVALIDARG; } } else { hr = VariantToSignatureBlob(&rgValue[i], &pData, &dwDataSize); if (SUCCEEDED(hr)) { hr = Write(hkey, rgwszName[i], pData, dwDataSize); delete [] pData; } } } } RegCloseKey(hkey); } else { hr = E_FAIL; } return hr; } STDMETHODIMP CSubscriptionItem::EnumProperties( /* [out] */ IEnumItemProperties **ppEnumItemProperties) { HRESULT hr; if (NULL == ppEnumItemProperties) { return E_INVALIDARG; } CEnumItemProperties *peip = new CEnumItemProperties; *ppEnumItemProperties = NULL; if (NULL != peip) { hr = peip->Initialize(&m_Cookie, this); if (SUCCEEDED(hr)) { hr = peip->QueryInterface(IID_IEnumItemProperties, (void **)ppEnumItemProperties); } peip->Release(); } else { hr = E_OUTOFMEMORY; } return hr; } STDMETHODIMP CSubscriptionItem::NotifyChanged() { HRESULT hr; // Notify the shell folder of the updated item // This is SOMEWHAT inefficient... why do we need 1000 properties for a pidl? // Why do we copy them around? Why why why? OOEBuf ooeBuf; LPMYPIDL newPidl = NULL; DWORD dwSize = 0; memset(&ooeBuf, 0, sizeof(ooeBuf)); hr = LoadWithCookie(NULL, &ooeBuf, &dwSize, &m_Cookie); if (SUCCEEDED(hr)) { newPidl = COfflineFolderEnum::NewPidl(dwSize); if (newPidl) { CopyToMyPooe(&ooeBuf, &(newPidl->ooe)); _GenerateEvent(SHCNE_UPDATEITEM, (LPITEMIDLIST)newPidl, NULL); COfflineFolderEnum::FreePidl(newPidl); } } return hr; }