windows-nt/Source/XPSP1/NT/net/ias/iasjet/dstore/dsobject.cpp
2020-09-26 16:20:57 +08:00

538 lines
12 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 1998 - 2000 , Microsoft Corp. All rights reserved.
//
// FILE
//
// dsobject.cpp
//
// SYNOPSIS
//
// This file defines the class DBObject.
//
// MODIFICATION HISTORY
//
// 02/20/1998 Original version.
// 10/02/1998 Allow rename through PutValue.
// 04/13/2000 Port to ATL 3.0
//
///////////////////////////////////////////////////////////////////////////////
#include <ias.h>
#include <iasutil.h>
#include <dsenum.h>
#include <dsobject.h>
#include <localtxn.h>
#include <oledbstore.h>
#include <guard.h>
#include <varvec.h>
#include <memory>
//////////
// ATL implementation of IEnumVARIANT
//////////
typedef CComEnum< IEnumVARIANT,
&__uuidof(IEnumVARIANT),
VARIANT,
_Copy<VARIANT>,
CComMultiThreadModelNoCS
> EnumVARIANT;
//////////
// Test if a property is the special 'name' property.
//////////
inline bool isNameProperty(PCWSTR p) throw ()
{
return (*p == L'N' || *p == L'n') ? !_wcsicmp(p, L"NAME") : false;
}
//////////
// Macro to acquire a scoped lock on the global data store.
//////////
#define LOCK_STORE() \
Guard< CComObjectRootEx< CComMultiThreadModel > >__GUARD__(*store)
//////////
// Macros to begin and commit transactions on the global session.
//////////
#define BEGIN_WRITE_TXN() \
LOCK_STORE(); \
LocalTransaction __TXN__(store->session)
#define COMMIT_WRITE_TXN() \
__TXN__.commit()
DBObject* DBObject::createInstance(
OleDBDataStore* owner,
IDataStoreContainer* container,
ULONG uniqueID,
PCWSTR relativeName
)
{
// Create a new CComObject.
CComObject<DBObject>* newObj;
_com_util::CheckError(CComObject<DBObject>::CreateInstance(&newObj));
// Cast to a DBObject and store it in an auto_ptr in case initialize throws
// an exception.
std::auto_ptr<DBObject> obj(newObj);
// Initialize the object.
obj->initialize(owner, container, uniqueID, relativeName);
// Release and return.
return obj.release();
}
IDataStoreObject* DBObject::spawn(ULONG childID, BSTR childName)
{
DBObject* child = DBObject::createInstance(store, this, childID, childName);
child->InternalAddRef();
return child;
}
STDMETHODIMP DBObject::get_Name(BSTR* pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
return (*pVal = SysAllocString(name)) ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP DBObject::get_Class(BSTR* pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
return (*pVal = SysAllocString(L"OLE-DB Object")) ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP DBObject::get_GUID(BSTR* pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
WCHAR sz[SZLONG_LENGTH];
_ultow(identity, sz, 10);
return (*pVal = SysAllocString(sz)) ? S_OK : E_OUTOFMEMORY;
}
STDMETHODIMP DBObject::get_Container(IDataStoreContainer** pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
if (*pVal = parent) { (*pVal)->AddRef(); }
return S_OK;
}
STDMETHODIMP DBObject::GetValue(BSTR bstrName, VARIANT* pVal)
{
if (bstrName == NULL || pVal == NULL) { return E_INVALIDARG; }
VariantInit(pVal);
if (isNameProperty(bstrName))
{
V_BSTR(pVal) = SysAllocString(name);
return (V_BSTR(pVal)) ? (V_VT(pVal) = VT_BSTR), S_OK : E_OUTOFMEMORY;
}
HRESULT hr;
try
{
hr = properties.getValue(bstrName, pVal) ? S_OK : DISP_E_MEMBERNOTFOUND;
}
CATCH_AND_RETURN()
return hr;
}
STDMETHODIMP DBObject::GetValueEx(BSTR bstrName, VARIANT* pVal)
{
RETURN_ERROR(GetValue(bstrName, pVal));
// Is it an array ?
if (V_VT(pVal) != (VT_VARIANT | VT_ARRAY))
{
// No, so we have to convert it to one.
try
{
// Save the single value.
_variant_t single(*pVal, false);
// Create a SAFEARRAY with a single element.
CVariantVector<VARIANT> multi(pVal, 1);
// Load the single value in.
multi[0] = single.Detach();
}
CATCH_AND_RETURN()
}
return S_OK;
}
STDMETHODIMP DBObject::PutValue(BSTR bstrName, VARIANT* pVal)
{
if (bstrName == NULL || pVal == NULL) { return E_INVALIDARG; }
try
{
if (isNameProperty(bstrName))
{
// 'name' property must be a BSTR.
if (V_VT(pVal) != VT_BSTR) { return DISP_E_TYPEMISMATCH; }
// 'name' property must be non-null.
if (V_BSTR(pVal) == NULL) { return E_INVALIDARG; }
// Did it actually change?
if (wcscmp(name, V_BSTR(pVal)) != 0)
{
// Yes, so save the new value ...
name = V_BSTR(pVal);
// ... and set the dirty flag.
nameDirty = true;
}
}
else if (V_VT(pVal) != VT_EMPTY)
{
properties.updateValue(bstrName, pVal);
}
else
{
// If the variant is empty, just erase the property.
properties.erase(bstrName);
}
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::Update()
{
try
{
BEGIN_WRITE_TXN();
// maybe someone created that same object before the update is called
// (concurent MMC scenario)
DBObject* owner = narrow(parent);
if (identity == 0)
{
identity = store->find.execute(owner->identity, name);
}
if (identity == 0)
{
// If we're newly created, then we have to update our record in
// the Objects table.
// An object always has an owner.
_ASSERT(owner != NULL);
store->create.execute(owner->identity, name);
identity = store->find.execute(owner->identity, name);
// This should never happen since the create succeeded.
_ASSERT(identity != 0);
}
else if (nameDirty)
{
store->update.execute(identity, name, narrow(parent)->identity);
}
// Reset the dirty flag.
nameDirty = false;
store->erase.execute(identity);
store->set.execute(identity, properties);
COMMIT_WRITE_TXN();
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::Restore()
{
try
{
properties.clear();
LOCK_STORE();
store->get.execute(identity, properties);
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::Item(BSTR bstrName, IDataStoreProperty** pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
*pVal = NULL;
_variant_t v;
RETURN_ERROR(GetValue(bstrName, &v));
try
{
// Create a new property object.
(*pVal = DSProperty::createInstance(bstrName, v, this))->AddRef();
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::get_PropertyCount(long* pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
// Add one for the special 'name' property.
*pVal = properties.size() + 1;
return S_OK;
}
STDMETHODIMP DBObject::get_NewPropertyEnum(IUnknown** pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
*pVal = NULL;
try
{
// Create a temporary array of items.
std::vector<_variant_t> items(properties.size() + 1);
//////////
// Load the special 'name' property.
//////////
std::vector<_variant_t>::iterator i = items.begin();
*i = DSProperty::createInstance(L"name", name, this);
++i;
//////////
// Load the regular properties into the temporary array.
//////////
PropertyBag::const_iterator j = properties.begin();
for ( ; j != properties.end(); ++i, ++j)
{
_variant_t value;
j->second.get(&value);
*i = DSProperty::createInstance(j->first, value, this);
}
//////////
// Create and initialize an enumerator for the items.
//////////
CComPtr<EnumVARIANT> newEnum(new CComObject<EnumVARIANT>);
_com_util::CheckError(newEnum->Init(items.begin(),
items.end(),
NULL,
AtlFlagCopy));
// Return it to the caller.
(*pVal = newEnum)->AddRef();
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::Item(BSTR bstrName, IDataStoreObject** ppObject)
{
if (bstrName == NULL || ppObject == NULL) { return E_INVALIDARG; }
*ppObject = NULL;
try
{
LOCK_STORE();
ULONG childID = store->find.execute(identity, bstrName);
if (childID == 0) { return HRESULT_FROM_WIN32(ERROR_NOT_FOUND); }
*ppObject = spawn(childID, bstrName);
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::Create(BSTR /* bstrClass */,
BSTR bstrName,
IDataStoreObject** ppObject)
{
if (bstrName == NULL || ppObject == NULL) { return E_INVALIDARG; }
*ppObject = NULL;
try
{
*ppObject = spawn(0, bstrName);
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::MoveHere(IDataStoreObject* pObject,
BSTR bstrNewName)
{
if (pObject == NULL) { return E_INVALIDARG; }
try
{
// Convert the subject to a DBObject.
DBObject* object = narrow(pObject);
// Can't do this unless the object has been persisted.
if (object->identity == 0) { return E_FAIL; }
// Compute the (possibly changed) RDN of the object.
PCWSTR rdn = bstrNewName ? bstrNewName : object->name;
// Write the new parent ID and possibly name to the database.
BEGIN_WRITE_TXN();
store->update.execute(object->identity, rdn, identity);
COMMIT_WRITE_TXN();
// It succeeded, so save the new name if necessary ...
if (bstrNewName) { object->name = bstrNewName; }
// ... and switch the parent pointer.
object->parent.Release();
object->parent = this;
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::Remove(BSTR /* bstrClass */, BSTR bstrName)
{
if (bstrName == NULL) { return E_INVALIDARG; }
try
{
BEGIN_WRITE_TXN();
store->destroy.execute(identity, bstrName);
COMMIT_WRITE_TXN();
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::get_ChildCount(long *pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
try
{
Rowset rowset;
LOCK_STORE();
store->members.execute(identity, &rowset);
long count = 0;
while (rowset.moveNext()) { ++count; }
// If this is the root, we have to subtract one since the root is a
// child of itself.
if (identity == 1) { --count; }
*pVal = count;
}
CATCH_AND_RETURN()
return S_OK;
}
STDMETHODIMP DBObject::get_NewChildEnum(IUnknown** pVal)
{
if (pVal == NULL) { return E_INVALIDARG; }
try
{
Rowset rowset;
LOCK_STORE();
store->members.execute(identity, &rowset);
(*pVal = new DBEnumerator(this, rowset))->AddRef();
}
CATCH_AND_RETURN()
return S_OK;
}
void DBObject::initialize(
OleDBDataStore* owner,
IDataStoreContainer* container,
ULONG uniqueID,
PCWSTR relativeName
)
{
// Set the class members.
store = owner;
parent = container;
identity = uniqueID;
name = relativeName;
nameDirty = false;
// If this object exists in the persistent store, then get its properties.
if (identity != 0)
{
LOCK_STORE();
store->get.execute(identity, properties);
}
}
DBObject* DBObject::narrow(IUnknown* p)
{
DBObject* object;
using _com_util::CheckError;
CheckError(p->QueryInterface(__uuidof(DBObject), (PVOID*)&object));
// We can get away with InternalRelease since the caller must still
// have a reference to this object.
object->InternalRelease();
return object;
}