1368 lines
33 KiB
C++
1368 lines
33 KiB
C++
///////////////////////////////////////////////////////////////////////////////
|
|
/* File: snapin.cpp
|
|
|
|
Description: Implements the MMC snapin for disk quota policy.
|
|
|
|
Revision History:
|
|
|
|
Date Description Programmer
|
|
-------- --------------------------------------------------- ----------
|
|
02/14/98 Initial creation. BrianAu
|
|
06/25/98 Disabled snapin code with #ifdef POLICY_MMC_SNAPIN. BrianAu
|
|
Switching to ADM-file approach to entering policy
|
|
data. Keeping snapin code available in case
|
|
we decide to switch back at a later time.
|
|
*/
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.h"
|
|
#pragma hdrstop
|
|
|
|
#ifdef POLICY_MMC_SNAPIN
|
|
|
|
#include "snapin.h"
|
|
#include "resource.h"
|
|
#include "guidsp.h"
|
|
#include "policy.h"
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CSnapInItem
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// General rendering function usable by this class and any
|
|
// derived classes to render data onto a medium. Only accepts
|
|
// TYMED_HGLOBAL.
|
|
//
|
|
HRESULT
|
|
CSnapInItem::RenderData( // [ static ]
|
|
LPVOID pvData,
|
|
int cbData,
|
|
LPSTGMEDIUM pMedium
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInItem::RenderData [ general ]")));
|
|
HRESULT hr = DV_E_TYMED;
|
|
|
|
if (NULL == pvData || NULL == pMedium)
|
|
return E_INVALIDARG;
|
|
|
|
//
|
|
// Make sure the type medium is HGLOBAL
|
|
//
|
|
if (TYMED_HGLOBAL == pMedium->tymed)
|
|
{
|
|
//
|
|
// Create the stream on the hGlobal passed in
|
|
//
|
|
LPSTREAM pStream;
|
|
hr = CreateStreamOnHGlobal(pMedium->hGlobal, FALSE, &pStream);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Write to the stream the number of bytes
|
|
//
|
|
unsigned long written;
|
|
|
|
hr = pStream->Write(pvData, cbData, &written);
|
|
|
|
//
|
|
// Because we told CreateStreamOnHGlobal with 'FALSE',
|
|
// only the stream is released here.
|
|
// Note - the caller (i.e. snap-in, object) will free the HGLOBAL
|
|
// at the correct time. This is according to the IDataObject specification.
|
|
//
|
|
pStream->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Format a GUID as a string.
|
|
//
|
|
void
|
|
CSnapInItem::GUIDToString( // [ static ]
|
|
const GUID& guid,
|
|
CString *pstr
|
|
)
|
|
{
|
|
StringFromGUID2(guid, pstr->GetBuffer(50), 50);
|
|
pstr->ReleaseBuffer();
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CScopeItem
|
|
//-----------------------------------------------------------------------------
|
|
UINT CScopeItem::m_cfNodeType = RegisterClipboardFormat(CCF_NODETYPE);
|
|
UINT CScopeItem::m_cfNodeTypeString = RegisterClipboardFormat(CCF_SZNODETYPE);
|
|
UINT CScopeItem::m_cfDisplayName = RegisterClipboardFormat(CCF_DISPLAY_NAME);
|
|
UINT CScopeItem::m_cfCoClass = RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
|
|
|
|
//
|
|
// Returns: S_OK = Data successfully rendered.
|
|
// DV_E_CLIPFORMAT = Clipboard format not supported.
|
|
// DV_E_TYMED = Medium is not HGLOBAL.
|
|
// Other = Rendering error.
|
|
HRESULT
|
|
CScopeItem::RenderData(
|
|
UINT cf,
|
|
LPSTGMEDIUM pMedium
|
|
) const
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CScopeItem::RenderData")));
|
|
HRESULT hr = DV_E_CLIPFORMAT;
|
|
|
|
if (cf == m_cfNodeType)
|
|
{
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("Rendering CCF_NODETYPE")));
|
|
hr = CSnapInItem::RenderData((LPVOID)&m_idType, sizeof(m_idType), pMedium);
|
|
}
|
|
else if (cf == m_cfNodeTypeString)
|
|
{
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("Rendering CCF_SZNODETYPE")));
|
|
CString s;
|
|
GUIDToString(m_idType, &s);
|
|
hr = CSnapInItem::RenderData((LPVOID)s.Cstr(), (s.Length() + 1) * sizeof(TCHAR), pMedium);
|
|
}
|
|
else if (cf == m_cfDisplayName)
|
|
{
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("Rendering CCF_DISPLAY_NAME")));
|
|
const CString& s = m_cd.DisplayName();
|
|
hr = CSnapInItem::RenderData((LPVOID)s.Cstr(), (s.Length() + 1) * sizeof(TCHAR), pMedium);
|
|
}
|
|
else if (cf == m_cfCoClass)
|
|
{
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("Rendering CCF_SNAPIN_CLASSID")));
|
|
hr = CSnapInItem::RenderData((LPVOID)&m_cd.ClassId(), sizeof(GUID), pMedium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
CScopeItem::~CScopeItem(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CScopeItem::~CScopeItem")));
|
|
while(0 < m_rgpChildren.Count())
|
|
{
|
|
delete m_rgpChildren[0];
|
|
m_rgpChildren.Delete(0);
|
|
}
|
|
while(0 < m_rgpResultItems.Count())
|
|
{
|
|
delete m_rgpResultItems[0];
|
|
m_rgpResultItems.Delete(0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CSnapInComp
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CSnapInComp::CSnapInComp(
|
|
HINSTANCE hInstance,
|
|
CSnapInCompData& cd
|
|
) : m_cRef(0),
|
|
m_hInstance(hInstance),
|
|
m_cd(cd),
|
|
m_pConsole(NULL),
|
|
m_pResult(NULL),
|
|
m_pHeader(NULL),
|
|
m_pImageResult(NULL),
|
|
m_strColumn(hInstance, IDS_SNAPIN_COLUMN),
|
|
m_cxColumn(200),
|
|
m_lViewMode(LVS_ICON)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInComp::CSnapInComp")));
|
|
}
|
|
|
|
|
|
CSnapInComp::~CSnapInComp(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInComp::~CSnapInComp")));
|
|
|
|
if (NULL != m_pImageResult)
|
|
{
|
|
m_pImageResult->Release();
|
|
m_pImageResult = NULL;
|
|
}
|
|
if (NULL != m_pResult)
|
|
{
|
|
m_pResult->Release();
|
|
m_pResult = NULL;
|
|
}
|
|
if (NULL != m_pHeader)
|
|
{
|
|
m_pHeader->Release();
|
|
m_pHeader = NULL;
|
|
}
|
|
if (NULL != m_pConsole)
|
|
{
|
|
m_pConsole->Release();
|
|
m_pConsole = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::QueryInterface(
|
|
REFIID riid,
|
|
LPVOID *ppvOut
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInComp::QueryInterface")));
|
|
DBGPRINTIID(DM_SNAPIN, DL_MID, riid);
|
|
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
if (NULL == ppvOut)
|
|
return E_INVALIDARG;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
if (IID_IUnknown == riid || IID_IComponent == riid)
|
|
{
|
|
*ppvOut = this;
|
|
}
|
|
else if (IID_IExtendContextMenu == riid)
|
|
{
|
|
*ppvOut = static_cast<IExtendContextMenu *>(this);
|
|
}
|
|
|
|
if (NULL != *ppvOut)
|
|
{
|
|
((LPUNKNOWN)*ppvOut)->AddRef();
|
|
hr = NOERROR;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
CSnapInComp::AddRef(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_LOW, TEXT("CSnapInComp::AddRef")));
|
|
ULONG ulReturn = m_cRef + 1;
|
|
InterlockedIncrement(&m_cRef);
|
|
return ulReturn;
|
|
}
|
|
|
|
|
|
ULONG
|
|
CSnapInComp::Release(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_LOW, TEXT("CSnapInComp::Release")));
|
|
ULONG ulReturn = m_cRef - 1;
|
|
if (InterlockedDecrement(&m_cRef) == 0)
|
|
{
|
|
delete this;
|
|
ulReturn = 0;
|
|
}
|
|
return ulReturn;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::Initialize(
|
|
LPCONSOLE lpConsole
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::Initialize")));
|
|
HRESULT hr = NOERROR;
|
|
|
|
m_pConsole = lpConsole;
|
|
m_pConsole->AddRef();
|
|
|
|
//
|
|
// Get IResultData interface to result pane.
|
|
//
|
|
hr = m_pConsole->QueryInterface(IID_IResultData,
|
|
reinterpret_cast<void **>(&m_pResult));
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
//
|
|
// Get IHeaderCtrl interface to header control.
|
|
//
|
|
hr = m_pConsole->QueryInterface(IID_IHeaderCtrl,
|
|
reinterpret_cast<void **>(&m_pHeader));
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// m_pConsole->SetHeader(m_pHeader); // Needed?
|
|
|
|
hr = m_pConsole->QueryResultImageList(&m_pImageResult);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::Notify(
|
|
LPDATAOBJECT lpDataObject,
|
|
MMC_NOTIFY_TYPE event,
|
|
long arg,
|
|
long param
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::Notify")));
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("\tpObj = 0x%08X, event = %d, arg = %d, param = 0x%08X"), lpDataObject, event, arg, param));
|
|
HRESULT hr = S_OK;
|
|
|
|
switch(event)
|
|
{
|
|
case MMCN_ADD_IMAGES:
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("MMCN_ADD_IMAGES")));
|
|
{
|
|
//
|
|
// Result pane image list has only one icon.
|
|
//
|
|
HICON hIcon = LoadIcon(m_hInstance, MAKEINTRESOURCE(IDI_QUOTA));
|
|
m_pImageResult->ImageListSetIcon(reinterpret_cast<long *>(hIcon), 0);
|
|
}
|
|
break;
|
|
|
|
case MMCN_SHOW:
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("MMCN_SHOW, arg = %d"), arg));
|
|
if (arg)
|
|
{
|
|
//
|
|
// Showing view. Set view information.
|
|
//
|
|
m_pHeader->InsertColumn(0, m_strColumn, LVCFMT_LEFT, m_cxColumn);
|
|
m_pResult->SetViewMode(m_lViewMode);
|
|
|
|
CScopeItem *psi;
|
|
hr = GetScopeItem((HSCOPEITEM)param, &psi);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("Failed getting item for MMCN_SHOW")));
|
|
return hr;
|
|
}
|
|
|
|
RESULTDATAITEM rdi;
|
|
rdi.mask = RDI_STR | RDI_IMAGE | RDI_PARAM;
|
|
int cResultItems = psi->NumResultItems();
|
|
|
|
for (int i = 0; i < cResultItems; i++)
|
|
{
|
|
CResultItem *pri = psi->ResultItem(i);
|
|
DBGASSERT((NULL != pri));
|
|
rdi.str = MMC_CALLBACK;
|
|
rdi.nImage = pri->ImageIndex();
|
|
rdi.lParam = (LPARAM)pri;
|
|
m_pResult->InsertItem(&rdi);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Hiding view. Gather view information.
|
|
//
|
|
m_pHeader->GetColumnWidth(0, &m_cxColumn);
|
|
m_pResult->GetViewMode(&m_lViewMode);
|
|
}
|
|
break;
|
|
|
|
case MMCN_CLICK:
|
|
case MMCN_DBLCLICK:
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("MMCN_CLICK or MMCN_DBLCLICK")));
|
|
m_cd.OpenVolumeQuotaProperties();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::Destroy(
|
|
LONG cookie
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::Destroy")));
|
|
if (NULL != m_pImageResult)
|
|
{
|
|
m_pImageResult->Release();
|
|
m_pImageResult = NULL;
|
|
}
|
|
if (NULL != m_pResult)
|
|
{
|
|
m_pResult->Release();
|
|
m_pResult = NULL;
|
|
}
|
|
if (NULL != m_pHeader)
|
|
{
|
|
m_pHeader->Release();
|
|
m_pHeader = NULL;
|
|
}
|
|
if (NULL != m_pConsole)
|
|
{
|
|
m_pConsole->Release();
|
|
m_pConsole = NULL;
|
|
}
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::QueryDataObject(
|
|
long cookie,
|
|
DATA_OBJECT_TYPES type,
|
|
LPDATAOBJECT *ppDataObject
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::QueryDataObject")));
|
|
//
|
|
// Forward the data object query to the component data object.
|
|
//
|
|
return m_cd.QueryDataObject(cookie, type, ppDataObject);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::GetResultViewType(
|
|
long cookie,
|
|
LPOLESTR *ppViewType,
|
|
long *pViewOptions
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::GetResultViewType")));
|
|
//
|
|
// Tell snapin mgr to use default listview.
|
|
//
|
|
return S_FALSE;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::GetDisplayInfo(
|
|
RESULTDATAITEM *prdi
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::GetDisplayInfo")));
|
|
CSnapInItem *psi = reinterpret_cast<CSnapInItem *>(prdi->lParam);
|
|
|
|
if (RDI_STR & prdi->mask)
|
|
{
|
|
prdi->str = (LPOLESTR)psi->DisplayName().Cstr();
|
|
}
|
|
if (RDI_IMAGE & prdi->mask)
|
|
{
|
|
prdi->nImage = psi->ImageIndex();
|
|
}
|
|
if (RDI_STATE & prdi->mask)
|
|
{
|
|
prdi->nState = 0;
|
|
}
|
|
if (RDI_INDEX & prdi->mask)
|
|
{
|
|
prdi->nIndex = 0;
|
|
}
|
|
if (RDI_INDENT & prdi->mask)
|
|
{
|
|
prdi->iIndent = 0;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::CompareObjects(
|
|
LPDATAOBJECT pdoA,
|
|
LPDATAOBJECT pdoB
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::CompareObjects")));
|
|
return m_cd.CompareObjects(pdoA, pdoB);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::AddMenuItems(
|
|
LPDATAOBJECT pDataObject,
|
|
LPCONTEXTMENUCALLBACK piCallback,
|
|
long *pInsertionAllowed
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::AddMenuItems")));
|
|
return m_cd.AddMenuItems(pDataObject, piCallback, pInsertionAllowed);
|
|
}
|
|
|
|
HRESULT
|
|
CSnapInComp::Command(
|
|
long lCommandID,
|
|
LPDATAOBJECT pDataObject
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInComp::Command")));
|
|
return m_cd.Command(lCommandID, pDataObject);
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CSnapInComp::GetScopeItem(
|
|
HSCOPEITEM hItem,
|
|
CScopeItem **ppsi
|
|
) const
|
|
{
|
|
HRESULT hr;
|
|
SCOPEDATAITEM item;
|
|
item.mask = SDI_PARAM;
|
|
item.ID = hItem;
|
|
|
|
hr = m_cd.m_pScope->GetItem(&item);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*ppsi = reinterpret_cast<CScopeItem *>(item.lParam);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CSnapInCompData
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CSnapInCompData::CSnapInCompData(
|
|
HINSTANCE hInstance,
|
|
LPCTSTR pszDisplayName,
|
|
const GUID& idClass
|
|
) : m_cRef(0),
|
|
m_hInstance(hInstance),
|
|
m_strDisplayName(pszDisplayName),
|
|
m_idClass(idClass),
|
|
m_pConsole(NULL),
|
|
m_pScope(NULL),
|
|
m_pRootScopeItem(NULL),
|
|
m_hRoot(NULL),
|
|
m_pGPEInformation(NULL)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInCompData::CSnapInCompData")));
|
|
|
|
InterlockedIncrement(&g_cRefThisDll);
|
|
|
|
//
|
|
// Root node.
|
|
//
|
|
m_pRootScopeItem = new CScopeItem(NODEID_DiskQuotaRoot, // Node ID
|
|
*this, // Snapin comp data ref.
|
|
NULL, // Parent node ptr.
|
|
TEXT("<root>"), // Node name str.
|
|
-1, // Image index.
|
|
-1); // Image index "open".
|
|
//
|
|
// Add "Disk Quota Settings" as a child of the root.
|
|
//
|
|
CString strName(hInstance, IDS_SNAPIN_SCOPENAME);
|
|
CScopeItem *psi = new CScopeItem(NODEID_DiskQuotaSettings,
|
|
*this,
|
|
m_pRootScopeItem,
|
|
strName,
|
|
iICON_QUOTA,
|
|
iICON_QUOTA_OPEN);
|
|
m_pRootScopeItem->AddChild(psi);
|
|
|
|
//
|
|
// Add single result item to "Disk Quota Settings".
|
|
//
|
|
strName.Format(hInstance, IDS_SNAPIN_RESULTNAME);
|
|
psi->AddResultItem(new CResultItem(strName, 0, *psi));
|
|
}
|
|
|
|
|
|
CSnapInCompData::~CSnapInCompData(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInCompData::~CSnapInCompData")));
|
|
|
|
delete m_pRootScopeItem;
|
|
|
|
if (NULL != m_pScope)
|
|
m_pScope->Release();
|
|
|
|
if (NULL != m_pConsole)
|
|
m_pConsole->Release();
|
|
|
|
if (NULL != m_pGPEInformation)
|
|
m_pGPEInformation->Release();
|
|
|
|
InterlockedDecrement(&g_cRefThisDll);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::QueryInterface(
|
|
REFIID riid,
|
|
LPVOID *ppvOut
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CSnapInCompData::QueryInterface")));
|
|
DBGPRINTIID(DM_SNAPIN, DL_MID, riid);
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
if (NULL == ppvOut)
|
|
return E_INVALIDARG;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
if (IID_IUnknown == riid || IID_IComponentData == riid)
|
|
{
|
|
*ppvOut = this;
|
|
}
|
|
else if (IID_IPersistStreamInit == riid)
|
|
{
|
|
*ppvOut = static_cast<IPersistStreamInit *>(this);
|
|
}
|
|
else if (IID_IExtendContextMenu == riid)
|
|
{
|
|
*ppvOut = static_cast<IExtendContextMenu *>(this);
|
|
}
|
|
|
|
if (NULL != *ppvOut)
|
|
{
|
|
((LPUNKNOWN)*ppvOut)->AddRef();
|
|
hr = NOERROR;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
CSnapInCompData::AddRef(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_LOW, TEXT("CSnapInCompData::AddRef")));
|
|
ULONG ulReturn = m_cRef + 1;
|
|
InterlockedIncrement(&m_cRef);
|
|
return ulReturn;
|
|
}
|
|
|
|
|
|
ULONG
|
|
CSnapInCompData::Release(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_LOW, TEXT("CSnapInCompData::Release")));
|
|
ULONG ulReturn = m_cRef - 1;
|
|
if (InterlockedDecrement(&m_cRef) == 0)
|
|
{
|
|
delete this;
|
|
ulReturn = 0;
|
|
}
|
|
return ulReturn;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::Initialize(
|
|
LPUNKNOWN pUnk
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::Initialize")));
|
|
DBGASSERT((NULL != pUnk));
|
|
DBGASSERT((NULL == m_pConsole));
|
|
|
|
HRESULT hr = pUnk->QueryInterface(IID_IConsole,
|
|
reinterpret_cast<void **>(&m_pConsole));
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("CSnapInCompData failed QI for IID_IConsole")));
|
|
return hr;
|
|
}
|
|
|
|
hr = pUnk->QueryInterface(IID_IConsoleNameSpace,
|
|
reinterpret_cast<void **>(&m_pScope));
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("CSnapInCompData failed QI for IID_IConsoleNameSpace")));
|
|
return hr;
|
|
}
|
|
|
|
LPIMAGELIST pImageList = NULL;
|
|
hr = m_pConsole->QueryScopeImageList(&pImageList);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("CSnapInCompData failed to get scope image list")));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Set the scope pane's image list icons.
|
|
//
|
|
static const struct
|
|
{
|
|
int idIcon;
|
|
int iIconIndex;
|
|
|
|
} rgIcons[] = { { IDI_QUOTA, iICON_QUOTA },
|
|
{ IDI_QUOTA_OPEN, iICON_QUOTA_OPEN } };
|
|
|
|
for (int i = 0; i < ARRAYSIZE(rgIcons); i++)
|
|
{
|
|
HICON hIcon = LoadIcon(m_hInstance,
|
|
MAKEINTRESOURCE(rgIcons[i].idIcon));
|
|
pImageList->ImageListSetIcon(reinterpret_cast<long *>(hIcon),
|
|
rgIcons[i].iIconIndex);
|
|
}
|
|
|
|
pImageList->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::CreateComponent(
|
|
LPCOMPONENT *ppComp
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::CreateComponent")));
|
|
|
|
HRESULT hr = S_OK;
|
|
try
|
|
{
|
|
CSnapInComp *pComp = new CSnapInComp(m_hInstance, *this);
|
|
hr = pComp->QueryInterface(IID_IComponent, reinterpret_cast<void **>(ppComp));
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
DBGERROR((TEXT("Insufficient memory")));
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::Destroy(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::Destroy")));
|
|
|
|
if (NULL != m_pScope)
|
|
{
|
|
m_pScope->Release();
|
|
m_pScope = NULL;
|
|
}
|
|
|
|
if (NULL != m_pConsole)
|
|
{
|
|
m_pConsole->Release();
|
|
m_pConsole = NULL;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::Notify(
|
|
LPDATAOBJECT pDataObject,
|
|
MMC_NOTIFY_TYPE event,
|
|
long arg,
|
|
long param
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::Notify")));
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("\tpDataObj = 0x%08X, event = 0x%08X, arg = %d, param = 0x%08X"),
|
|
pDataObject, event, arg, param));
|
|
HRESULT hr = S_OK;
|
|
try
|
|
{
|
|
switch(event)
|
|
{
|
|
case MMCN_EXPAND:
|
|
if (arg)
|
|
{
|
|
if (NULL == m_pGPEInformation)
|
|
{
|
|
hr = pDataObject->QueryInterface(IID_IGPEInformation,
|
|
reinterpret_cast<void **>(&m_pGPEInformation));
|
|
}
|
|
if (NULL != m_pGPEInformation)
|
|
{
|
|
hr = EnumerateScopePane((HSCOPEITEM)param);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
DBGERROR((TEXT("Insufficient memory")));
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::GetDisplayInfo(
|
|
SCOPEDATAITEM *psdi
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::GetDispInfo")));
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("\tmask = 0x%08X"), psdi->mask));
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
CScopeItem *pItem = reinterpret_cast<CScopeItem *>(psdi->lParam);
|
|
if (psdi->mask & SDI_STR)
|
|
{
|
|
psdi->displayname = (LPOLESTR)((LPCTSTR)pItem->DisplayName());
|
|
}
|
|
if (psdi->mask & SDI_IMAGE)
|
|
{
|
|
psdi->nImage = pItem->ImageIndex();
|
|
}
|
|
if (psdi->mask & SDI_OPENIMAGE)
|
|
{
|
|
psdi->nOpenImage = pItem->OpenImageIndex();
|
|
}
|
|
if (psdi->mask & SDI_CHILDREN)
|
|
{
|
|
psdi->cChildren = pItem->NumChildren();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::CompareObjects(
|
|
LPDATAOBJECT pdoA,
|
|
LPDATAOBJECT pdoB
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::GetDispInfo")));
|
|
LPQUOTADATAOBJECT pDataA, pDataB;
|
|
|
|
HRESULT hr = S_FALSE;
|
|
|
|
if (FAILED(pdoA->QueryInterface(IID_IDiskQuotaSnapInData, reinterpret_cast<void **>(&pDataA))))
|
|
return S_FALSE;
|
|
|
|
if (FAILED(pdoB->QueryInterface(IID_IDiskQuotaSnapInData, reinterpret_cast<void **>(&pDataB))))
|
|
{
|
|
pDataA->Release();
|
|
return FALSE;
|
|
}
|
|
|
|
CSnapInItem *psiA, *psiB;
|
|
pDataA->GetItem(&psiA);
|
|
pDataB->GetItem(&psiB);
|
|
|
|
if (psiA == psiB)
|
|
hr = S_OK;
|
|
|
|
pDataA->Release();
|
|
pDataB->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::QueryDataObject(
|
|
long cookie,
|
|
DATA_OBJECT_TYPES type,
|
|
LPDATAOBJECT *ppDataObject
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::QueryDataObject")));
|
|
|
|
HRESULT hr = E_NOINTERFACE;
|
|
CDataObject *pDataObject = NULL;
|
|
LPQUOTADATAOBJECT pQuotaData = NULL;
|
|
|
|
try
|
|
{
|
|
pDataObject = new CDataObject(*this);
|
|
pDataObject->SetType(type);
|
|
pDataObject->SetItem(reinterpret_cast<CSnapInItem *>(cookie));
|
|
|
|
hr = pDataObject->QueryInterface(IID_IDataObject, (LPVOID *)ppDataObject);
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
DBGERROR((TEXT("Insufficient memory")));
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::EnumerateScopePane (
|
|
HSCOPEITEM hParent
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::EnumerateScopePane")));
|
|
HRESULT hr = S_OK;
|
|
CScopeItem *psi = m_pRootScopeItem; // Default to enumerating from root.
|
|
SCOPEDATAITEM item;
|
|
|
|
if (NULL == m_hRoot)
|
|
m_hRoot = hParent; // Remember the root node's handle.
|
|
|
|
if (m_hRoot != hParent)
|
|
{
|
|
item.mask = SDI_PARAM;
|
|
item.ID = hParent;
|
|
|
|
hr = m_pScope->GetItem (&item);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("Failed getting item")));
|
|
return hr;
|
|
}
|
|
|
|
psi = reinterpret_cast<CScopeItem *>(item.lParam);
|
|
}
|
|
|
|
item.mask = SDI_STR | SDI_STATE | SDI_IMAGE | SDI_OPENIMAGE | SDI_PARAM | SDI_CHILDREN;
|
|
item.nState = 0;
|
|
item.relativeID = hParent;
|
|
|
|
int cChildren = psi->NumChildren();
|
|
for (int iChild = 0; iChild < cChildren; iChild++)
|
|
{
|
|
CScopeItem *pChild = psi->Child(iChild);
|
|
|
|
item.displayname = MMC_CALLBACK;
|
|
item.nImage = pChild->ImageIndex();
|
|
item.nOpenImage = pChild->OpenImageIndex();
|
|
item.cChildren = pChild->NumChildren();
|
|
item.lParam = reinterpret_cast<LPARAM>(pChild);
|
|
|
|
m_pScope->InsertItem (&item);
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::InitNew(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::InitNew")));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::GetClassID(
|
|
CLSID *pClassID
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::GetClassID")));
|
|
if (!pClassID)
|
|
return E_FAIL;
|
|
|
|
*pClassID = ClassId();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT
|
|
CSnapInCompData::IsDirty(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::IsDirty")));
|
|
return S_FALSE;
|
|
}
|
|
|
|
HRESULT
|
|
CSnapInCompData::Load(
|
|
IStream *pStm
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::Load")));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::Save(
|
|
IStream *pStm,
|
|
BOOL fClearDirty
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::Save")));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::GetSizeMax(
|
|
ULARGE_INTEGER *pcbSize
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::GetSizeMax")));
|
|
DWORD dwSize = 0;
|
|
|
|
if (NULL == pcbSize)
|
|
return E_FAIL;
|
|
|
|
pcbSize->QuadPart = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CSnapInCompData::AddMenuItems(
|
|
LPDATAOBJECT pDataObject,
|
|
LPCONTEXTMENUCALLBACK piCallback,
|
|
long *pInsertionAllowed
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::AddMenuItems")));
|
|
HRESULT hr = S_OK;
|
|
|
|
if (CCM_INSERTIONALLOWED_TOP & *pInsertionAllowed)
|
|
{
|
|
CString strOpen(m_hInstance, IDS_VERB_OPEN);
|
|
CONTEXTMENUITEM ci;
|
|
ci.strName = strOpen;
|
|
ci.strStatusBarText = NULL;
|
|
ci.lCommandID = 0;
|
|
ci.lInsertionPointID = CCM_INSERTIONPOINTID_PRIMARY_TOP;
|
|
ci.fFlags = 0;
|
|
ci.fSpecialFlags = CCM_SPECIAL_DEFAULT_ITEM;
|
|
|
|
hr = piCallback->AddItem(&ci);
|
|
if (FAILED(hr))
|
|
{
|
|
DBGERROR((TEXT("Error 0x%08X adding context menu item"), hr));
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CSnapInCompData::Command(
|
|
long lCommandID,
|
|
LPDATAOBJECT pDataObject
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::Command")));
|
|
DBGPRINT((DM_SNAPIN, DL_LOW, TEXT("Command ID = %d"), lCommandID));
|
|
|
|
if (0 == lCommandID)
|
|
OpenVolumeQuotaProperties();
|
|
else
|
|
DBGERROR((TEXT("Unrecognized command ID (%d)"), lCommandID));
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
const int MAX_SNAPIN_PROP_PAGES = 1;
|
|
|
|
BOOL CALLBACK
|
|
CSnapInCompData::AddPropSheetPage(
|
|
HPROPSHEETPAGE hpage,
|
|
LPARAM lParam
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::AddPropSheetPage")));
|
|
PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
|
|
|
|
if (ppsh->nPages < MAX_SNAPIN_PROP_PAGES)
|
|
{
|
|
ppsh->phpage[ppsh->nPages++] = hpage;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CSnapInCompData::PropPageThreadProc(
|
|
LPVOID pvParam
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::PropPageThreadProc")));
|
|
MSG msg;
|
|
HWND hwndPropSheet = NULL;
|
|
CSnapInCompData *pThis = (CSnapInCompData *)pvParam;
|
|
|
|
com_autoptr<IShellExtInit> psei;
|
|
HRESULT hr = CoInitialize(NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = CoCreateInstance(CLSID_DiskQuotaUI,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IShellExtInit,
|
|
reinterpret_cast<void **>(psei.getaddr()));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
com_autoptr<IShellPropSheetExt> pspse;
|
|
hr = psei->QueryInterface(IID_ISnapInPropSheetExt,
|
|
reinterpret_cast<void **>(pspse.getaddr()));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
com_autoptr<IDiskQuotaPolicy> pdqp;
|
|
hr = pspse->QueryInterface(IID_IDiskQuotaPolicy,
|
|
reinterpret_cast<void **>(pdqp.getaddr()));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pdqp->Initialize(pThis->m_pGPEInformation, NULL);
|
|
|
|
HPROPSHEETPAGE rghPages[1];
|
|
|
|
PROPSHEETHEADER psh;
|
|
ZeroMemory(&psh, sizeof(psh));
|
|
//
|
|
// Define sheet.
|
|
//
|
|
psh.dwSize = sizeof(PROPSHEETHEADER);
|
|
psh.dwFlags = PSH_MODELESS;
|
|
psh.hInstance = pThis->m_hInstance;
|
|
psh.pszIcon = NULL;
|
|
psh.pszCaption = NULL;
|
|
psh.nPages = 0;
|
|
psh.nStartPage = 0;
|
|
psh.phpage = rghPages;
|
|
|
|
pThis->m_pConsole->GetMainWindow(&psh.hwndParent);
|
|
|
|
hr = pspse->AddPages(AddPropSheetPage, (LPARAM)&psh);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hwndPropSheet = (HWND)PropertySheet(&psh);
|
|
if (NULL != hwndPropSheet)
|
|
{
|
|
//
|
|
// Set the title on the property sheet.
|
|
//
|
|
CString strTitle(pThis->m_hInstance, IDS_SNAPIN_POLICYDLG_TITLE);
|
|
SetWindowText(hwndPropSheet, strTitle);
|
|
|
|
MSG msg;
|
|
while (0 != ::GetMessage(&msg, NULL, 0, 0))
|
|
{
|
|
if (!PropSheet_IsDialogMessage(hwndPropSheet, &msg))
|
|
{
|
|
::TranslateMessage(&msg);
|
|
::DispatchMessage(&msg);
|
|
}
|
|
if (NULL == PropSheet_GetCurrentPageHwnd(hwndPropSheet))
|
|
{
|
|
DestroyWindow(hwndPropSheet);
|
|
PostQuitMessage(0);
|
|
}
|
|
}
|
|
}
|
|
else if (-1 == (int)hwndPropSheet)
|
|
{
|
|
DBGERROR((TEXT("PropertySheet failed with error %d"), GetLastError()));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGERROR((TEXT("AddPages failed")));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
DBGERROR((TEXT("CoCreateInstance failed with result 0x%08X"), hr));
|
|
|
|
CoUninitialize();
|
|
}
|
|
else
|
|
DBGERROR((TEXT("CoInitialize failed with result 0x%08X"), hr));
|
|
return 0;
|
|
}
|
|
|
|
|
|
//
|
|
// Create a new thread and run the volume properties dialog (modified for
|
|
// policy snapin).
|
|
//
|
|
HRESULT
|
|
CSnapInCompData::OpenVolumeQuotaProperties(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CSnapInCompData::OpenVolumeQuotaProperties")));
|
|
HRESULT hr = E_FAIL;
|
|
UINT idThread;
|
|
HANDLE hThread = CreateThread(NULL,
|
|
0, // Default stack size
|
|
(LPTHREAD_START_ROUTINE)PropPageThreadProc,
|
|
(LPVOID)this,
|
|
0,
|
|
(LPDWORD)&idThread);
|
|
if (INVALID_HANDLE_VALUE != hThread)
|
|
{
|
|
hr = NOERROR;
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Snap-in data object implementation. CDataObject
|
|
//-----------------------------------------------------------------------------
|
|
CDataObject::CDataObject(
|
|
CSnapInCompData& cd
|
|
) : m_cRef(0),
|
|
m_cd(cd),
|
|
m_type(CCT_UNINITIALIZED),
|
|
m_pItem(NULL)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CDataObject::CDataObject")));
|
|
|
|
}
|
|
|
|
|
|
CDataObject::~CDataObject(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CDataObject::~CDataObject")));
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CDataObject::QueryInterface(
|
|
REFIID riid,
|
|
LPVOID *ppvOut
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_MID, TEXT("CDataObject::QueryInterface")));
|
|
DBGPRINTIID(DM_SNAPIN, DL_MID, riid);
|
|
|
|
HRESULT hr = E_NOINTERFACE;
|
|
|
|
if (NULL == ppvOut)
|
|
return E_INVALIDARG;
|
|
|
|
*ppvOut = NULL;
|
|
|
|
if (IID_IDiskQuotaSnapInData == riid)
|
|
{
|
|
*ppvOut = reinterpret_cast<IQuotaDataObject *>(this);
|
|
}
|
|
else if(IID_IUnknown == riid || IID_IDataObject == riid)
|
|
{
|
|
*ppvOut = this;
|
|
}
|
|
|
|
if (NULL != *ppvOut)
|
|
{
|
|
((LPUNKNOWN)*ppvOut)->AddRef();
|
|
hr = NOERROR;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
ULONG
|
|
CDataObject::AddRef(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_LOW, TEXT("CDataObject::AddRef")));
|
|
|
|
ULONG ulReturn = m_cRef + 1;
|
|
InterlockedIncrement(&m_cRef);
|
|
return ulReturn;
|
|
}
|
|
|
|
|
|
ULONG
|
|
CDataObject::Release(
|
|
void
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_LOW, TEXT("CDataObject::Release")));
|
|
|
|
ULONG ulReturn = m_cRef - 1;
|
|
if (InterlockedDecrement(&m_cRef) == 0)
|
|
{
|
|
delete this;
|
|
ulReturn = 0;
|
|
}
|
|
return ulReturn;
|
|
}
|
|
|
|
|
|
|
|
HRESULT
|
|
CDataObject::GetDataHere(
|
|
LPFORMATETC lpFormatetc,
|
|
LPSTGMEDIUM lpMedium
|
|
)
|
|
{
|
|
DBGTRACE((DM_SNAPIN, DL_HIGH, TEXT("CDataObject::GetDataHere")));
|
|
HRESULT hr = S_OK;
|
|
try
|
|
{
|
|
//
|
|
// Forward the rendering request to the data object's item object.
|
|
//
|
|
hr = m_pItem->RenderData(lpFormatetc->cfFormat, lpMedium);
|
|
}
|
|
catch(CAllocException& e)
|
|
{
|
|
DBGERROR((TEXT("Insufficient memory")));
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
#endif // POLICY_MMC_SNAPIN
|