You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4954 lines
137 KiB
4954 lines
137 KiB
#include <windows.h>
|
|
#include <windowsx.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <io.h>
|
|
#include <hlink.h>
|
|
#include <dispex.h>
|
|
#include "mshtml.h"
|
|
#include "msxml.h"
|
|
#include <winnls.h>
|
|
#include "atlbase.h" // USES_CONVERSION
|
|
#include "dbg.h"
|
|
#include "..\inc\cstr.h"
|
|
#include "macros.h"
|
|
#include <comdef.h>
|
|
//You may derive a class from CComModule and use it if you want to override
|
|
//something, but do not change the name of _Module
|
|
extern CComModule _Module;
|
|
#include <atlcom.h>
|
|
#include <map>
|
|
#include <list>
|
|
#include <vector>
|
|
#include "mmcdebug.h"
|
|
#include "mmcerror.h"
|
|
#include "..\inc\xmlbase.h"
|
|
#include "countof.h"
|
|
#include <commctrl.h>
|
|
#include "picon.h"
|
|
#include "base64.h"
|
|
#include "strings.h"
|
|
#include "autoptr.h"
|
|
#include <shlobj.h>
|
|
#include "zlib.h"
|
|
#include "xmlicon.h"
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// The safer string handling routines
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
#include <strsafe.h>
|
|
|
|
SC ScEncodeBinary(CComBSTR& bstrResult, const CXMLBinary& binSrc);
|
|
SC ScDecodeBinary(const CComBSTR& bstrSource, CXMLBinary *pBinResult);
|
|
SC ScSaveXMLDocumentToString(CXMLDocument& xmlDocument, std::wstring& strResult);
|
|
|
|
// Traces
|
|
#ifdef DBG
|
|
CTraceTag tagXMLCompression(TEXT("Console Files"), TEXT("Compression"));
|
|
#endif
|
|
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// helper classes used in this file
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* class CXMLBinaryValue
|
|
*
|
|
* PURPOSE: Persists the contents of XMLValue on binary storage
|
|
* It's a simle wrapper needed to inform CPersistor about
|
|
* values wish to be persisted on Binary storage
|
|
* [see comment "CONCEPT OF BINARY STORAGE" in "xmbase.h"]
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
class CXMLBinaryValue : public CXMLObject
|
|
{
|
|
CXMLValue m_xval;
|
|
public:
|
|
CXMLBinaryValue(CXMLValue xval) : m_xval(xval) {}
|
|
virtual LPCTSTR GetXMLType() { return m_xval.GetTypeName(); }
|
|
virtual void Persist(CPersistor &persistor)
|
|
{
|
|
persistor.PersistContents (m_xval);
|
|
}
|
|
virtual bool UsesBinaryStorage() { return true; }
|
|
};
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class CXMLElementCollection
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElementCollection::get_count
|
|
*
|
|
* PURPOSE: // returns count of elements in the collection
|
|
*
|
|
* PARAMETERS:
|
|
* long *plLength [out] - count of the elements
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElementCollection::get_count(long *plCount)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElementCollection::get_count"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
sc = m_sp->get_length(plCount);
|
|
if (sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElementCollection::item
|
|
*
|
|
* PURPOSE: wraps item method from IXMLDOMNodeList
|
|
*
|
|
* PARAMETERS:
|
|
* VARIANT Var1 [in] parameter #1
|
|
* VARIANT Var2 [in] parameter #2
|
|
* CXMLElement *pElem [out] resulting element
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElementCollection::item(LONG lIndex, CXMLElement *pElem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElementCollection::item"));
|
|
|
|
// check params
|
|
sc = ScCheckPointers(pElem);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// init ret val
|
|
*pElem = CXMLElement();
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMNode> spNode;
|
|
sc = m_sp->get_item(lIndex , &spNode);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
// return the object
|
|
*pElem = CXMLElement(spNode);
|
|
}
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class CXMLElement
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::get_tagName
|
|
*
|
|
* PURPOSE: returns tag name of the element
|
|
*
|
|
* PARAMETERS:
|
|
* CStr &strTagName [out] element's name
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::get_tagName(CStr &strTagName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::get_tagName"));
|
|
|
|
USES_CONVERSION;
|
|
|
|
// get the element
|
|
CComQIPtr<IXMLDOMElement> spEl;
|
|
spEl = m_sp;
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(spEl, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComBSTR bstr;
|
|
sc = spEl->get_tagName(&bstr);
|
|
if(sc)
|
|
sc.Throw();
|
|
strTagName=OLE2T(bstr);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::get_parent
|
|
*
|
|
* PURPOSE: returns parent element
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElement * pParent - [out] parent element
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::get_parent(CXMLElement * pParent)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::get_parent"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(pParent);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// init return value
|
|
*pParent = CXMLElement();
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMNode> spParent;
|
|
sc = m_sp->get_parentNode(&spParent);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
*pParent = CXMLElement(spParent);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::setAttribute
|
|
*
|
|
* PURPOSE: assigns attribute to the element
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr &strPropertyName - attribute name
|
|
* const CComBSTR &bstrPropertyValue - attribute value
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::setAttribute(const CStr &strPropertyName, const CComBSTR &bstrPropertyValue)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::setAttribute"));
|
|
|
|
// get the element
|
|
CComQIPtr<IXMLDOMElement> spEl;
|
|
spEl = m_sp;
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(spEl, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComBSTR bstrPropertyName (strPropertyName);
|
|
CComVariant varPropertyValue(bstrPropertyValue);
|
|
sc = spEl->setAttribute(bstrPropertyName, varPropertyValue);
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::getAttribute
|
|
*
|
|
* PURPOSE: gets attribute from element
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr &strPropertyName - [in] attribute name
|
|
* CComBSTR &bstrPropertyValue - [out] attribute value
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
bool
|
|
CXMLElement::getAttribute(const CStr &strPropertyName, CComBSTR &bstrPropertyValue)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::getAttribute"));
|
|
|
|
// get the element
|
|
CComQIPtr<IXMLDOMElement> spEl;
|
|
spEl = m_sp;
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(spEl, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComBSTR bstrPropertyName (strPropertyName);
|
|
CComVariant varPropertyValue;
|
|
sc = spEl->getAttribute(bstrPropertyName, &varPropertyValue);
|
|
if(sc) // no resuls cannot be read either
|
|
sc.Throw();
|
|
|
|
if (sc.ToHr() == S_FALSE)
|
|
return false;
|
|
|
|
// check if we've got the expected value type
|
|
if ( varPropertyValue.vt != VT_BSTR )
|
|
sc.Throw( E_UNEXPECTED );
|
|
|
|
bstrPropertyValue = varPropertyValue.bstrVal;
|
|
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::removeAttribute
|
|
*
|
|
* PURPOSE: removes attribute from the elament
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr &strPropertyName - [in] atrtibute name
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::removeAttribute(const CStr &strPropertyName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::removeAttribute"));
|
|
|
|
// get the element
|
|
CComQIPtr<IXMLDOMElement> spEl;
|
|
spEl = m_sp;
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(spEl, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComBSTR bstrPropertyName (strPropertyName);
|
|
sc = spEl->removeAttribute(bstrPropertyName);
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::get_children
|
|
*
|
|
* PURPOSE: returns collection of children which belong to element
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElementCollection *pChildren - [out] collection
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::get_children(CXMLElementCollection *pChildren)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::get_children"));
|
|
|
|
sc = ScCheckPointers(pChildren);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// init ret value
|
|
*pChildren = CXMLElementCollection();
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMNodeList> spChildren;
|
|
sc = m_sp->get_childNodes(&spChildren);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
// return the object
|
|
*pChildren = CXMLElementCollection(spChildren);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::get_type
|
|
*
|
|
* PURPOSE: returns the type of the element
|
|
*
|
|
* PARAMETERS:
|
|
* long *plType - [out] element's type
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::get_type(DOMNodeType *pType)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::get_type"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
sc = m_sp->get_nodeType(pType);
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::get_text
|
|
*
|
|
* PURPOSE: retrieves contents of the text element
|
|
* NOTE: it only works for text elements!
|
|
*
|
|
* PARAMETERS:
|
|
* CComBSTR &bstrContent - storage for resulting string
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::get_text(CComBSTR &bstrContent)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::get_text"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
bstrContent.Empty();
|
|
sc = m_sp->get_text(&bstrContent);
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::addChild
|
|
*
|
|
* PURPOSE: adds the new child element to current element
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElement& rChildElem [in] element to become a child
|
|
* long lIndex [in] index for new element
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::addChild(CXMLElement& rChildElem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::addChild"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMNode> spCreated;
|
|
sc = m_sp->appendChild(rChildElem.m_sp, &spCreated);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
rChildElem.m_sp = spCreated;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::removeChild
|
|
*
|
|
* PURPOSE: removes child element
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElement& rChildElem - [in] child to remove
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLElement::removeChild(CXMLElement& rChildElem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::removeChild"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMNode> spRemoved;
|
|
sc = m_sp->removeChild(rChildElem.m_sp, &spRemoved);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
rChildElem.m_sp = spRemoved;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::GetTextIndent
|
|
*
|
|
* PURPOSE: returns indentation for the child element \ closing tag
|
|
* Indentation is calulated by element depth in the tree
|
|
*
|
|
* PARAMETERS:
|
|
* CComBSTR& bstrIndent [out] string conatining required indent
|
|
* bool bForAChild [in] if the indent is for a child
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
bool CXMLElement::GetTextIndent(CComBSTR& bstrIndent, bool bForAChild)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::GetTextIndent"));
|
|
|
|
const size_t nIdentStep = 2;
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// initialize the result
|
|
bstrIndent.Empty();
|
|
|
|
|
|
CComPtr<IXMLDOMNode> spNext;
|
|
CComPtr<IXMLDOMNode> spParent;
|
|
|
|
// calculate node depth
|
|
int nNodeDepth = 0;
|
|
spNext = m_sp;
|
|
while ( S_OK == spNext->get_parentNode(&spParent) && spParent != NULL)
|
|
{
|
|
++nNodeDepth;
|
|
spNext = spParent;
|
|
spParent.Release();
|
|
}
|
|
|
|
// no indent for topmost things
|
|
if (nNodeDepth < 1)
|
|
return false;
|
|
|
|
// do not count root node - not ours
|
|
--nNodeDepth;
|
|
|
|
// child is indented more
|
|
if (bForAChild)
|
|
++nNodeDepth;
|
|
|
|
if (bForAChild)
|
|
{
|
|
// it may already have indent for the closing tag (if its' not the first element)
|
|
// than we just need a little increase
|
|
|
|
// see if the we have child elements added;
|
|
CXMLElementCollection colChildren;
|
|
get_children(&colChildren);
|
|
|
|
// count all elements
|
|
long nChildren = 0;
|
|
if (!colChildren.IsNull())
|
|
colChildren.get_count(&nChildren);
|
|
|
|
// we will have at least 2 for normal elements
|
|
// since the indent (text element) will be added prior to the first one
|
|
if (nChildren > 1)
|
|
{
|
|
bstrIndent = std::wstring( nIdentStep, ' ' ).c_str();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
std::wstring strResult(nIdentStep * (nNodeDepth) + 1/*for new line*/, ' ');
|
|
// new line for each (1st) new item
|
|
strResult[0] = '\n';
|
|
bstrIndent = strResult.c_str();
|
|
|
|
return true;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::replaceChild
|
|
*
|
|
* PURPOSE: replaces the element with the new on
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElement& rNewChildElem [in] new element
|
|
* CXMLElement& rOldChildElem [in/out] old element
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CXMLElement::replaceChild(CXMLElement& rNewChildElem, CXMLElement& rOldChildElem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::replaceChild"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// forward to MSXML
|
|
CComPtr<IXMLDOMNode> spRemoved;
|
|
sc = m_sp->replaceChild(rNewChildElem.m_sp, rOldChildElem.m_sp, &spRemoved);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
rOldChildElem = CXMLElement(spRemoved);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::getNextSibling
|
|
*
|
|
* PURPOSE: returns sibling to this element
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElement * pNext [out] sibling element
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
void CXMLElement::getNextSibling(CXMLElement * pNext)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::getNextSibling"));
|
|
|
|
// parameter check;
|
|
sc = ScCheckPointers(pNext);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// initialization
|
|
*pNext = CXMLElement();
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// forward to MSXML
|
|
CComPtr<IXMLDOMNode> spNext;
|
|
sc = m_sp->get_nextSibling(&spNext);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
*pNext = CXMLElement(spNext);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLElement::getChildrenByName
|
|
*
|
|
* PURPOSE: returns children by specified name
|
|
*
|
|
* PARAMETERS:
|
|
* LPTCSTR szTagName - [in] tag name
|
|
* CXMLElementCollection *pChildren - [out] collection
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CXMLElement::getChildrenByName(LPCTSTR szTagName, CXMLElementCollection *pChildren)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::getChildrenByName"));
|
|
|
|
sc = ScCheckPointers(pChildren);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// init ret value
|
|
*pChildren = CXMLElementCollection();
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMNodeList> spChildren;
|
|
sc = m_sp->selectNodes(CComBSTR(szTagName), &spChildren);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
// return the object
|
|
*pChildren = CXMLElementCollection(spChildren);
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CXMLElement::put_text
|
|
*
|
|
* PURPOSE: Per IXMLDOMNode
|
|
*
|
|
* PARAMETERS:
|
|
* BSTR bstrValue :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void
|
|
CXMLElement::put_text(BSTR bstrValue)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLElement::put_text"));
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
sc = m_sp->put_text(bstrValue);
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class CXMLDocument
|
|
//
|
|
// These are documented in the Platform SDK.
|
|
//############################################################################
|
|
//############################################################################
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::get_root
|
|
*
|
|
* PURPOSE: returns root element of the document
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLElement *pElem
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLDocument::get_root(CXMLElement *pElem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::get_root"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(pElem);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// init ret value
|
|
*pElem = CXMLElement();
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComPtr<IXMLDOMElement> spElem;
|
|
sc = m_sp->get_documentElement(&spElem);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
*pElem = CXMLElement(spElem);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::createElement
|
|
*
|
|
* PURPOSE: creates new element in XML document
|
|
*
|
|
* PARAMETERS:
|
|
* NODE_TYPE type - type of the element requested
|
|
* CIXMLElement *pElem - resulting element
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLDocument::createElement(DOMNodeType type, BSTR bstrTag, CXMLElement *pElem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::createElement"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(pElem);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// init ret val
|
|
*pElem = CXMLElement();
|
|
|
|
// check if we have the interface pointer to forward the call
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
if (type == NODE_ELEMENT)
|
|
{
|
|
CComPtr<IXMLDOMElement> spElem;
|
|
sc = m_sp->createElement(bstrTag, &spElem);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
*pElem = CXMLElement(spElem);
|
|
}
|
|
else if (type == NODE_TEXT)
|
|
{
|
|
CComPtr<IXMLDOMText> spText;
|
|
sc = m_sp->createTextNode(bstrTag, &spText);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
*pElem = CXMLElement(spText);
|
|
}
|
|
else
|
|
{
|
|
sc.Throw(E_UNEXPECTED);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::CreateBinaryStorage
|
|
*
|
|
* PURPOSE: Creates XML element to be used for subsequent persist operations
|
|
* the object informs Persistor if it wants to be saved as binary data.
|
|
* If so, only reference will be saved in original place of the object
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr &strElementType - type of the element
|
|
* LPCTSTR szElementName - name of the element
|
|
*
|
|
* RETURNS:
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLDocument::CreateBinaryStorage()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::CreateBinaryStorage"));
|
|
|
|
// check if it is attachment is not a doubled
|
|
if (!m_XMLElemBinaryStorage.IsNull())
|
|
sc.Throw(E_UNEXPECTED);
|
|
|
|
CXMLElement elemRoot;
|
|
get_root(&elemRoot);
|
|
|
|
// create persistor on parent element
|
|
CPersistor persistorParent(*this, elemRoot);
|
|
persistorParent.SetLoading(false);
|
|
CPersistor persistorStor(persistorParent, XML_TAG_BINARY_STORAGE, NULL);
|
|
|
|
m_XMLElemBinaryStorage = persistorStor.GetCurrentElement();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::LocateBinaryStorage
|
|
*
|
|
* PURPOSE: Locates XML element to be used for subsequent persist operations
|
|
* the object informs Persistor if it wants to be saved as binary data.
|
|
* If so, only reference will be saved in original place of the object
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr &strElementType - type of the element
|
|
* LPCTSTR szElementName - name of the element
|
|
*
|
|
* RETURNS:
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLDocument::LocateBinaryStorage()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::LocateBinaryStorage"));
|
|
|
|
// check if it is attachment is not a doubled
|
|
if (!m_XMLElemBinaryStorage.IsNull())
|
|
sc.Throw(E_UNEXPECTED);
|
|
|
|
CXMLElement elemRoot;
|
|
get_root(&elemRoot);
|
|
|
|
// create persistor on parent element
|
|
CPersistor persistorParent(*this, elemRoot);
|
|
persistorParent.SetLoading(true);
|
|
CPersistor persistorStor(persistorParent, XML_TAG_BINARY_STORAGE, NULL);
|
|
// find the element
|
|
m_XMLElemBinaryStorage = persistorStor.GetCurrentElement();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::CommitBinaryStorage
|
|
*
|
|
* PURPOSE: makes binary storage the last element in the collection
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CXMLDocument::CommitBinaryStorage()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::CommitBinaryStorage"));
|
|
|
|
if (m_XMLElemBinaryStorage.IsNull())
|
|
sc.Throw(E_UNEXPECTED);
|
|
|
|
CXMLElement elemRoot;
|
|
get_root(&elemRoot);
|
|
|
|
// get the next siblings
|
|
CXMLElement elNext;
|
|
m_XMLElemBinaryStorage.getNextSibling(&elNext);
|
|
|
|
// drag itself and the next element (indent text) to the end
|
|
elemRoot.removeChild(m_XMLElemBinaryStorage); // remove element
|
|
|
|
// the element was padded to have proper indentation - need to remove it
|
|
DOMNodeType elType = NODE_INVALID;
|
|
while (!elNext.IsNull() && (elNext.get_type(&elType), elType == NODE_TEXT))
|
|
{
|
|
CXMLElement elNext2;
|
|
elNext.getNextSibling(&elNext2);
|
|
|
|
elemRoot.removeChild(elNext); // remove element (that was just an indent)
|
|
elNext = elNext2;
|
|
}
|
|
|
|
// create persistor on parent element
|
|
CPersistor persistorParent(*this, elemRoot);
|
|
persistorParent.SetLoading(false);
|
|
// create the new binary storage
|
|
CPersistor persistorStor(persistorParent, XML_TAG_BINARY_STORAGE, NULL);
|
|
|
|
// replace the current element with the one which hass all the binary storage
|
|
elemRoot.replaceChild(m_XMLElemBinaryStorage, persistorStor.GetCurrentElement());
|
|
|
|
m_XMLElemBinaryStorage = NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::ScCoCreate
|
|
*
|
|
* PURPOSE: (co)creates new xml document. puts charset and version
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpstrCharSet - charset (NULL - use default)
|
|
* CXMLDocument& doc - created document
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLDocument::ScCoCreate(bool bPutHeader)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::ScCoCreate"));
|
|
|
|
// cannot use this on co-created doc!
|
|
if (m_sp)
|
|
return sc = E_UNEXPECTED;
|
|
|
|
// Create an empty XML document
|
|
sc = ::CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
|
|
IID_IXMLDOMDocument, (void**)&m_sp);
|
|
if(sc)
|
|
return sc;
|
|
|
|
m_sp->put_preserveWhiteSpace(-1);
|
|
|
|
try
|
|
{
|
|
CXMLElement elemDoc = m_sp;
|
|
|
|
// put the document version
|
|
if (bPutHeader)
|
|
{
|
|
// valid document must have a top element - add the dummy one
|
|
WCHAR szVersion[] = L"<?xml version=\"1.0\"?>\n<DUMMY/>";
|
|
|
|
// load
|
|
sc = ScLoad(szVersion);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// we can now strip the dummy el.
|
|
CXMLElement elemRoot;
|
|
get_root(&elemRoot);
|
|
elemDoc.removeChild(elemRoot);
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::ScLoad
|
|
*
|
|
* PURPOSE: lods XML document from given IStream
|
|
*
|
|
* PARAMETERS:
|
|
* IStream *pStream [in] - stream to load from
|
|
* bool bSilentOnErrors [in] - do not trace if open fails
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLDocument::ScLoad(IStream *pStream, bool bSilentOnErrors /*= false*/ )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::ScLoad"));
|
|
|
|
// check params
|
|
sc = ScCheckPointers(pStream);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// get the interface
|
|
IPersistStreamInitPtr spPersistStream = m_sp;
|
|
sc = ScCheckPointers(spPersistStream, E_UNEXPECTED);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// load (do not trace the error - it may be that the old console
|
|
// is attempted to load - mmc will revert to old format after this failure)
|
|
SC sc_no_trace = spPersistStream->Load(pStream);
|
|
if ( sc_no_trace )
|
|
{
|
|
if ( !bSilentOnErrors )
|
|
sc = sc_no_trace;
|
|
return sc_no_trace;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::ScLoad
|
|
*
|
|
* PURPOSE: lods XML document from given string
|
|
*
|
|
* PARAMETERS:
|
|
* LPCWSTR strSource [in] - string to load from
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLDocument::ScLoad(LPCWSTR strSource)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::ScLoad"));
|
|
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
return sc;
|
|
|
|
CComBSTR bstrSource(strSource);
|
|
VARIANT_BOOL bOK;
|
|
|
|
sc = m_sp->loadXML(bstrSource, &bOK);
|
|
if (sc)
|
|
return sc;
|
|
|
|
if (bOK != VARIANT_TRUE)
|
|
return sc = E_FAIL;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::ScSaveToFile
|
|
*
|
|
* PURPOSE: saves xml document to given stream
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpcstrFileName - [in] file to save to
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLDocument::ScSaveToFile(LPCTSTR lpcstrFileName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::ScSaveToFile"));
|
|
|
|
// check params
|
|
sc = ScCheckPointers(lpcstrFileName);
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CComVariant var(lpcstrFileName);
|
|
sc = m_sp->save(var);
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLDocument::ScSave
|
|
*
|
|
* PURPOSE: saves xml document to given string
|
|
*
|
|
* PARAMETERS:
|
|
* CComBSTR &bstrResult - [out] string
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLDocument::ScSave(CComBSTR &bstrResult)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLDocument::ScSave"));
|
|
|
|
sc = ScCheckPointers(m_sp, E_NOINTERFACE);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
bstrResult.Empty();
|
|
sc = m_sp->get_xml(&bstrResult);
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLObject::ScSaveToString
|
|
*
|
|
* PURPOSE: saves XML object to string (in raw UNICODE or UTF-8 fromat)
|
|
*
|
|
* PARAMETERS:
|
|
* tstring *pString - resulting string
|
|
* bool bPutHeader - whether to put xml header info
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
|
|
SC CXMLObject::ScSaveToString(std::wstring *pString, bool bPutHeader /*= false*/)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLObject::ScSaveToString"));
|
|
|
|
// check parameter
|
|
sc = ScCheckPointers(pString);
|
|
if (sc)
|
|
return sc;
|
|
|
|
//initialize output
|
|
pString->erase();
|
|
|
|
// Create an empty XML document
|
|
CXMLDocument xmlDocument;
|
|
sc = xmlDocument.ScCoCreate(bPutHeader);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// persist the contents
|
|
try
|
|
{
|
|
CXMLElement elemDoc = xmlDocument;
|
|
|
|
CPersistor persistor(xmlDocument, elemDoc);
|
|
persistor.SetLoading(false);
|
|
persistor.EnableValueSplit(false); // disable split (no string table, no binary storage)
|
|
persistor.Persist(*this);
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
// dump it to the string
|
|
sc = ScSaveXMLDocumentToString(xmlDocument, *pString);
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLObject::ScSaveToDocument
|
|
*
|
|
* PURPOSE: saves XML object to file as XML document
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLDocument& xmlDocument - xmlDocument to save to
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLObject::ScSaveToDocument( CXMLDocument& xmlDocument )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLObject::ScSaveToDocument"));
|
|
|
|
// Create an empty XML document
|
|
sc = xmlDocument.ScCoCreate(true/*bPutHeader*/);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// persist the contents
|
|
try
|
|
{
|
|
CXMLElement elemDoc = xmlDocument;
|
|
|
|
CPersistor persistor(xmlDocument, elemDoc);
|
|
persistor.SetLoading(false);
|
|
persistor.Persist(*this);
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLObject::ScLoadFromString
|
|
*
|
|
* PURPOSE: loads XML object from data stored in string
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpcwstrSource
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLObject::ScLoadFromString(LPCWSTR lpcwstrSource, PersistorMode mode)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLObject::ScLoadFromString"));
|
|
|
|
// check parameter
|
|
sc = ScCheckPointers(lpcwstrSource);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// Create an empty XML document
|
|
CXMLDocument xmlDocument;
|
|
sc = xmlDocument.ScCoCreate(false/*bPutHeader*/);
|
|
if(sc)
|
|
return sc;
|
|
|
|
sc = xmlDocument.ScLoad(lpcwstrSource);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// persist the contents
|
|
try
|
|
{
|
|
CPersistor persistor(xmlDocument, CXMLElement(xmlDocument));
|
|
persistor.SetLoading(true);
|
|
persistor.SetMode(mode);
|
|
persistor.Persist(*this);
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLObject::ScLoadFromDocument
|
|
*
|
|
* PURPOSE: loads XML object from xml document saved as file
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLDocument& xmlDocument - xml document to read from
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLObject::ScLoadFromDocument( CXMLDocument& xmlDocument )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLObject::ScLoadFromDocument"));
|
|
|
|
// persist the contents
|
|
try
|
|
{
|
|
CPersistor persistor(xmlDocument, CXMLElement(xmlDocument));
|
|
persistor.SetLoading(true);
|
|
persistor.Persist(*this);
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
|
|
return sc;
|
|
}
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class CPersistor
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::CommonConstruct
|
|
*
|
|
* PURPOSE: common constructor, not to be used from outside.
|
|
* provided as common place for member initialization
|
|
* all the constructors should call it prior to doing anything specific.
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
*
|
|
\***************************************************************************/
|
|
void CPersistor::CommonConstruct()
|
|
{
|
|
// smart pointers are initialized by their constructors
|
|
ASSERT (m_XMLElemCurrent.IsNull());
|
|
ASSERT (m_XMLDocument.IsNull());
|
|
|
|
m_bIsLoading = false;
|
|
m_bLockedOnChild = false;
|
|
m_dwModeFlags = persistorModeDefault; // the default mode.
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::BecomeAChildOf
|
|
*
|
|
* PURPOSE: Initialization (second part of construction) of a child persistor
|
|
* All members, inherited from the parent persistor, are initialized here
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor &persistorParent - [in] (to be) parent persistor of current persistor
|
|
* CXMLElement elem - [in] element on which current persistor is based
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CPersistor::BecomeAChildOf(CPersistor &persistorParent, CXMLElement elem)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::BecomeAChildOf"));
|
|
|
|
// assign the element
|
|
m_XMLElemCurrent = elem;
|
|
|
|
// we do not inherit m_bLockedOnChild from parent!!!
|
|
m_bLockedOnChild = false;
|
|
|
|
// inherited members are copied here
|
|
m_XMLDocument = persistorParent.GetDocument();
|
|
m_bIsLoading = persistorParent.m_bIsLoading;
|
|
m_dwModeFlags = persistorParent.m_dwModeFlags;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::CPersistor
|
|
*
|
|
* PURPOSE: construct a persistor from a parent persistor.
|
|
* this creates a new XML element with the given name,
|
|
* and everything persisted to the new persistor
|
|
* is persisted under this element.
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor &persistorParent - parent persistor
|
|
* const CStr &strElementType - element type [element tag in XML file]
|
|
* LPCTSTR szElementName - "Name" attribute [optional]
|
|
*
|
|
\***************************************************************************/
|
|
CPersistor::CPersistor(CPersistor &persistorParent, const CStr &strElementType, LPCTSTR szElementName /*= NULL*/)
|
|
{
|
|
// initialize using common constructor
|
|
CommonConstruct();
|
|
|
|
CXMLElement elem;
|
|
if (persistorParent.IsStoring())
|
|
elem = persistorParent.AddElement(strElementType, szElementName);
|
|
else if (persistorParent.m_bLockedOnChild)
|
|
{
|
|
// if we already have the child located - just take it from parent!
|
|
// plus recheck to see it XML document actually has such an element
|
|
elem = persistorParent.CheckCurrentElement(strElementType, szElementName);
|
|
}
|
|
else
|
|
elem = persistorParent.GetElement(strElementType, szElementName);
|
|
|
|
// construct child persistor on elem
|
|
BecomeAChildOf(persistorParent, elem);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::CPersistor
|
|
*
|
|
* PURPOSE: creates new persistor for XML document
|
|
*
|
|
* PARAMETERS:
|
|
* IXMLDocument * pDocument - document
|
|
* CXMLElement &rElemCurrent - root element for persistor
|
|
*
|
|
\***************************************************************************/
|
|
CPersistor::CPersistor(CXMLDocument &document, CXMLElement& rElemCurrent)
|
|
{
|
|
// initialize using common constructor
|
|
CommonConstruct();
|
|
m_XMLDocument = document;
|
|
m_XMLElemCurrent = rElemCurrent;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::CPersistor
|
|
*
|
|
* PURPOSE: Creates new persistor based on parent an supplied element
|
|
*
|
|
* PARAMETERS:
|
|
* const CPersistor &other - parent persistor
|
|
* CXMLElement &rElemCurrent - root element for persistor
|
|
* bool bLockedOnChild - if new persistor should be a fake parent
|
|
* to be used to create persistors
|
|
*
|
|
\***************************************************************************/
|
|
CPersistor::CPersistor(CPersistor &other, CXMLElement& rElemCurrent, bool bLockedOnChild /*= false*/)
|
|
{
|
|
// initialize using common constructor
|
|
CommonConstruct();
|
|
|
|
// inherit...
|
|
BecomeAChildOf(other, rElemCurrent);
|
|
|
|
// this prevents locating the element on load (assuming the persistor is on element already)
|
|
// used to load items for collections
|
|
m_bLockedOnChild = bLockedOnChild;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::Persist
|
|
*
|
|
* PURPOSE: persists XML object
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpstrName - "Name" attribute for element [optional = NULL]
|
|
* CXMLObject & object - object to persist
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CPersistor::Persist(CXMLObject & object, LPCTSTR lpstrName /*= NULL*/)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::Persist"));
|
|
|
|
// persist w/o splitting if saved to string
|
|
if (!object.UsesBinaryStorage() || !FEnableValueSplit())
|
|
{
|
|
// ordinary object;
|
|
CPersistor persistorNew(*this,object.GetXMLType(),lpstrName);
|
|
object.Persist(persistorNew);
|
|
}
|
|
else
|
|
{
|
|
// this element should be split in 2 places
|
|
// see comment "CONCEPT OF BINARY STORAGE" in "xmbase.h"
|
|
|
|
CXMLElement elemBinStorage = GetDocument().GetBinaryStorage();
|
|
if (elemBinStorage.IsNull())
|
|
sc.Throw(E_UNEXPECTED);
|
|
|
|
// get elements enumeration in binaries
|
|
CXMLElementCollection colChildren;
|
|
elemBinStorage.getChildrenByName(XML_TAG_BINARY, &colChildren);
|
|
|
|
long nChildren = 0;
|
|
|
|
if (!colChildren.IsNull())
|
|
colChildren.get_count(&nChildren);
|
|
|
|
int iReffIndex = nChildren;
|
|
|
|
// save reference instead of contents
|
|
CPersistor persistorNew(*this, object.GetXMLType(), lpstrName);
|
|
persistorNew.PersistAttribute(XML_ATTR_BINARY_REF_INDEX, iReffIndex);
|
|
|
|
// persist the object
|
|
CPersistor persistorBinaries(*this, elemBinStorage);
|
|
// locate/create the element [cannot reuse constructor since we have collection here]
|
|
CXMLElement elem;
|
|
if (IsLoading())
|
|
{
|
|
// locate the element
|
|
elem = persistorBinaries.GetElement(XML_TAG_BINARY, object.GetBinaryEntryName(), iReffIndex );
|
|
}
|
|
else
|
|
{
|
|
// storing - just create sub-persistor
|
|
elem = persistorBinaries.AddElement(XML_TAG_BINARY, object.GetBinaryEntryName());
|
|
}
|
|
CPersistor persistorThisBinary(persistorBinaries, elem);
|
|
|
|
// start from new line
|
|
if (IsStoring())
|
|
{
|
|
persistorThisBinary.AddTextElement(CComBSTR(L"\n"));
|
|
}
|
|
|
|
object.Persist(persistorThisBinary);
|
|
|
|
// new line after contents
|
|
if (IsStoring())
|
|
{
|
|
CComBSTR bstrIndent;
|
|
if (persistorThisBinary.GetCurrentElement().GetTextIndent(bstrIndent, false /*bForAChild*/))
|
|
persistorThisBinary.AddTextElement(bstrIndent);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::Persist
|
|
*
|
|
* PURPOSE: persists XML value as stand-alone object
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLValue xval - value to persist
|
|
* LPCTSTR name - "Name" attribute for element [optional = NULL]
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CPersistor::Persist(CXMLValue xval, LPCTSTR name /*= NULL*/)
|
|
{
|
|
if (xval.UsesBinaryStorage())
|
|
{
|
|
// binary value to be saved to Binary storage.
|
|
// see comment "CONCEPT OF BINARY STORAGE" in "xmbase.h"
|
|
// wrap it into special object, which handles it and pass to Perist method
|
|
CXMLBinaryValue val(xval);
|
|
Persist(val, name);
|
|
}
|
|
else
|
|
{
|
|
// standard value, persist as ordinary element
|
|
CPersistor persistorNew(*this,xval.GetTypeName(),name);
|
|
persistorNew.PersistContents(xval);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::PersistAttribute
|
|
*
|
|
* PURPOSE: Persists attribute
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR name - Name of attribute
|
|
* CXMLValue xval - Value of attribute
|
|
* const XMLAttributeType type - type of attribute [ required/ optional ]
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CPersistor::PersistAttribute(LPCTSTR name, CXMLValue xval, const XMLAttributeType type /*= attr_required*/)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::PersistAttribute"));
|
|
|
|
if(IsLoading())
|
|
{
|
|
CComBSTR bstrPropertyValue;
|
|
bool bValueSupplied = GetCurrentElement().getAttribute(name, bstrPropertyValue);
|
|
|
|
if (bValueSupplied)
|
|
{
|
|
sc = xval.ScReadFromBSTR(bstrPropertyValue);
|
|
if (sc)
|
|
sc.Throw(E_FAIL);
|
|
}
|
|
else if (type != attr_optional)
|
|
sc.Throw(E_FAIL);
|
|
}
|
|
else // IsStoring
|
|
{
|
|
CComBSTR bstr; // must be empty!
|
|
sc = xval.ScWriteToBSTR(&bstr);
|
|
if (sc)
|
|
sc.Throw();
|
|
GetCurrentElement().setAttribute(name, bstr);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::PersistContents
|
|
*
|
|
* PURPOSE: perists XMLValues as a contents of xml element
|
|
* <this_element>persisted_contents</this_element>
|
|
* to be used insted of PersistAttribute where apropriate
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLValue xval - value to persist as contents of the element
|
|
*
|
|
* NOTE: element cannot have both value-as-contents and sub-elements
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CPersistor::PersistContents(CXMLValue xval)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::PersistContents"));
|
|
|
|
if (IsStoring())
|
|
{
|
|
CComBSTR bstr; // must be empty!
|
|
sc = xval.ScWriteToBSTR(&bstr);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
AddTextElement(bstr);
|
|
}
|
|
else
|
|
{
|
|
CComBSTR bstrPropertyValue;
|
|
GetTextElement(bstrPropertyValue);
|
|
|
|
sc = xval.ScReadFromBSTR(bstrPropertyValue);
|
|
if (sc)
|
|
sc.Throw();
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CPersistor::AddElement
|
|
*
|
|
* PURPOSE: Creates a new element below this element with the specified name.
|
|
* All persistence to the new persistor will write underneath this
|
|
* new element.
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr :
|
|
* CPersistor& persistorNew :
|
|
*
|
|
* RETURNS:
|
|
* CXMLElement - created child element
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
CXMLElement
|
|
CPersistor::AddElement(const CStr &strElementType, LPCTSTR szElementName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::AddElement"));
|
|
|
|
CXMLElement elem;
|
|
GetDocument().createElement(NODE_ELEMENT, CComBSTR(strElementType), &elem);
|
|
|
|
CComBSTR bstrIndent;
|
|
if (GetCurrentElement().GetTextIndent(bstrIndent, true /*bForAChild*/))
|
|
AddTextElement(bstrIndent);
|
|
|
|
GetCurrentElement().addChild(elem); // add the new element to the end.
|
|
|
|
if (szElementName)
|
|
{
|
|
CPersistor persistorNew(*this, elem);
|
|
persistorNew.SetName(szElementName);
|
|
}
|
|
|
|
// sub element was added - that means this element will have a closing tag
|
|
// add the indent for it in advance
|
|
if (GetCurrentElement().GetTextIndent(bstrIndent, false /*bForAChild*/))
|
|
AddTextElement(bstrIndent);
|
|
|
|
return elem;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::AddTextElement
|
|
*
|
|
* PURPOSE: creates new element of type "text"
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* CXMLElement - created child element
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CPersistor::AddTextElement(BSTR bstrData)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::AddTextElement"));
|
|
|
|
CXMLElement elem;
|
|
GetDocument().createElement(NODE_TEXT, bstrData, &elem);
|
|
GetCurrentElement().addChild(elem); // add the new element to the end.
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CPersistor::GetElement
|
|
*
|
|
* PURPOSE: Retrievs child element of the current element with the specified type [and name].
|
|
* All persistence to the new persistor will read underneath this element.
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr& strElementType : type name of the element
|
|
* LPCTSTR szElementName : name of the element or NULL if doesn't matter
|
|
* int iIndex : index of the element [optional = -1]
|
|
*
|
|
* RETURNS:
|
|
* CXMLElement - resulting new element
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
CXMLElement
|
|
CPersistor::GetElement(const CStr &strElementType, LPCTSTR szElementName, int iIndex /*= -1*/ )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::GetElement"));
|
|
CXMLElement elem;
|
|
|
|
CXMLElementCollection colChildren;
|
|
GetCurrentElement().getChildrenByName(strElementType, &colChildren);
|
|
|
|
long nChildren = 0;
|
|
|
|
if (!colChildren.IsNull())
|
|
colChildren.get_count(&nChildren);
|
|
|
|
if (nChildren == 0)
|
|
sc.Throw(E_FAIL);
|
|
|
|
long nChild = 0;
|
|
if (iIndex >= 0)
|
|
{
|
|
// limit iteration to one loop, if we have index supplied
|
|
nChild = iIndex;
|
|
nChildren = iIndex + 1;
|
|
}
|
|
for (; nChild < nChildren; nChild++)
|
|
{
|
|
CXMLElement el;
|
|
colChildren.item(nChild, &el);
|
|
|
|
if (!el.IsNull())
|
|
{
|
|
if (szElementName)
|
|
{
|
|
CPersistor temp(*this,el);
|
|
CStr strName(temp.GetName());
|
|
if (0 != strName.CompareNoCase(szElementName))
|
|
continue;
|
|
}
|
|
elem = el;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(elem.IsNull())
|
|
sc.Throw(E_FAIL);
|
|
|
|
return elem;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::GetTextElement
|
|
*
|
|
* PURPOSE: Gets text element attached to the new element
|
|
* NOTE: returned CPersistor may have current element equal NULL -
|
|
* this should indicate to caller that the contents is empty
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* CXMLElement - resulting new element
|
|
*
|
|
\***************************************************************************/
|
|
void
|
|
CPersistor::GetTextElement(CComBSTR &bstrData)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::GetTextElement"));
|
|
|
|
bstrData = L"";
|
|
|
|
CXMLElement elem;
|
|
|
|
CXMLElementCollection colChildren;
|
|
GetCurrentElement().get_children(&colChildren);
|
|
|
|
long nChildren = 0;
|
|
|
|
if (!colChildren.IsNull())
|
|
colChildren.get_count(&nChildren);
|
|
|
|
if (nChildren == 0)
|
|
return; // no text element means "there is no contents"
|
|
|
|
for (long nChild = 0; nChild < nChildren; nChild++)
|
|
{
|
|
CXMLElement el;
|
|
colChildren.item(nChild, &el);
|
|
|
|
if (!el.IsNull())
|
|
{
|
|
DOMNodeType lType = NODE_INVALID;
|
|
el.get_type(&lType);
|
|
if (lType == NODE_TEXT)
|
|
{
|
|
elem = el;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (elem.IsNull())
|
|
return;
|
|
|
|
elem.get_text(bstrData);
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CPersistor::HasElement
|
|
*
|
|
* PURPOSE: checks if persistor has a specified element
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr& strElementType : type name of the element
|
|
* LPCTSTR szElementName : name of the element or NULL if doesn't matter
|
|
*
|
|
* RETURNS:
|
|
* bool true == requested element exist
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
bool
|
|
CPersistor::HasElement(const CStr &strElementType, LPCTSTR szElementName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::HasElement"));
|
|
|
|
if(GetCurrentElement().IsNull())
|
|
sc.Throw(E_POINTER);
|
|
|
|
CXMLElementCollection colChildren;
|
|
GetCurrentElement().getChildrenByName(strElementType, &colChildren);
|
|
|
|
if (colChildren.IsNull())
|
|
return false;
|
|
|
|
long nChildren = 0;
|
|
colChildren.get_count(&nChildren);
|
|
|
|
if (nChildren == 0)
|
|
return false;
|
|
|
|
for (long nChild = 0; nChild < nChildren; nChild++)
|
|
{
|
|
CXMLElement el;
|
|
colChildren.item(nChild, &el);
|
|
|
|
if (!el.IsNull())
|
|
{
|
|
if (szElementName)
|
|
{
|
|
CPersistor temp(*this,el);
|
|
CStr strName(temp.GetName());
|
|
if (0 != strName.CompareNoCase(szElementName))
|
|
continue;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CPersistor::CheckCurrentElement
|
|
*
|
|
* PURPOSE: Checks if current element is of specified type [and name]
|
|
* used to check collection elements
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr& strElementType : type name of the element
|
|
* LPCTSTR szElementName : name of the element or NULL if doesn't matter
|
|
*
|
|
* RETURNS:
|
|
* CXMLElement - pointer to current element
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
CXMLElement
|
|
CPersistor::CheckCurrentElement(const CStr &strElementType, LPCTSTR szElementName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::CheckCurrentElement"));
|
|
|
|
CXMLElement elem = GetCurrentElement();
|
|
|
|
if(elem.IsNull())
|
|
sc.Throw(E_POINTER);
|
|
|
|
CStr strTagName;
|
|
elem.get_tagName(strTagName);
|
|
if (0 != strTagName.CompareNoCase(strElementType))
|
|
sc.Throw(E_FAIL);
|
|
|
|
if (szElementName)
|
|
{
|
|
CPersistor temp(*this, elem);
|
|
CStr strName(temp.GetName());
|
|
if (0 != strName.CompareNoCase(szElementName))
|
|
sc.Throw(E_FAIL);
|
|
}
|
|
|
|
return elem;
|
|
}
|
|
|
|
void
|
|
CPersistor::SetName(const CStr &strName)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::SetName"));
|
|
CStr _strName = strName;
|
|
ASSERT(IsStoring());
|
|
PersistAttribute(XML_ATTR_NAME, _strName);
|
|
}
|
|
|
|
CStr
|
|
CPersistor::GetName()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::GetName"));
|
|
CStr _strName;
|
|
ASSERT(IsLoading());
|
|
// just return empty string if there is no name
|
|
PersistAttribute(XML_ATTR_NAME, _strName, attr_optional);
|
|
return _strName;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::PersistString
|
|
*
|
|
* PURPOSE: persists stringtable string
|
|
*
|
|
* PARAMETERS:
|
|
* const CStr &strTag - tag name for the new element
|
|
* CStringTableStringBase &str - string to persist
|
|
* LPCTSTR lpstrName - name [optional]
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CPersistor::PersistString(LPCTSTR lpstrName, CStringTableStringBase &str)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CPersistor::PersistString"));
|
|
|
|
USES_CONVERSION;
|
|
|
|
CPersistor subPersistor(*this, XML_TAG_STRING_TABLE_STRING, lpstrName);
|
|
if (subPersistor.IsLoading())
|
|
{
|
|
str.m_id = CStringTableStringBase::eNoValue;
|
|
str.m_str.erase();
|
|
|
|
subPersistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_ID, str.m_id, attr_optional);
|
|
if (str.m_id != CStringTableStringBase::eNoValue)
|
|
{
|
|
|
|
sc = ScCheckPointers(str.m_spStringTable);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
ULONG cch = 0;
|
|
sc = str.m_spStringTable->GetStringLength (str.m_id, &cch, NULL);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
// allow for NULL terminator
|
|
cch++;
|
|
std::auto_ptr<WCHAR> spszText (new (std::nothrow) WCHAR[cch]);
|
|
LPWSTR pszText = spszText.get();
|
|
|
|
sc = ScCheckPointers(pszText,E_OUTOFMEMORY);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
sc = str.m_spStringTable->GetString (str.m_id, cch, pszText, NULL, NULL);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
str.m_str = W2T (pszText);
|
|
|
|
return;
|
|
}
|
|
std::wstring text;
|
|
subPersistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_VALUE, text, attr_optional);
|
|
str.m_str = W2CT(text.c_str());
|
|
return;
|
|
}
|
|
|
|
str.CommitToStringTable();
|
|
if (FEnableValueSplit() && str.m_id != CStringTableStringBase::eNoValue)
|
|
{
|
|
#ifdef DBG
|
|
/*
|
|
* make sure CommitToStringTable really committed
|
|
*/
|
|
if (str.m_id != CStringTableStringBase::eNoValue)
|
|
{
|
|
WCHAR sz[256];
|
|
ASSERT (str.m_spStringTable != NULL);
|
|
HRESULT hr = str.m_spStringTable->GetString (str.m_id, countof(sz), sz, NULL, NULL);
|
|
ASSERT (SUCCEEDED(hr) && "Persisted a CStringTableString to a stream that's not in the string table");
|
|
}
|
|
#endif
|
|
subPersistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_ID, str.m_id);
|
|
}
|
|
else
|
|
{
|
|
if (str.m_id == CStringTableStringBase::eNoValue)
|
|
str.m_str.erase();
|
|
subPersistor.PersistAttribute(XML_ATTR_STRING_TABLE_STR_VALUE, str.m_str);
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CPersistor::PersistAttribute
|
|
*
|
|
* PURPOSE: special method to persist bitflags
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR name [in] name of the flags
|
|
* CXMLBitFlags& flags [in] flags to persist
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CPersistor::PersistAttribute(LPCTSTR name, CXMLBitFlags& flags )
|
|
{
|
|
flags.PersistMultipleAttributes(name, *this);
|
|
}
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class XMLPoint
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
XMLPoint::XMLPoint(const CStr &strObjectName, POINT &point)
|
|
:m_strObjectName(strObjectName), m_point(point)
|
|
{
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* XMLPoint::Persist
|
|
*
|
|
* PURPOSE: Persists an XMLPoint to a persistor.
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistor :
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void
|
|
XMLPoint::Persist(CPersistor& persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("XMLPoint::Persist"));
|
|
if (persistor.IsStoring())
|
|
persistor.SetName(m_strObjectName);
|
|
persistor.PersistAttribute(XML_ATTR_POINT_X, m_point.x);
|
|
persistor.PersistAttribute(XML_ATTR_POINT_Y, m_point.y);
|
|
}
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class XMLRect
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
XMLRect::XMLRect(const CStr strObjectName, RECT &rect)
|
|
:m_strObjectName(strObjectName), m_rect(rect)
|
|
{
|
|
}
|
|
|
|
void
|
|
XMLRect::Persist(CPersistor& persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("XMLRect::Persist"));
|
|
if (persistor.IsStoring())
|
|
persistor.SetName(m_strObjectName);
|
|
persistor.PersistAttribute(XML_ATTR_RECT_TOP, m_rect.top);
|
|
persistor.PersistAttribute(XML_ATTR_RECT_BOTTOM, m_rect.bottom);
|
|
persistor.PersistAttribute(XML_ATTR_RECT_LEFT, m_rect.left);
|
|
persistor.PersistAttribute(XML_ATTR_RECT_RIGHT, m_rect.right);
|
|
}
|
|
|
|
//############################################################################
|
|
//############################################################################
|
|
//
|
|
// Implementation of class CXMLValue
|
|
//
|
|
//############################################################################
|
|
//############################################################################
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLValue::GetTypeName
|
|
*
|
|
* PURPOSE: returns tag name (usually type name) to be used as element tag
|
|
* when value is persisted as element via CPersistor.Persist(val)
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* LPCTSTR - tag name
|
|
*
|
|
\***************************************************************************/
|
|
LPCTSTR CXMLValue::GetTypeName() const
|
|
{
|
|
switch(m_type)
|
|
{
|
|
case XT_I4 : return XML_TAG_VALUE_LONG;
|
|
case XT_UI4 : return XML_TAG_VALUE_ULONG;
|
|
case XT_UI1 : return XML_TAG_VALUE_BYTE;
|
|
case XT_I2 : return XML_TAG_VALUE_SHORT;
|
|
case XT_DW : return XML_TAG_VALUE_DWORD;
|
|
case XT_BOOL : return XML_TAG_VALUE_BOOL;
|
|
case XT_CPP_BOOL : return XML_TAG_VALUE_BOOL;
|
|
case XT_UINT : return XML_TAG_VALUE_UINT;
|
|
case XT_INT : return XML_TAG_VALUE_INT;
|
|
case XT_STR : return XML_TAG_VALUE_CSTR;
|
|
case XT_WSTR : return XML_TAG_VALUE_WSTRING;
|
|
case XT_GUID : return XML_TAG_VALUE_GUID;
|
|
case XT_BINARY : return XML_TAG_VALUE_BIN_DATA;
|
|
case XT_EXTENSION: return m_val.pExtension->GetTypeName();
|
|
default: return XML_TAG_VALUE_UNKNOWN;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLValue::ScWriteToBSTR
|
|
*
|
|
* PURPOSE: Converts an XML value to a bstring.
|
|
* internally uses WCHAR buffer on the stack for the conversion of integer
|
|
* types.
|
|
*
|
|
* PARAMETERS:
|
|
* BSTR * pbstr - [out] resulting string
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLValue::ScWriteToBSTR (BSTR * pbstr) const
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLValue::ScWriteToBSTR"));
|
|
|
|
// check parameter
|
|
sc = ScCheckPointers(pbstr);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// initialize
|
|
*pbstr = NULL;
|
|
|
|
WCHAR szBuffer[40];
|
|
int cchBuffer = 40;
|
|
CComBSTR bstrResult;
|
|
USES_CONVERSION;
|
|
|
|
switch(m_type)
|
|
{
|
|
case XT_I4: //LONG
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"%d\0", *m_val.pL);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_UI4: //LONG
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"%u\0", *m_val.pUl);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_UI1: //BYTE
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"0x%02.2x\0", (int)*m_val.pByte);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_I2: //SHORT
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"%d\0", (int)*m_val.pS);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_DW: //DWORD
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"0x%04.4x\0", *m_val.pDw);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_BOOL://BOOL: can either print true/false
|
|
bstrResult = ( *m_val.pBOOL ? XML_VAL_BOOL_TRUE : XML_VAL_BOOL_FALSE );
|
|
break;
|
|
|
|
case XT_CPP_BOOL://bool: can either print true/false
|
|
bstrResult = ( *m_val.pbool ? XML_VAL_BOOL_TRUE : XML_VAL_BOOL_FALSE );
|
|
break;
|
|
|
|
case XT_UINT: //UINT
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"%u\0", *m_val.pUint);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_INT: //UINT
|
|
sc = StringCchPrintfW(szBuffer, cchBuffer, L"%d\0", *m_val.pInt);
|
|
if(sc)
|
|
return sc;
|
|
bstrResult = szBuffer;
|
|
break;
|
|
|
|
case XT_STR: //CStr
|
|
bstrResult = T2COLE(static_cast<LPCTSTR>(*m_val.pStr));
|
|
break;
|
|
|
|
case XT_WSTR: //wstring
|
|
bstrResult = m_val.pWStr->c_str();
|
|
break;
|
|
|
|
case XT_TSTR: //tstring
|
|
bstrResult = T2COLE(m_val.pTStr->c_str());
|
|
break;
|
|
|
|
case XT_GUID: //GUID
|
|
{
|
|
LPOLESTR sz;
|
|
StringFromCLSID(*m_val.pGuid, &sz);
|
|
bstrResult = sz;
|
|
CoTaskMemFree(sz);
|
|
}
|
|
break;
|
|
|
|
case XT_BINARY:
|
|
sc = ScEncodeBinary(bstrResult, *m_val.pXmlBinary);
|
|
if (sc)
|
|
return sc;
|
|
|
|
break;
|
|
|
|
case XT_EXTENSION:
|
|
sc = m_val.pExtension->ScWriteToBSTR (&bstrResult);
|
|
if (sc)
|
|
return sc;
|
|
|
|
break;
|
|
|
|
default:
|
|
//ASSERT(0 && "Should not come here!!");
|
|
return sc = E_NOTIMPL;
|
|
}
|
|
|
|
*pbstr = bstrResult.Detach();
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLValue::ScReadFromBSTR
|
|
*
|
|
* PURPOSE: Converts a string an XML value
|
|
*
|
|
* PARAMETERS:
|
|
* const BSTR bstr - [in] string to be read
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLValue::ScReadFromBSTR(const BSTR bstr)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLValue::ScReadFromBSTR"));
|
|
|
|
LPCOLESTR olestr = bstr;
|
|
if (olestr == NULL) // make sure we always have a valid pointer
|
|
olestr = L""; // in case of NULL we use own empty string
|
|
|
|
USES_CONVERSION;
|
|
switch(m_type)
|
|
{
|
|
case XT_I4: //LONG
|
|
*m_val.pL = wcstol(olestr,NULL,10);
|
|
break;
|
|
|
|
case XT_UI4: //LONG
|
|
*m_val.pUl = wcstoul(olestr,NULL,10);
|
|
break;
|
|
|
|
case XT_UI1: //BYTE
|
|
*m_val.pByte = static_cast<BYTE>(wcstol(olestr,NULL,10));
|
|
break;
|
|
|
|
case XT_I2: //SHORT
|
|
*m_val.pS = static_cast<SHORT>(wcstol(olestr,NULL,10));
|
|
break;
|
|
|
|
case XT_DW: //DWORD
|
|
*m_val.pDw = wcstoul(olestr,NULL,10);
|
|
break;
|
|
|
|
case XT_BOOL://BOOL: can either be true/false
|
|
{
|
|
*m_val.pBOOL = FALSE;
|
|
LPCWSTR pszXmlBool = T2CW(XML_VAL_BOOL_TRUE);
|
|
if (NULL != pszXmlBool)
|
|
{
|
|
*m_val.pBOOL = (0 == _wcsicmp(olestr, pszXmlBool));
|
|
}
|
|
}
|
|
break;
|
|
case XT_CPP_BOOL://bool: can either be true/false
|
|
{
|
|
*m_val.pbool = FALSE;
|
|
LPCWSTR pszXmlBool = T2CW(XML_VAL_BOOL_TRUE);
|
|
if (NULL != pszXmlBool)
|
|
{
|
|
*m_val.pbool = (0 == _wcsicmp(olestr, pszXmlBool));
|
|
}
|
|
}
|
|
break;
|
|
case XT_UINT: //UINT
|
|
*m_val.pUint = wcstoul(olestr,NULL,10);
|
|
break;
|
|
|
|
case XT_INT: //UINT
|
|
*m_val.pInt = wcstol(olestr,NULL,10);
|
|
break;
|
|
|
|
case XT_STR: //CStr
|
|
*m_val.pStr = OLE2CT(olestr);
|
|
break;
|
|
|
|
case XT_WSTR: //CString
|
|
*m_val.pWStr = olestr;
|
|
break;
|
|
|
|
case XT_TSTR: //tstring
|
|
*m_val.pTStr = OLE2CT(olestr);
|
|
break;
|
|
|
|
case XT_GUID: //GUID
|
|
sc = CLSIDFromString(const_cast<LPOLESTR>(olestr), m_val.pGuid);
|
|
if (sc)
|
|
return sc;
|
|
|
|
break;
|
|
|
|
case XT_BINARY:
|
|
sc = ScDecodeBinary(olestr, m_val.pXmlBinary);
|
|
if (sc)
|
|
return sc;
|
|
|
|
break;
|
|
|
|
case XT_EXTENSION:
|
|
sc = m_val.pExtension->ScReadFromBSTR(bstr);
|
|
if (sc)
|
|
return sc;
|
|
|
|
break;
|
|
|
|
default:
|
|
//ASSERT(0 && "Should not come here!!");
|
|
return sc = E_NOTIMPL;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* METHOD: XMLListCollectionBase::Persist
|
|
*
|
|
* PURPOSE: implements persisting of list contents from XML file
|
|
* iterates child elements calling virtual mem. OnNewElement for each
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistorNew : persistor object
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void XMLListCollectionBase::Persist(CPersistor& persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("XMLListCollectionBase::Persist"));
|
|
ASSERT(persistor.IsLoading());
|
|
|
|
CXMLElementCollection colChildren;
|
|
persistor.GetCurrentElement().get_children(&colChildren);
|
|
|
|
if (colChildren.IsNull())
|
|
{
|
|
// no children -> we are done!
|
|
return;
|
|
}
|
|
|
|
long nChildren = 0;
|
|
colChildren.get_count(&nChildren);
|
|
|
|
for (long nChild = 0; nChild < nChildren; nChild++)
|
|
{
|
|
CXMLElement el;
|
|
colChildren.item(nChild, &el);
|
|
|
|
if (!el.IsNull())
|
|
{
|
|
DOMNodeType lType = NODE_INVALID;
|
|
el.get_type(&lType);
|
|
|
|
if (lType == NODE_ELEMENT)
|
|
{
|
|
CPersistor persistorNewLocked(persistor, el, true);
|
|
OnNewElement(persistorNewLocked);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* METHOD: XMLMapCollectionBase::Persist
|
|
*
|
|
* PURPOSE: implements persisting of map contents from XML file
|
|
* iterates child elements calling virtual mem. OnNewElement for each pair
|
|
*
|
|
* PARAMETERS:
|
|
* CPersistor& persistorNew : persistor object
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void XMLMapCollectionBase::Persist(CPersistor& persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("XMLMapCollectionBase::Persist"));
|
|
ASSERT(persistor.IsLoading());
|
|
|
|
CXMLElementCollection colChildren;
|
|
persistor.GetCurrentElement().get_children(&colChildren);
|
|
|
|
if (colChildren.IsNull())
|
|
{
|
|
// no children -> we are done!
|
|
return;
|
|
}
|
|
|
|
long nChildren = 0;
|
|
colChildren.get_count(&nChildren);
|
|
|
|
// collect all elements of proper type
|
|
std::vector<CXMLElement> vecChilds;
|
|
|
|
for (long nChild = 0; nChild < nChildren; nChild ++)
|
|
{
|
|
CXMLElement el;
|
|
colChildren.item(nChild, &el);
|
|
|
|
if (!el.IsNull())
|
|
{
|
|
DOMNodeType lType = NODE_INVALID;
|
|
el.get_type(&lType);
|
|
|
|
if (lType == NODE_ELEMENT)
|
|
vecChilds.push_back(el);
|
|
}
|
|
}
|
|
|
|
|
|
for (nChild = 0; nChild + 1 < vecChilds.size(); nChild += 2)
|
|
{
|
|
CXMLElement el(vecChilds[nChild]);
|
|
CXMLElement el2(vecChilds[nChild+1]);
|
|
|
|
CPersistor persistorNew1(persistor, el, true);
|
|
CPersistor persistorNew2(persistor, el2, true);
|
|
OnNewElement(persistorNew1,persistorNew2);
|
|
}
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* ScEncodeBinary
|
|
*
|
|
* PURPOSE: converts data to encoded format for xml
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - error code
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
static SC ScEncodeBinary(CComBSTR& bstrResult, const CXMLBinary& binSrc)
|
|
{
|
|
DECLARE_SC(sc, TEXT("ScEncodeBinary"));
|
|
|
|
// initialize
|
|
bstrResult.Empty();
|
|
|
|
// nothing if binary is zero size...
|
|
if (binSrc.GetSize() == 0)
|
|
return sc;
|
|
|
|
// line length for the binary data. maximum allowed by base64 per line is 76
|
|
const int line_len = 76;
|
|
// symbols to be placed as terminators of each line
|
|
const WCHAR line_end[] = { 0x0d, 0x0a };
|
|
DWORD dwBytesLeft = binSrc.GetSize();
|
|
// space required for encription
|
|
DWORD dwCount = (dwBytesLeft*8+5)/6;
|
|
// ... plus up to three '='
|
|
dwCount += (4 - dwCount%4) & 0x03;
|
|
// allow space for white_spaces inerted and terminating zero
|
|
dwCount += (dwCount / line_len)*countof(line_end) + 1;
|
|
|
|
BOOL bOk = SysReAllocStringLen(&bstrResult,NULL,dwCount);
|
|
if (bOk != TRUE || (LPOLESTR)bstrResult == NULL)
|
|
return sc = E_OUTOFMEMORY;
|
|
|
|
LPOLESTR pstrResult = bstrResult;
|
|
*pstrResult = 0;
|
|
|
|
if (!dwBytesLeft)
|
|
return sc; // emty seq? - we are done
|
|
|
|
const BYTE *pData = NULL;
|
|
sc = binSrc.ScLockData((const void **)&pData);
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = ScCheckPointers(pData, E_UNEXPECTED);
|
|
if(sc)
|
|
return sc;
|
|
|
|
DWORD dwCharsStored = 0;
|
|
while (dwBytesLeft)
|
|
{
|
|
base64_table::encode(pData, dwBytesLeft, pstrResult);
|
|
dwCharsStored += 4;
|
|
if (0 == (dwCharsStored % line_len) && dwBytesLeft)
|
|
for (int i = 0; i < countof(line_end); i++)
|
|
*pstrResult++ = line_end[i];
|
|
}
|
|
|
|
// terminate
|
|
*pstrResult = 0;
|
|
|
|
sc = binSrc.ScUnlockData();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* ScDecodeBinary
|
|
*
|
|
* PURPOSE: converts encoded data back to image
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
static SC ScDecodeBinary(const CComBSTR& bstrSource, CXMLBinary *pBinResult)
|
|
{
|
|
DECLARE_SC(sc, TEXT("ScDecodeBinary"));
|
|
|
|
DWORD dwCount = bstrSource.Length();
|
|
DWORD dwSize = (dwCount*6+7)/8;
|
|
|
|
sc = ScCheckPointers(pBinResult);
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = pBinResult->ScFree(); // ignore the error here
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
if (!dwSize) // no data? - good
|
|
return sc;
|
|
|
|
sc = pBinResult->ScAlloc(dwSize);
|
|
if(sc)
|
|
return sc;
|
|
|
|
CXMLBinaryLock sLock(*pBinResult);
|
|
|
|
BYTE *pData = NULL;
|
|
sc = sLock.ScLock(&pData);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// recheck
|
|
sc = ScCheckPointers(pData, E_UNEXPECTED);
|
|
if (sc)
|
|
return sc;
|
|
|
|
BYTE * const pDataStart = pData;
|
|
|
|
LPOLESTR pInput = bstrSource;
|
|
|
|
while(base64_table::decode(pInput, pData));
|
|
|
|
sc = sLock.ScUnlock();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
DWORD dwDataDecoded = pData - pDataStart;
|
|
|
|
// fix data size , if required
|
|
|
|
if (dwDataDecoded != dwSize)
|
|
{
|
|
if (dwDataDecoded == 0)
|
|
sc = pBinResult->ScFree();
|
|
else
|
|
sc = pBinResult->ScRealloc(dwDataDecoded);
|
|
|
|
if (sc)
|
|
return sc;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStorage::ScInitialize
|
|
*
|
|
* PURPOSE: Initializes object. Creates new storage if does not have one
|
|
*
|
|
* PARAMETERS:
|
|
* bool& bCreatedNewOne [out] - created new stream
|
|
*
|
|
* RETURNS:
|
|
* SC - result code.
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStorage::ScInitialize(bool& bCreatedNewOne)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStorage::ScInitialize"));
|
|
|
|
// short cut if initialized oalready
|
|
if (m_Storage != NULL)
|
|
{
|
|
bCreatedNewOne = false;
|
|
return sc;
|
|
}
|
|
|
|
bCreatedNewOne = true;
|
|
|
|
// create the ILockBytes
|
|
sc = CreateILockBytesOnHGlobal(NULL, TRUE, &m_LockBytes);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// create the IStorage
|
|
sc = StgCreateDocfileOnILockBytes( m_LockBytes,
|
|
STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
|
|
0, &m_Storage);
|
|
if(sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStorage::ScInitializeFrom
|
|
*
|
|
* PURPOSE: Initializes object. copies contents from provided source
|
|
*
|
|
* PARAMETERS:
|
|
* IStorage *pSource [in] initial contents of the storage
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStorage::ScInitializeFrom( IStorage *pSource )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStorage::ScInitializeFrom"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers( pSource );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// init empty
|
|
bool bCreatedNewOne = false; // not used here
|
|
sc = ScInitialize(bCreatedNewOne);
|
|
if (sc)
|
|
return sc;
|
|
|
|
ASSERT( m_Storage != NULL );
|
|
|
|
// copy contents
|
|
sc = pSource->CopyTo( 0, NULL, NULL, m_Storage );
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStorage::ScGetIStorage
|
|
*
|
|
* PURPOSE: returns pointer to maintained storage.
|
|
*
|
|
* PARAMETERS:
|
|
* IStorage **ppStorage [out] pointer to the storage
|
|
*
|
|
* RETURNS:
|
|
* SC - result code.
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStorage::ScGetIStorage( IStorage **ppStorage )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStorage::ScGetIStorage"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers( ppStorage );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// init out parameter
|
|
*ppStorage = NULL;
|
|
|
|
// make sure we have storage - initialize
|
|
bool bCreatedNewOne = false; // not used here
|
|
sc = ScInitialize( bCreatedNewOne );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// recheck if the member is set
|
|
sc = ScCheckPointers ( m_Storage, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// return the pointer
|
|
*ppStorage = m_Storage;
|
|
(*ppStorage)->AddRef();
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* METHOD: CXML_IStorage::ScRequestSave
|
|
*
|
|
* PURPOSE: asks snapin to save using snapin's IPersistStorage
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
SC
|
|
CXML_IStorage::ScRequestSave( IPersistStorage * pPersistStorage )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStorage::ScRequestSave"));
|
|
|
|
bool bCreatedNewOne = false;
|
|
sc = ScInitialize( bCreatedNewOne );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// recheck the pointer
|
|
sc = ScCheckPointers( m_Storage, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = pPersistStorage->Save(m_Storage, !bCreatedNewOne);
|
|
if(sc)
|
|
return sc;
|
|
|
|
sc = pPersistStorage->SaveCompleted(NULL);
|
|
// we were always passing a storage in MMC 1.2, so some of the
|
|
// snapins did not expect it to be NULL (which is correct value when
|
|
// storage does not change)
|
|
// to be able to save such snapins we need to to ignore this error
|
|
// see bug 96344
|
|
if (sc == SC(E_INVALIDARG))
|
|
{
|
|
#ifdef DBG
|
|
m_dbg_Data.TraceErr(_T("IPersistStorage::SaveCompleted"), _T("legal argument NULL passed to snapin, but error returned"));
|
|
#endif
|
|
|
|
sc = pPersistStorage->SaveCompleted(m_Storage);
|
|
}
|
|
|
|
if(sc)
|
|
return sc;
|
|
|
|
// commit the changes - this ensures everything is in HGLOBAL
|
|
sc = m_Storage->Commit( STGC_DEFAULT );
|
|
if(sc)
|
|
return sc;
|
|
|
|
#ifdef DBG
|
|
if (S_FALSE != pPersistStorage->IsDirty())
|
|
m_dbg_Data.TraceErr(_T("IPersistStorage"), _T("Reports 'IsDirty' right after 'Save'"));
|
|
#endif // #ifdef DBG
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* m_Storage: CXML_IStorage::Persist
|
|
*
|
|
* PURPOSE: dumps data to HGLOBAL and persists
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void
|
|
CXML_IStorage::Persist(CPersistor &persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStorage::Persist"));
|
|
|
|
if (persistor.IsStoring())
|
|
{
|
|
bool bCreatedNewOne = false; // not used here
|
|
sc = ScInitialize( bCreatedNewOne );
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
HANDLE hStorage = NULL;
|
|
sc = GetHGlobalFromILockBytes(m_LockBytes, &hStorage);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
STATSTG statstg;
|
|
ZeroMemory(&statstg, sizeof(statstg));
|
|
|
|
sc = m_LockBytes->Stat(&statstg, STATFLAG_NONAME);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CXMLBinary binInitial;
|
|
binInitial.Attach(hStorage, statstg.cbSize.LowPart);
|
|
|
|
// persist the contents
|
|
persistor.PersistContents(binInitial);
|
|
|
|
return; // done
|
|
}
|
|
|
|
//--- Loading ---
|
|
CXMLAutoBinary binLoaded;
|
|
persistor.PersistContents(binLoaded);
|
|
|
|
// Need to recreate storage...
|
|
ASSERT(persistor.IsLoading()); // should not reallocate else!!
|
|
m_LockBytes = NULL;
|
|
|
|
ULARGE_INTEGER new_size = { binLoaded.GetSize(), 0 };
|
|
sc = CreateILockBytesOnHGlobal(binLoaded.GetHandle(), TRUE, &m_LockBytes);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
// control transferred to ILockBytes
|
|
binLoaded.Detach();
|
|
|
|
sc = m_LockBytes->SetSize(new_size);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
sc = StgOpenStorageOnILockBytes(m_LockBytes, NULL , STGM_SHARE_EXCLUSIVE | STGM_READWRITE,
|
|
NULL, 0, &m_Storage);
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStream::ScInitialize
|
|
*
|
|
* PURPOSE: initializes object. creates empty stream if does not have one
|
|
*
|
|
* PARAMETERS:
|
|
* bool& bCreatedNewOne [out] - created new stream
|
|
*
|
|
* RETURNS:
|
|
* SC - result code.
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStream::ScInitialize( bool& bCreatedNewOne )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStream::ScInitialize"));
|
|
|
|
if (m_Stream != NULL)
|
|
{
|
|
bCreatedNewOne = false;
|
|
return sc;
|
|
}
|
|
|
|
bCreatedNewOne = true;
|
|
|
|
sc = CreateStreamOnHGlobal( NULL, TRUE, &m_Stream);
|
|
if(sc)
|
|
return sc;
|
|
|
|
const ULARGE_INTEGER zero_size = {0,0};
|
|
sc = m_Stream->SetSize(zero_size);
|
|
if(sc)
|
|
return sc;
|
|
|
|
sc = ScSeekBeginning();
|
|
if(sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStream::ScInitializeFrom
|
|
*
|
|
* PURPOSE: Initializes object. Copies contents from provided source
|
|
*
|
|
* PARAMETERS:
|
|
* IStream *pSource [in] initial contents of the stream
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStream::ScInitializeFrom( IStream *pSource )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStream::ScInitializeFrom"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers( pSource );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// initialize empty
|
|
bool bCreatedNewOne = false; // not used here
|
|
sc = ScInitialize( bCreatedNewOne );
|
|
if (sc)
|
|
return sc;
|
|
|
|
ASSERT( m_Stream != NULL );
|
|
|
|
// reset stream pointer
|
|
sc = ScSeekBeginning();
|
|
if(sc)
|
|
return sc;
|
|
|
|
// copy contents from source
|
|
STATSTG statstg;
|
|
sc = pSource->Stat(&statstg, STATFLAG_NONAME);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// copy contents
|
|
ULARGE_INTEGER cbRead;
|
|
ULARGE_INTEGER cbWritten;
|
|
sc = pSource->CopyTo( m_Stream, statstg.cbSize, &cbRead, &cbWritten );
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStream::ScSeekBeginning
|
|
*
|
|
* PURPOSE: resets stream pointer to the beginning of the stream
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStream::ScSeekBeginning()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStream::ScSeekBeginning"));
|
|
|
|
LARGE_INTEGER null_offset = { 0, 0 };
|
|
sc = m_Stream->Seek(null_offset, STREAM_SEEK_SET, NULL);
|
|
if(sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXML_IStream::ScGetIStream
|
|
*
|
|
* PURPOSE: returns the pointer to maintained stream
|
|
*
|
|
* PARAMETERS:
|
|
* IStream **ppStream
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXML_IStream::ScGetIStream( IStream **ppStream )
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStream::ScGetIStream"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers( ppStream );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// init out parameter
|
|
*ppStream = NULL;
|
|
|
|
bool bCreatedNewOne = false; // not used here
|
|
sc = ScInitialize( bCreatedNewOne );
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = ScSeekBeginning();
|
|
if (sc)
|
|
return sc;
|
|
|
|
// recheck if the member is set
|
|
sc = ScCheckPointers ( m_Stream, E_UNEXPECTED );
|
|
if (sc)
|
|
return sc;
|
|
|
|
*ppStream = m_Stream;
|
|
(*ppStream)->AddRef();
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* METHOD: CXML_IStream::Persist
|
|
*
|
|
* PURPOSE: persist data of maintained IStream
|
|
*
|
|
* NOTE: Object may point to another Stream after this method
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void
|
|
CXML_IStream::Persist(CPersistor &persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXML_IStream::Persist"));
|
|
|
|
if (persistor.IsStoring())
|
|
{
|
|
bool bCreatedNewOne = false; // not used here
|
|
sc = ScInitialize( bCreatedNewOne );
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
sc = ScCheckPointers(m_Stream, E_UNEXPECTED);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
HANDLE hStream = NULL;
|
|
sc = GetHGlobalFromStream( m_Stream, &hStream );
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
STATSTG statstg;
|
|
ZeroMemory(&statstg, sizeof(statstg));
|
|
|
|
sc = m_Stream->Stat(&statstg, STATFLAG_NONAME);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
CXMLBinary binInitial;
|
|
binInitial.Attach(hStream, statstg.cbSize.LowPart);
|
|
|
|
// persist the contents
|
|
persistor.PersistContents(binInitial);
|
|
|
|
return; // done
|
|
}
|
|
|
|
//--- Loading ---
|
|
CXMLAutoBinary binLoaded;
|
|
persistor.PersistContents(binLoaded);
|
|
|
|
// Need to recreate stream...
|
|
ULARGE_INTEGER new_size = { binLoaded.GetSize(), 0 };
|
|
sc = CreateStreamOnHGlobal(binLoaded.GetHandle(), TRUE, &m_Stream);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
// control transferred to IStream
|
|
binLoaded.Detach();
|
|
|
|
sc = m_Stream->SetSize(new_size);
|
|
if(sc)
|
|
sc.Throw();
|
|
|
|
// reset stream pointer
|
|
sc = ScSeekBeginning();
|
|
if(sc)
|
|
sc.Throw();
|
|
}
|
|
|
|
/***************************************************************************\
|
|
| trace support helper for CHK builds
|
|
\***************************************************************************/
|
|
#ifdef DBG
|
|
void CXML_IStream::DBG_TraceNotResettingDirty(LPCSTR strIntfName)
|
|
{
|
|
USES_CONVERSION;
|
|
tstring inft = A2CT(strIntfName); // get the name of interface
|
|
inft.erase(inft.begin(), inft.begin() + strlen("struct ")); // cut the 'struct' off
|
|
|
|
m_dbg_Data.TraceErr(inft.c_str(), _T("Reports 'IsDirty' right after 'Save'"));
|
|
}
|
|
#endif
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* METHOD: CXMLPersistableIcon::Persist
|
|
*
|
|
* PURPOSE: persists icon contents
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
|
|
void CXMLPersistableIcon::Persist(CPersistor &persistor)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLPersistableIcon::Persist"));
|
|
|
|
persistor.PersistAttribute(XML_ATTR_ICON_INDEX, m_Icon.m_Data.m_nIndex);
|
|
CStr strIconFile = m_Icon.m_Data.m_strIconFile.c_str();
|
|
persistor.PersistAttribute(XML_ATTR_ICON_FILE, strIconFile);
|
|
m_Icon.m_Data.m_strIconFile = strIconFile;
|
|
|
|
CXMLIcon iconLarge (XML_ATTR_CONSOLE_ICON_LARGE);
|
|
CXMLIcon iconSmall (XML_ATTR_CONSOLE_ICON_SMALL);
|
|
|
|
if (persistor.IsStoring())
|
|
{
|
|
iconLarge = m_Icon.m_icon32;
|
|
iconSmall = m_Icon.m_icon16;
|
|
}
|
|
|
|
// keep this order intact to allow icon lookup by shellext
|
|
persistor.Persist (iconLarge, XML_NAME_ICON_LARGE);
|
|
persistor.Persist (iconSmall, XML_NAME_ICON_SMALL);
|
|
|
|
if (persistor.IsLoading())
|
|
{
|
|
m_Icon.m_icon32 = iconLarge;
|
|
m_Icon.m_icon16 = iconSmall;
|
|
}
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* FUNCTION: ScReadDataFromFile
|
|
*
|
|
* PURPOSE: reads file data to global memory
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
static SC ScReadDataFromFile(LPCTSTR strName, CXMLBinary *pBinResult)
|
|
{
|
|
DECLARE_SC(sc, TEXT("ScReadDataFromFile"));
|
|
|
|
// check parameter
|
|
sc = ScCheckPointers(pBinResult);
|
|
if (sc)
|
|
return sc;
|
|
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// try to open existing file
|
|
hFile = CreateFile(strName,
|
|
GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL
|
|
);
|
|
|
|
// check if we are unable to get to the file
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
sc.FromWin32(GetLastError());
|
|
return sc;
|
|
}
|
|
|
|
// see how large the file is
|
|
ULARGE_INTEGER cbCurrSize;
|
|
cbCurrSize.LowPart = GetFileSize(hFile,&cbCurrSize.HighPart);
|
|
if (cbCurrSize.HighPart != 0 || (LONG)(cbCurrSize.LowPart) < 0) // limiting to 2GB
|
|
{
|
|
sc = E_UNEXPECTED;
|
|
goto CleanUpAndExit;
|
|
}
|
|
|
|
if (!cbCurrSize.LowPart)
|
|
{
|
|
// empty file. ok at this point
|
|
goto CleanUpAndExit;
|
|
}
|
|
|
|
sc = pBinResult->ScAlloc(cbCurrSize.LowPart);
|
|
if (sc)
|
|
goto CleanUpAndExit;
|
|
|
|
{ // scoping for vars
|
|
|
|
// no the time to do some reading
|
|
DWORD dwRead = 0;
|
|
BOOL bRead = FALSE;
|
|
|
|
CXMLBinaryLock sLock(*pBinResult); // will unlock in destructor
|
|
|
|
LPVOID pData = NULL;
|
|
sc = sLock.ScLock(&pData);
|
|
if (sc)
|
|
goto CleanUpAndExit;
|
|
|
|
sc = ScCheckPointers(pData,E_OUTOFMEMORY);
|
|
if (sc)
|
|
goto CleanUpAndExit;
|
|
|
|
bRead = ReadFile(hFile,pData,cbCurrSize.LowPart,&dwRead,NULL);
|
|
if (!bRead)
|
|
{
|
|
sc.FromLastError();
|
|
goto CleanUpAndExit;
|
|
}
|
|
else if (dwRead != cbCurrSize.LowPart)
|
|
{
|
|
// something strange
|
|
sc = E_UNEXPECTED;
|
|
goto CleanUpAndExit;
|
|
}
|
|
} // scoping for vars
|
|
|
|
CleanUpAndExit:
|
|
|
|
CloseHandle(hFile);
|
|
return sc;
|
|
}
|
|
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* FUNCTION: ScSaveXMLDocumentToString
|
|
*
|
|
* PURPOSE: stores contents of XML document into the string
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
SC ScSaveXMLDocumentToString(CXMLDocument& xmlDocument, std::wstring& strResult)
|
|
{
|
|
DECLARE_SC(sc, TEXT("ScSaveXMLDocumentToString"));
|
|
|
|
CComBSTR bstrResult;
|
|
sc = xmlDocument.ScSave(bstrResult);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// allocate and copy string
|
|
strResult = bstrResult;
|
|
|
|
// now remove all the \n and \r characters
|
|
tstring::size_type pos;
|
|
while ((pos = strResult.find_first_of(L"\n\r")) != strResult.npos)
|
|
strResult.erase(pos, 1);
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
* CXMLVariant::Persist
|
|
*
|
|
* Persists a CXMLVariant to/from an XML persistor.
|
|
*--------------------------------------------------------------------------*/
|
|
|
|
#define ValNamePair(x) { x, L#x }
|
|
|
|
struct VARTYPE_MAP
|
|
{
|
|
VARENUM vt;
|
|
LPCWSTR pszName;
|
|
};
|
|
|
|
void CXMLVariant::Persist (CPersistor &persistor)
|
|
{
|
|
DECLARE_SC (sc, _T("CXMLVariant::Persist"));
|
|
|
|
static const VARTYPE_MAP TypeMap[] =
|
|
{
|
|
ValNamePair (VT_EMPTY),
|
|
ValNamePair (VT_NULL),
|
|
ValNamePair (VT_I2),
|
|
ValNamePair (VT_I4),
|
|
ValNamePair (VT_R4),
|
|
ValNamePair (VT_R8),
|
|
ValNamePair (VT_CY),
|
|
ValNamePair (VT_DATE),
|
|
ValNamePair (VT_BSTR),
|
|
ValNamePair (VT_ERROR),
|
|
// ValNamePair (VT_BOOL), VT_BOOL is handled as a special case
|
|
ValNamePair (VT_DECIMAL),
|
|
ValNamePair (VT_I1),
|
|
ValNamePair (VT_UI1),
|
|
ValNamePair (VT_UI2),
|
|
ValNamePair (VT_UI4),
|
|
ValNamePair (VT_INT),
|
|
ValNamePair (VT_UINT),
|
|
};
|
|
|
|
std::wstring strValue, strType;
|
|
|
|
/*
|
|
* storing?
|
|
*/
|
|
if (persistor.IsStoring())
|
|
{
|
|
/*
|
|
* can't store variants that aren't "simple" (i.e. by-ref, array, etc.)
|
|
*/
|
|
if (!IsPersistable())
|
|
(sc = E_FAIL).Throw();
|
|
|
|
/*
|
|
* special case for VT_BOOL
|
|
*/
|
|
if (V_VT(this) == VT_BOOL)
|
|
{
|
|
strValue = (V_BOOL(this) == VARIANT_FALSE) ? L"False" : L"True";
|
|
strType = L"VT_BOOL";
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* we can only VARIANTs that can be converted to text
|
|
*/
|
|
CComVariant varPersist;
|
|
sc = varPersist.ChangeType (VT_BSTR, this);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
/*
|
|
* find the name for the type we're persisting
|
|
*/
|
|
for (int i = 0; i < countof (TypeMap); i++)
|
|
{
|
|
if (V_VT(this) == TypeMap[i].vt)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* unrecognized type that's convertible to string?
|
|
*/
|
|
if (i >= countof (TypeMap))
|
|
(sc = E_FAIL).Throw();
|
|
|
|
/*
|
|
* set the values that'll get saved
|
|
*/
|
|
strValue = V_BSTR(&varPersist);
|
|
strType = TypeMap[i].pszName;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* put to/get from the persistor
|
|
*/
|
|
persistor.PersistAttribute (XML_ATTR_VARIANT_VALUE, strValue);
|
|
persistor.PersistAttribute (XML_ATTR_VARIANT_TYPE, strType);
|
|
|
|
/*
|
|
* loading?
|
|
*/
|
|
if (persistor.IsLoading())
|
|
{
|
|
/*
|
|
* clear out the current contents
|
|
*/
|
|
Clear();
|
|
|
|
/*
|
|
* special case for VT_BOOL
|
|
*/
|
|
if (strType == L"VT_BOOL")
|
|
{
|
|
V_VT (this) = VT_BOOL;
|
|
V_BOOL(this) = (_wcsicmp (strValue.data(), L"False")) ? VARIANT_FALSE : VARIANT_TRUE;
|
|
}
|
|
|
|
else
|
|
{
|
|
/*
|
|
* find the VARIANT type in our map so we can convert back
|
|
* to the right type
|
|
*/
|
|
for (int i = 0; i < countof (TypeMap); i++)
|
|
{
|
|
if (strType == TypeMap[i].pszName)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* unrecognized type that's convertible to string?
|
|
*/
|
|
if (i >= countof (TypeMap))
|
|
(sc = E_FAIL).Throw();
|
|
|
|
/*
|
|
* convert from string back to the original type
|
|
*/
|
|
CComVariant varPersisted (strValue.data());
|
|
sc = ChangeType (TypeMap[i].vt, &varPersisted);
|
|
if (sc)
|
|
sc.Throw();
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLEnumeration::ScReadFromBSTR
|
|
*
|
|
* PURPOSE: reads value from BSTR and evaluates (decodes) it
|
|
*
|
|
* PARAMETERS:
|
|
* const BSTR bstr - [in] string containing the value
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLEnumeration::ScReadFromBSTR(const BSTR bstr)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLEnumeration::ScReadFromBSTR"));
|
|
|
|
// parameter check. (null BSTR is legal, but we do not support empty values either)
|
|
sc = ScCheckPointers(bstr);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// convert to TSTRING
|
|
USES_CONVERSION;
|
|
LPCTSTR strInput = OLE2CT(bstr);
|
|
|
|
// find a match in the mapping array
|
|
for (size_t idx = 0; idx < m_count; idx ++)
|
|
{
|
|
if ( 0 == _tcscmp(strInput, m_pMaps[idx].m_literal) )
|
|
{
|
|
// found! set enum to proper value
|
|
m_rVal = static_cast<enum_t>(m_pMaps[idx].m_enum);
|
|
return sc;
|
|
}
|
|
}
|
|
// didn't find? - too bad
|
|
return sc = E_INVALIDARG;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLEnumeration::ScWriteToBSTR
|
|
*
|
|
* PURPOSE: Strores (prints) value into BSTR to be used in XML document
|
|
*
|
|
* PARAMETERS:
|
|
* BSTR * pbstr [out] resulting string
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLEnumeration::ScWriteToBSTR (BSTR * pbstr ) const
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLEnumeration::ScWriteToBSTR"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(pbstr);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// initialization
|
|
*pbstr = NULL;
|
|
|
|
// find string representation for enum
|
|
for (size_t idx = 0; idx < m_count; idx ++)
|
|
{
|
|
if ( m_pMaps[idx].m_enum == (UINT)m_rVal )
|
|
{
|
|
// found! - return it
|
|
*pbstr = CComBSTR(m_pMaps[idx].m_literal).Detach();
|
|
return sc;
|
|
}
|
|
}
|
|
|
|
// didn't find? - too bad
|
|
return sc = E_INVALIDARG;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBitFlags::PersistMultipleAttributes
|
|
*
|
|
* PURPOSE: perists bitflags as separate attributes. These are stored as
|
|
* attributes of the PARENT object, using the names specified in the
|
|
* name map. Any unknown flags are stored in an attribute
|
|
* specified by the name parameter, in numerical form.
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR name [in] flag name (used only for not recognized flags)
|
|
* CPersistor &persistor [in] persistor to perform operation on
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CXMLBitFlags::PersistMultipleAttributes(LPCTSTR name, CPersistor &persistor)
|
|
{
|
|
// temporaries
|
|
UINT uiValToSave = persistor.IsStoring() ? m_rVal : 0;
|
|
UINT uiValLoaded = 0;
|
|
|
|
// iterate thru all the entries in the map
|
|
for (size_t idx = 0; idx < m_count; idx ++)
|
|
{
|
|
UINT uiMask = m_pMaps[idx].m_enum;
|
|
|
|
// we do only care about true flags - any nonzero value.
|
|
if (!uiMask)
|
|
continue;
|
|
|
|
// initialize the value properly for storing
|
|
// when loading (it will remain the same if attribute isn't found)
|
|
bool bValue = false;
|
|
if ( (uiValToSave & uiMask) == uiMask )
|
|
{
|
|
bValue = true;
|
|
uiValToSave &= ~uiMask; // since we have taken care of this, remove the bits.
|
|
// anything left over is saved numerically (see below)
|
|
}
|
|
|
|
// do not store "false" values - they are useless
|
|
bool bNeedsPersisting = persistor.IsLoading() || bValue;
|
|
|
|
if (bNeedsPersisting)
|
|
persistor.PersistAttribute( m_pMaps[idx].m_literal, CXMLBoolean(bValue), attr_optional );
|
|
|
|
uiValLoaded |= bValue ? uiMask : 0;
|
|
}
|
|
|
|
/* If there are any flags which do not have a corresponding text version,
|
|
these are persisted using the original name of the attribute, with the numerical
|
|
value of the flags*/
|
|
UINT uiValTheRest = uiValToSave;
|
|
bool bNeedsPersisting = persistor.IsLoading() || (uiValTheRest != 0);
|
|
if (bNeedsPersisting)
|
|
persistor.PersistAttribute( name, uiValTheRest, attr_optional );
|
|
|
|
uiValLoaded |= uiValTheRest;
|
|
|
|
if (persistor.IsLoading())
|
|
m_rVal = uiValLoaded;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::CXMLBinary
|
|
*
|
|
* PURPOSE: default constructor
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
\***************************************************************************/
|
|
CXMLBinary::CXMLBinary() :
|
|
m_Handle(NULL),
|
|
m_Size(0),
|
|
m_Locks(0)
|
|
{
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::CXMLBinary
|
|
*
|
|
* PURPOSE: constructor
|
|
*
|
|
* PARAMETERS:
|
|
* HGLOBAL handle - handle to attach to
|
|
* size_t size - real size of data
|
|
*
|
|
\***************************************************************************/
|
|
CXMLBinary::CXMLBinary(HGLOBAL handle, size_t size) :
|
|
m_Handle(handle),
|
|
m_Size(size),
|
|
m_Locks(0)
|
|
{
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::Attach
|
|
*
|
|
* PURPOSE: Attaches object to allocated data. Will free the data it already has
|
|
*
|
|
* PARAMETERS:
|
|
* HGLOBAL handle - handle to attach to
|
|
* size_t size - real size of data
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
void CXMLBinary::Attach(HGLOBAL handle, size_t size)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinary::Attach"));
|
|
|
|
sc = ScFree();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
ASSERT(m_Handle == NULL && m_Size == 0 && m_Locks == 0);
|
|
m_Handle = handle;
|
|
m_Size = size;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::Detach
|
|
*
|
|
* PURPOSE: transfers control to the caller
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* HGLOBAL - handle of allocated memory
|
|
*
|
|
\***************************************************************************/
|
|
HGLOBAL CXMLBinary::Detach()
|
|
{
|
|
HGLOBAL ret = m_Handle;
|
|
m_Handle = NULL;
|
|
m_Size = 0;
|
|
m_Locks = 0;
|
|
return ret;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::GetSize
|
|
*
|
|
* PURPOSE: returns the size of binary data
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* size_t - size
|
|
*
|
|
\***************************************************************************/
|
|
size_t CXMLBinary::GetSize() const
|
|
{
|
|
return m_Size;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::GetHandle
|
|
*
|
|
* PURPOSE: returns handle to allocated memory (NULL if size is zero)
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* HGLOBAL - handle
|
|
*
|
|
\***************************************************************************/
|
|
HGLOBAL CXMLBinary::GetHandle() const
|
|
{
|
|
return m_Handle;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::ScAlloc
|
|
*
|
|
* PURPOSE: allocates the memory for binary data. Previosly allocated date will
|
|
* be fred.
|
|
*
|
|
* NOTE: 0 in general is a valid size, GetHandle will return NULL in that case
|
|
* ScLock however will fail
|
|
*
|
|
* PARAMETERS:
|
|
* size_t size - new size of binary data
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinary::ScAlloc(size_t size, bool fZeroInit /* =false */)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinary::ScAlloc"));
|
|
|
|
if (size == 0) // use ScFree to free the data
|
|
return sc = E_INVALIDARG;
|
|
|
|
sc = ScFree();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
|
|
ASSERT(m_Handle == NULL && m_Size == 0 && m_Locks == 0);
|
|
|
|
DWORD dwFlags = GMEM_MOVEABLE;
|
|
if (fZeroInit)
|
|
dwFlags |= GMEM_ZEROINIT;
|
|
|
|
m_Handle = GlobalAlloc(dwFlags, size);
|
|
if (!m_Handle)
|
|
return sc.FromLastError(), sc;
|
|
|
|
m_Size = size;
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::ScRealloc
|
|
*
|
|
* PURPOSE: reallocates the data. If data is present it will be coppied over
|
|
*
|
|
* PARAMETERS:
|
|
* size_t new_size - new binary data size
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinary::ScRealloc(size_t new_size, bool fZeroInit /* =false */)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinary::ScRealloc"));
|
|
|
|
if (new_size == 0) // use ScFree to fre the data
|
|
return sc = E_INVALIDARG;
|
|
|
|
if (m_Size == 0) // use Alloc to allocate new data
|
|
return sc = E_UNEXPECTED;
|
|
|
|
ASSERT(m_Handle != NULL && m_Locks == 0);
|
|
|
|
if (m_Handle == NULL)
|
|
return sc = E_UNEXPECTED;
|
|
|
|
HGLOBAL hgNew = GlobalReAlloc(m_Handle, new_size, fZeroInit ? GMEM_ZEROINIT : 0);
|
|
if (!hgNew)
|
|
return sc.FromLastError(), sc;
|
|
|
|
m_Handle = hgNew;
|
|
m_Size = new_size;
|
|
m_Locks = 0;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::ScUnlock
|
|
*
|
|
* PURPOSE: Remove one lock from binary data
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinary::ScUnlockData() const
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinary::ScUnlockData()"));
|
|
|
|
ASSERT(m_Handle != NULL && m_Locks != 0);
|
|
|
|
if (!m_Locks || m_Handle == NULL)
|
|
return sc = E_UNEXPECTED;
|
|
|
|
GlobalUnlock(m_Handle);
|
|
--m_Locks;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::Free
|
|
*
|
|
* PURPOSE: Frees data asociated with the object
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinary::ScFree()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinary::ScFree"));
|
|
|
|
while(m_Locks)
|
|
{
|
|
sc = ScUnlockData();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
}
|
|
|
|
if (m_Handle)
|
|
GlobalFree(m_Handle);
|
|
|
|
Detach(); // null the handle, etc.
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinary::ScLockData
|
|
*
|
|
* PURPOSE: Helper function used frol ScLock templates
|
|
*
|
|
* PARAMETERS:
|
|
* const void **ppData
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinary::ScLockData(const void **ppData) const
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinary::ScLockData"));
|
|
|
|
// paramter check
|
|
sc = ScCheckPointers(ppData);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// initialization
|
|
*ppData = NULL;
|
|
|
|
// data allocated?
|
|
if (!m_Handle)
|
|
return sc = E_POINTER;
|
|
|
|
// lock
|
|
*ppData = GlobalLock(m_Handle);
|
|
|
|
// recheck
|
|
if (*ppData == NULL)
|
|
return sc.FromLastError(), sc;
|
|
|
|
++m_Locks; // keep count of locks
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinaryLock::CXMLBinaryLock
|
|
*
|
|
* PURPOSE: constructor
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLBinary& binary - object to perform locking on
|
|
*
|
|
\***************************************************************************/
|
|
CXMLBinaryLock::CXMLBinaryLock(CXMLBinary& binary) :
|
|
m_rBinary(binary),
|
|
m_bLocked(false)
|
|
{
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinaryLock::~CXMLBinaryLock
|
|
*
|
|
* PURPOSE: destructor; also removes existing lock
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
\***************************************************************************/
|
|
CXMLBinaryLock::~CXMLBinaryLock()
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinaryLock::~CXMLBinaryLock"));
|
|
|
|
if (m_bLocked)
|
|
{
|
|
sc = ScUnlock();
|
|
if (sc)
|
|
sc.TraceAndClear();
|
|
}
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinaryLock::ScLockWorker
|
|
*
|
|
* PURPOSE: type-insensitive lock method (helper)
|
|
*
|
|
* PARAMETERS:
|
|
* void **ppData - pointer to locked data
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinaryLock::ScLockWorker(void **ppData)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CXMLBinaryLock::ScLockWorker"));
|
|
|
|
if (m_bLocked)
|
|
return sc = E_UNEXPECTED;
|
|
|
|
sc = m_rBinary.ScLockData(reinterpret_cast<void**>(ppData));
|
|
if (sc)
|
|
return sc;
|
|
|
|
m_bLocked = true;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CXMLBinaryLock::ScUnlock
|
|
*
|
|
* PURPOSE: removes the lock
|
|
*
|
|
* PARAMETERS:
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CXMLBinaryLock::ScUnlock()
|
|
{
|
|
DECLARE_SC(sc, TEXT("ScUnlock"));
|
|
|
|
if (!m_bLocked)
|
|
return sc = E_UNEXPECTED;
|
|
|
|
sc = m_rBinary.ScUnlockData();
|
|
if (sc)
|
|
return sc;
|
|
|
|
m_bLocked = false;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: ScGetConsoleFileChecksum
|
|
*
|
|
* PURPOSE: inspects the contents and validates if it looks like valid XML document
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpszPathName - [in] path to the document
|
|
* bool& bXmlBased - [out] true if file is xml based
|
|
* tstring& pstrFileCRC - [out] crc for the file
|
|
*
|
|
* RETURNS:
|
|
* SC - error or validation result (S_OK / S_FALSE)
|
|
*
|
|
\***************************************************************************/
|
|
SC ScGetConsoleFileChecksum(LPCTSTR lpszPathName, tstring& strFileCRC)
|
|
{
|
|
DECLARE_SC(sc, TEXT("ScGetConsoleFileChecksum"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(lpszPathName);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// init out parameters;
|
|
strFileCRC.erase();
|
|
|
|
// open the file
|
|
CAutoWin32Handle shFile( CreateFile(lpszPathName, GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, 0, NULL) );
|
|
|
|
if ( !shFile.IsValid() )
|
|
return sc.FromLastError();
|
|
|
|
// we are sure here the sizeHi is zero. mapping should fail else
|
|
DWORD dwLenHi = 0;
|
|
DWORD dwLen = GetFileSize(shFile, &dwLenHi);
|
|
|
|
if ( dwLenHi != 0 )
|
|
return sc = E_OUTOFMEMORY;
|
|
|
|
// allocate memory for whole file
|
|
CAutoArrayPtr<BYTE> spData( new BYTE[dwLen] );
|
|
if ( spData == NULL )
|
|
return sc = E_OUTOFMEMORY;
|
|
|
|
// read the file into the memory
|
|
DWORD dwRead = 0;
|
|
if ( TRUE != ReadFile( shFile, spData, dwLen, &dwRead, NULL ) )
|
|
return sc.FromLastError();
|
|
|
|
// assert all the data was read
|
|
if ( dwRead != dwLen )
|
|
return sc = E_UNEXPECTED;
|
|
|
|
// calculate the crc
|
|
ULONG init_crc = 0; /*initial crc - do not change this, or you will have different
|
|
checksums calculated - thus existing user data discarded */
|
|
|
|
ULONG crc = crc32( init_crc, spData, dwLen );
|
|
|
|
// convert
|
|
TCHAR buff[20] = {0};
|
|
strFileCRC = _ultot(crc, buff, 10 /*radix*/);
|
|
|
|
// done
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScOpenDocAsStructuredStorage
|
|
*
|
|
* PURPOSE: Opens the file and reads the contents into the memory
|
|
* returns the pointer to memory based IStorage
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpszPathName [in] - file name
|
|
* IStorage **ppStorage [out] - pointer to IStorage
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScOpenDocAsStructuredStorage(LPCTSTR lpszPathName, IStorage **ppStorage)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScOpenDocAsStructuredStorage"));
|
|
|
|
// check out parameter
|
|
sc = ScCheckPointers(ppStorage);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// init out parameter
|
|
*ppStorage = NULL;
|
|
|
|
// check in parameter
|
|
sc = ScCheckPointers(lpszPathName);
|
|
if (sc)
|
|
return sc;
|
|
|
|
CAutoWin32Handle hFile(CreateFile(lpszPathName, GENERIC_READ, FILE_SHARE_READ,
|
|
NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
|
|
if (!hFile.IsValid())
|
|
return sc.FromLastError();
|
|
|
|
// get file data
|
|
ULARGE_INTEGER cbFileSize;
|
|
cbFileSize.LowPart = GetFileSize(hFile, &cbFileSize.HighPart);
|
|
|
|
// will not handle files bigger than 2Gb
|
|
if (cbFileSize.HighPart)
|
|
return E_UNEXPECTED;
|
|
|
|
// alocate memory blob and read the data
|
|
CXMLAutoBinary binData;
|
|
if (cbFileSize.LowPart)
|
|
{
|
|
// allocate
|
|
sc = binData.ScAlloc(cbFileSize.LowPart);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// get pointer to data
|
|
CXMLBinaryLock lock(binData);
|
|
BYTE *pData = NULL;
|
|
sc = lock.ScLock(&pData);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// read file contents
|
|
DWORD dwBytesRead = 0;
|
|
BOOL bOK = ReadFile(hFile, pData, cbFileSize.LowPart, &dwBytesRead, NULL);
|
|
if (!bOK)
|
|
return sc.FromLastError();
|
|
else if (cbFileSize.LowPart != dwBytesRead)
|
|
return sc = E_UNEXPECTED;
|
|
}
|
|
|
|
// create lockbytes
|
|
ILockBytesPtr spLockBytes;
|
|
sc = CreateILockBytesOnHGlobal(binData.GetHandle(), TRUE, &spLockBytes);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// ILockBytes took control over HGLOBAL block, detach from it
|
|
binData.Detach();
|
|
|
|
// set correct size for data
|
|
sc = spLockBytes->SetSize(cbFileSize);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// ask ole to open storage for client
|
|
const DWORD grfMode = STGM_DIRECT | STGM_SHARE_EXCLUSIVE | STGM_READWRITE;
|
|
sc = StgOpenStorageOnILockBytes(spLockBytes, NULL, grfMode, NULL, 0, ppStorage);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// done...
|
|
return sc;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScGetUserDataFolder
|
|
*
|
|
* PURPOSE: Calculates location (dir) for user data folder
|
|
*
|
|
* PARAMETERS:
|
|
* tstring& strUserDataFolder [out] - user data folder path
|
|
* * for instance 'E:\Documents and Settings\John\Application Data\Microsoft\MMC' *
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScGetUserDataFolder(tstring& strUserDataFolder)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScGetUserDataFolder"));
|
|
|
|
// init out parameter
|
|
strUserDataFolder.erase();
|
|
|
|
// get owner for error boxes
|
|
HWND hwndOwner = IsWindowVisible(sc.GetHWnd()) ? sc.GetHWnd() : NULL;
|
|
|
|
// get shell folder
|
|
TCHAR szFolderPath[_MAX_PATH] = {0};
|
|
BOOL bOK = SHGetSpecialFolderPath(hwndOwner, szFolderPath, CSIDL_APPDATA, TRUE/*fCreate*/);
|
|
if ( !bOK )
|
|
return sc = E_FAIL;
|
|
|
|
// return the path;
|
|
strUserDataFolder = szFolderPath;
|
|
strUserDataFolder += _T('\\');
|
|
strUserDataFolder += g_szUserDataSubFolder;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScGetUserDataPath
|
|
*
|
|
* PURPOSE: Calculates location (dir) for user data file by given original console path
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpstrOriginalPath [in] - original console path
|
|
* * for instance 'c:\my consoles\my_tool.msc' *
|
|
* tstring& strUserDataPath [out] - user data file path
|
|
* * for instance 'E:\Documents and Settings\John\Application Data\Microsoft\MMC\my_tool.msc' *
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScGetUserDataPath(LPCTSTR lpstrOriginalPath, tstring& strUserDataPath)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScGetUserDataPath"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(lpstrOriginalPath);
|
|
if ( sc )
|
|
return sc;
|
|
|
|
// init out parameter
|
|
strUserDataPath.erase();
|
|
|
|
// get only the filename from the path
|
|
LPCTSTR lpstrOriginalFileName = _tcsrchr( lpstrOriginalPath, _T('\\') );
|
|
if ( lpstrOriginalFileName == NULL )
|
|
lpstrOriginalFileName = lpstrOriginalPath;
|
|
else
|
|
++lpstrOriginalFileName;
|
|
|
|
// skip whitespaces
|
|
while ( *lpstrOriginalFileName && _istspace(*lpstrOriginalFileName) )
|
|
++lpstrOriginalFileName;
|
|
|
|
// check if the name is non-empty
|
|
if ( !*lpstrOriginalFileName )
|
|
return sc = E_INVALIDARG;
|
|
|
|
// get folder
|
|
sc = ScGetUserDataFolder(strUserDataPath);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// ensure mmc folder exists
|
|
DWORD dwFileAtts = ::GetFileAttributes( strUserDataPath.c_str() );
|
|
if ( 0 == ( dwFileAtts & FILE_ATTRIBUTE_DIRECTORY ) || (DWORD)-1 == dwFileAtts )
|
|
{
|
|
// create the directory
|
|
if ( !CreateDirectory( strUserDataPath.c_str(), NULL ) )
|
|
return sc.FromLastError();
|
|
}
|
|
|
|
// get the length of the file
|
|
int iFileNameLen = _tcslen( lpstrOriginalFileName );
|
|
int iConsoleExtensionLen = _tcslen( g_szDEFAULT_CONSOLE_EXTENSION );
|
|
|
|
// subtract 'msc' extension if such was added
|
|
if ( iFileNameLen > iConsoleExtensionLen )
|
|
{
|
|
if ( 0 == _tcsicmp( g_szDEFAULT_CONSOLE_EXTENSION, lpstrOriginalFileName + iFileNameLen - iConsoleExtensionLen ) )
|
|
{
|
|
iFileNameLen -= (iConsoleExtensionLen - 1); // will add the dot to prevent assumming the different extension
|
|
// so that a.b.msc won't have b extension after msc is removed
|
|
}
|
|
}
|
|
|
|
strUserDataPath += _T('\\');
|
|
strUserDataPath.append( lpstrOriginalFileName, iFileNameLen ); // excludes .msc extension
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CConsoleFilePersistor::GetBinaryCollection
|
|
*
|
|
* PURPOSE: Returns a handle to the collection of Binary elements in the specified
|
|
* document
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLDocument& xmlDocument : [in]: the specified console file document
|
|
* CXMLElementCollection& colBinary : [out]: the collection
|
|
*
|
|
* RETURNS:
|
|
* void
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
void
|
|
CConsoleFilePersistor::GetBinaryCollection(CXMLDocument& xmlDocument, CXMLElementCollection& colBinary)
|
|
{
|
|
// get the root elements of the source and the destination documents
|
|
CPersistor persistorRoot (xmlDocument, CXMLElement(xmlDocument ));
|
|
|
|
// set the navigation to loading
|
|
persistorRoot.SetLoading(true);
|
|
|
|
// navigate to the MMC_ConsoleFile node
|
|
CPersistor persistorConsole (persistorRoot, XML_TAG_MMC_CONSOLE_FILE);
|
|
|
|
// navigate to the binary storage node
|
|
CPersistor persistorBinaryStorage (persistorConsole, XML_TAG_BINARY_STORAGE);
|
|
|
|
// get the collection of binary objects
|
|
persistorBinaryStorage .GetCurrentElement().getChildrenByName(XML_TAG_BINARY, &colBinary);
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CompareStrings
|
|
*
|
|
* PURPOSE: Does a whitespace-insensitive, but case-SENSITIVE comparison
|
|
* of the two strings.
|
|
*
|
|
* PARAMETERS:
|
|
* CComBSTR& bstr1 :
|
|
* CComBSTR & bstr2 :
|
|
*
|
|
* RETURNS:
|
|
* static bool : true if match. else false
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
static bool
|
|
CompareStrings(CComBSTR& bstr1, CComBSTR &bstr2)
|
|
{
|
|
UINT length1 = bstr1.Length();
|
|
UINT length2 = bstr2.Length();
|
|
|
|
// the current indexes
|
|
UINT i1 = 0;
|
|
UINT i2 = 0;
|
|
|
|
bool bEnd1 = false; // is the first string over?
|
|
bool bEnd2 = false; // is the second string over?
|
|
|
|
BSTR sz1 = bstr1;
|
|
BSTR sz2 = bstr2;
|
|
|
|
// either both should be null or neither should be
|
|
if( (NULL == sz1) && (NULL==sz2) )
|
|
return true;
|
|
|
|
if( (NULL == sz1) || (NULL==sz2) )
|
|
return false;
|
|
|
|
// compare the strings
|
|
while( (!bEnd1) || (!bEnd2) )
|
|
{
|
|
WCHAR ch1 = sz1[i1];
|
|
WCHAR ch2 = sz2[i2];
|
|
|
|
// 1. get the next non-whitespace char of the first string
|
|
if (i1 == length1)
|
|
bEnd1 = true;
|
|
else
|
|
{
|
|
if(iswspace(ch1))
|
|
{
|
|
++i1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 2. get the next non-whitespace char of the second string
|
|
if (i2 == length2)
|
|
bEnd2 = true;
|
|
else
|
|
{
|
|
if(iswspace(ch2))
|
|
{
|
|
++i2;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// 3. if either of the strings have ended, break. Taken care of below.
|
|
if(bEnd1 || bEnd2)
|
|
break;
|
|
|
|
// 4. compare the characters (must be a case sensitive comparison)
|
|
if(ch1 != ch2)
|
|
return false;
|
|
|
|
// 5. increment the counters
|
|
++i1;
|
|
++i2;
|
|
}
|
|
|
|
// both strings should have ended together for a match
|
|
if(bEnd1 && bEnd2)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CConsoleFilePersistor::ScCompressUserStateFile
|
|
*
|
|
* PURPOSE: Compresses the user-state console file to avoid redundancies. Most of a
|
|
* console file's size is in the binary elements. These are also usually the
|
|
* least likely to change in user mode. For instance, the console file icons
|
|
* and console task icons cannot be changed in user mode.
|
|
*
|
|
* Therefore, the compression algorithm iterates through all <BINARY> elements
|
|
* in the user state file, and looks for matches in the original console file.
|
|
* If a <BINARY> element has the same contents as a <BINARY> element in the
|
|
* original console file, the contents are replaced by a SourceIndex attribute
|
|
* that gives the index of the matching <BINARY> element in the source.
|
|
* This usually results in a >80% reduction of user state file size.
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR szConsoleFilePath : [IN]: the (authored) console file path
|
|
* CXMLDocument & xmlDocument : [IN/OUT]: The user state document, which is compressed
|
|
*
|
|
* RETURNS:
|
|
* static SC
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
SC
|
|
CConsoleFilePersistor::ScCompressUserStateFile(LPCTSTR szConsoleFilePath, CXMLDocument & xmlDocument)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScCompressUserStateFile"));
|
|
|
|
sc = ScCheckPointers(szConsoleFilePath);
|
|
if(sc)
|
|
return sc;
|
|
|
|
CXMLDocument xmlDocumentOriginal; // the original file
|
|
sc = xmlDocumentOriginal.ScCoCreate( false/*bPutHeader*/ ); // initialize it.
|
|
if(sc)
|
|
return sc;
|
|
|
|
sc = /*CConsoleFilePersistor::*/ScLoadXMLDocumentFromFile(xmlDocumentOriginal, szConsoleFilePath, true /*bSilentOnErrors*/);
|
|
if(sc)
|
|
{
|
|
// ignore the error - this just means that original console is not
|
|
// an XML based - we are not able to compress it - not an error
|
|
sc.Clear();
|
|
return sc;
|
|
}
|
|
|
|
try
|
|
{
|
|
// get the collection of Binary tags
|
|
CXMLElementCollection colBinaryOrignal, colBinary;
|
|
GetBinaryCollection(xmlDocumentOriginal, colBinaryOrignal);
|
|
GetBinaryCollection(xmlDocument, colBinary);
|
|
|
|
long cItemsOriginal = 0;
|
|
long cItems = 0;
|
|
|
|
colBinaryOrignal.get_count(&cItemsOriginal);
|
|
colBinary .get_count(&cItems);
|
|
|
|
// look for matches
|
|
for(int i = 0; i< cItems; i++)
|
|
{
|
|
CXMLElement elemBinary = NULL;
|
|
colBinary.item(i, &elemBinary); // get the i'th Binary element in the dest. file
|
|
CComBSTR bstrBinary;
|
|
elemBinary.get_text(bstrBinary);
|
|
|
|
for(int j = 0; j< cItemsOriginal; j++)
|
|
{
|
|
CXMLElement elemBinaryOriginal = NULL;
|
|
colBinaryOrignal.item(j, &elemBinaryOriginal); // get the j'th Binary element in the dest. file
|
|
CComBSTR bstrBinaryOriginal;
|
|
elemBinaryOriginal.get_text(bstrBinaryOriginal);
|
|
|
|
// compare
|
|
if(CompareStrings(bstrBinaryOriginal, bstrBinary))
|
|
{
|
|
// yahoo!! compress.
|
|
Trace(tagXMLCompression, TEXT("Found match!"));
|
|
|
|
// 1. nuke the contents
|
|
elemBinary.put_text(NULL); // NULL is a valid value for a BSTR
|
|
|
|
CStr strValue;
|
|
strValue.Format(TEXT("%d"), j);
|
|
|
|
// 2. set the contents
|
|
elemBinary.setAttribute(XML_ATTR_SOURCE_INDEX, CComBSTR(strValue));
|
|
|
|
// done.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
|
|
|
|
return sc;
|
|
}
|
|
|
|
/*+-------------------------------------------------------------------------*
|
|
*
|
|
* CConsoleFilePersistor::ScUncompressUserStateFile
|
|
*
|
|
* PURPOSE: Uncompresses user data files that were compressed by ScCompressUserStateFile.
|
|
* Applies the compression algorithm in reverse.
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLDocument & xmlDocumentOriginal :
|
|
* CXMLDocument& xmlDocument :
|
|
*
|
|
* RETURNS:
|
|
* SC
|
|
*
|
|
*+-------------------------------------------------------------------------*/
|
|
SC
|
|
CConsoleFilePersistor::ScUncompressUserStateFile(CXMLDocument &xmlDocumentOriginal, CXMLDocument& xmlDocument)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScUncompressUserStateFile"));
|
|
|
|
try
|
|
{
|
|
// get the collection of Binary tags
|
|
CXMLElementCollection colBinaryOrignal, colBinary;
|
|
GetBinaryCollection(xmlDocumentOriginal, colBinaryOrignal);
|
|
GetBinaryCollection(xmlDocument, colBinary);
|
|
|
|
long cItems = 0;
|
|
|
|
colBinary .get_count(&cItems);
|
|
|
|
// decompress each item in colBinary
|
|
for(int i = 0; i< cItems; i++)
|
|
{
|
|
CXMLElement elemBinary = NULL;
|
|
colBinary.item(i, &elemBinary); // get the i'th Binary element in the dest. file
|
|
|
|
CComBSTR bstrSourceIndex;
|
|
|
|
if(elemBinary.getAttribute(XML_ATTR_SOURCE_INDEX, bstrSourceIndex))
|
|
{
|
|
int j = _wtoi(bstrSourceIndex);
|
|
|
|
CXMLElement elemBinaryOriginal;
|
|
colBinaryOrignal.item(j, &elemBinaryOriginal); // get the j'th Binary element in the dest. file
|
|
CComBSTR bstrBinaryOriginal;
|
|
elemBinaryOriginal.get_text(bstrBinaryOriginal);
|
|
|
|
// replace the destination binary contents (which should be empty) with the original.
|
|
elemBinary.put_text(bstrBinaryOriginal);
|
|
|
|
// don't need to delete the SourceIndex attribute because the xmlDocument is thrown away after reading it in.
|
|
}
|
|
}
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScLoadConsole
|
|
*
|
|
* PURPOSE: Loads the mmc console from file
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpstrConsolePath [in] path, where the console resides.
|
|
* bool& bXmlBased [out] whether document is XML-based
|
|
* CXMLDocument& xmlDocument [out] xmlDocument containing data (only if xml-Based)
|
|
* IStorage **ppStorage [out] storage containing data(only if non xml-Based)
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScLoadConsole(LPCTSTR lpstrConsolePath, bool& bXmlBased,
|
|
CXMLDocument& xmlDocument, IStorage **ppStorage)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScLoadConsole"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers(lpstrConsolePath, ppStorage);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// init out parameters
|
|
bXmlBased = false;
|
|
*ppStorage = NULL;
|
|
|
|
// Create an empty XML document
|
|
CXMLDocument xmlOriginalDoc;
|
|
sc = xmlOriginalDoc.ScCoCreate(false/*bPutHeader*/);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// inspect original console file by trying to load XML document
|
|
bool bOriginalXmlBased = false;
|
|
sc = ScLoadXMLDocumentFromFile(xmlOriginalDoc, lpstrConsolePath, true /*bSilentOnErrors*/);
|
|
if( !sc.IsError() )
|
|
bOriginalXmlBased = true;
|
|
|
|
sc.Clear(); // ignore the error - assume it is not XML based
|
|
|
|
// test it is not a user data which is being opened - cannot be so!
|
|
if ( bOriginalXmlBased )
|
|
{
|
|
try
|
|
{
|
|
// construct persistor
|
|
CPersistor persistor(xmlOriginalDoc, CXMLElement(xmlOriginalDoc));
|
|
persistor.SetLoading(true);
|
|
|
|
// navigate to CRC storage
|
|
CPersistor persistorConsole( persistor, XML_TAG_MMC_CONSOLE_FILE );
|
|
if ( persistorConsole.HasElement(XML_TAG_ORIGINAL_CONSOLE_CRC, NULL) )
|
|
return sc = E_UNEXPECTED;
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
}
|
|
|
|
tstring strFileCRC;
|
|
sc = ScGetConsoleFileChecksum( lpstrConsolePath, strFileCRC );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// store data to be used for saving
|
|
m_strFileCRC = strFileCRC;
|
|
m_bCRCValid = true;
|
|
|
|
// get the path to user data
|
|
tstring strUserDataPath;
|
|
sc = ScGetUserDataPath( lpstrConsolePath, strUserDataPath);
|
|
if (sc)
|
|
{
|
|
// don't fail - trace only - missing user data not a reason to fail loading
|
|
sc.TraceAndClear();
|
|
}
|
|
|
|
// go get the user data
|
|
bool bValidUserData = false;
|
|
sc = ScGetUserData( strUserDataPath, strFileCRC, bValidUserData, xmlDocument );
|
|
if (sc)
|
|
{
|
|
// don't fail - trace only - missing user data not a reason to fail loading
|
|
bValidUserData = false;
|
|
sc.TraceAndClear();
|
|
}
|
|
|
|
// user data loaded?
|
|
if (bValidUserData)
|
|
{
|
|
|
|
// uncompress the user data if the original was XML
|
|
if(bOriginalXmlBased)
|
|
{
|
|
sc = ScUncompressUserStateFile(xmlOriginalDoc, xmlDocument);
|
|
if(sc)
|
|
return sc;
|
|
}
|
|
|
|
// done, just return the staff
|
|
bXmlBased = true; // user data always is XML
|
|
// pxmlDocument is already updated by ScGetUserData
|
|
return sc;
|
|
}
|
|
|
|
// no luck with user data, lets load the original file
|
|
|
|
// XML contents
|
|
if ( bOriginalXmlBased )
|
|
{
|
|
// return the data
|
|
bXmlBased = true;
|
|
xmlDocument = xmlOriginalDoc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
// old, ole-storage based file:
|
|
sc = ScOpenDocAsStructuredStorage( lpstrConsolePath, ppStorage );
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScGetUserData
|
|
*
|
|
* PURPOSE: inspects if user data matches console file, loads the xml document if it does
|
|
*
|
|
* PARAMETERS:
|
|
* tstring& strUserDataConsolePath [in] - path to the user data
|
|
* const tstring& strFileCRC, [in] - crc of original console file
|
|
* bool& bValid [out] - if user data is valid
|
|
* CXMLDocument& xmlDocument [out] - loaded document (only if valid)
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScGetUserData(const tstring& strUserDataConsolePath, const tstring& strFileCRC,
|
|
bool& bValid, CXMLDocument& xmlDocument)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScGetUserData"));
|
|
|
|
// assume invalid initially
|
|
bValid = false;
|
|
|
|
// check if user file exist
|
|
DWORD dwFileAtts = ::GetFileAttributes( strUserDataConsolePath.c_str() );
|
|
|
|
// if file is missing dwFileAtts will be -1, so bValidUserData will be eq. to false
|
|
bool bValidUserData = ( ( dwFileAtts & FILE_ATTRIBUTE_DIRECTORY ) == 0 );
|
|
if ( !bValidUserData )
|
|
return sc;
|
|
|
|
// Create an empty XML document
|
|
CXMLDocument xmlDoc;
|
|
sc = xmlDoc.ScCoCreate( false/*bPutHeader*/ );
|
|
if(sc)
|
|
return sc;
|
|
|
|
// upload the data
|
|
sc = ScLoadXMLDocumentFromFile( xmlDoc, strUserDataConsolePath.c_str() );
|
|
if(sc)
|
|
return sc;
|
|
|
|
// get the CRC
|
|
try
|
|
{
|
|
CPersistor persistor(xmlDoc, CXMLElement(xmlDoc));
|
|
persistor.SetLoading(true);
|
|
|
|
// navigate to CRC storage
|
|
CPersistor persistorConsole( persistor, XML_TAG_MMC_CONSOLE_FILE );
|
|
CPersistor persistorCRC( persistorConsole, XML_TAG_ORIGINAL_CONSOLE_CRC );
|
|
|
|
tstring strCRC;
|
|
persistorCRC.PersistContents(strCRC);
|
|
|
|
// valid if CRC matches
|
|
if ( strCRC == strFileCRC )
|
|
{
|
|
// return the document
|
|
bValid = true;
|
|
|
|
xmlDocument = xmlDoc;
|
|
}
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScSaveConsole
|
|
*
|
|
* PURPOSE: Saves console to file
|
|
*
|
|
* PARAMETERS:
|
|
* LPCTSTR lpstrConsolePath [in] - console file path
|
|
* bool bForAuthorMode [in] - if console was authored
|
|
* const CXMLDocument& xmlDocument [in] - document conatining data to be saved
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScSaveConsole(LPCTSTR lpstrConsolePath, bool bForAuthorMode, const CXMLDocument& xmlDocument)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScSaveConsole"));
|
|
|
|
// parameter check
|
|
sc = ScCheckPointers( lpstrConsolePath );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// sanity check - if saving in user mode, have to be loaded from file.
|
|
// To save in user mode CRC of the original document must be known.
|
|
// It is calculated on loading, but seems like loading was never done.
|
|
if ( !bForAuthorMode && !m_bCRCValid )
|
|
return sc = E_UNEXPECTED;
|
|
|
|
// prepare data for save
|
|
tstring strDestinationFile( lpstrConsolePath );
|
|
CXMLDocument xmlDocumentToSave( xmlDocument );
|
|
|
|
// need to modify slightly if saving just the user data
|
|
if ( !bForAuthorMode )
|
|
{
|
|
// get user data file path
|
|
sc = ScGetUserDataPath( lpstrConsolePath, strDestinationFile );
|
|
if (sc)
|
|
return sc;
|
|
|
|
// optimize the file to be saved, to remove redundancies
|
|
sc = ScCompressUserStateFile(lpstrConsolePath, xmlDocumentToSave);
|
|
if(sc)
|
|
return sc;
|
|
|
|
// add crc to the document
|
|
try
|
|
{
|
|
CPersistor persistor(xmlDocumentToSave, CXMLElement(xmlDocumentToSave));
|
|
persistor.SetLoading(true); // navigate like 'loading'
|
|
|
|
// navigate to CRC storage
|
|
CPersistor persistorConsole( persistor, XML_TAG_MMC_CONSOLE_FILE );
|
|
|
|
// create the crc record
|
|
persistorConsole.SetLoading(false);
|
|
CPersistor persistorCRC( persistorConsole, XML_TAG_ORIGINAL_CONSOLE_CRC );
|
|
|
|
// save data
|
|
persistorCRC.PersistContents( m_strFileCRC );
|
|
}
|
|
catch(SC sc_thrown)
|
|
{
|
|
return sc = sc_thrown;
|
|
}
|
|
}
|
|
|
|
// save document contents
|
|
sc = xmlDocumentToSave.ScSaveToFile( strDestinationFile.c_str() );
|
|
if (sc)
|
|
return sc;
|
|
|
|
return sc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
*
|
|
* METHOD: CConsoleFilePersistor::ScLoadXMLDocumentFromFile
|
|
*
|
|
* PURPOSE: reads CXMLDocument contents from file
|
|
*
|
|
* PARAMETERS:
|
|
* CXMLDocument& xmlDocument [out] document to be receive contents
|
|
* LPCTSTR szFileName [in] source file name
|
|
* bool bSilentOnErrors [in] if true - does not trace opennning errors
|
|
*
|
|
* RETURNS:
|
|
* SC - result code
|
|
*
|
|
\***************************************************************************/
|
|
SC CConsoleFilePersistor::ScLoadXMLDocumentFromFile(CXMLDocument& xmlDocument, LPCTSTR szFileName, bool bSilentOnErrors /*= false*/)
|
|
{
|
|
DECLARE_SC(sc, TEXT("CConsoleFilePersistor::ScLoadXMLDocumentFromFile"));
|
|
|
|
// read data
|
|
CXMLAutoBinary binData;
|
|
sc = ScReadDataFromFile(szFileName, &binData);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// create stream - NOTE it will take care of HGLOBAL if succeeds
|
|
IStreamPtr spStream;
|
|
sc = CreateStreamOnHGlobal(binData.GetHandle(), TRUE, &spStream);
|
|
if (sc)
|
|
return sc;
|
|
|
|
const ULARGE_INTEGER new_size = { binData.GetSize(), 0 };
|
|
binData.Detach(); // not the owner anymore (IStream took ownership)
|
|
|
|
sc = ScCheckPointers(spStream, E_UNEXPECTED);
|
|
if (sc)
|
|
return sc;
|
|
|
|
sc = spStream->SetSize(new_size);
|
|
if (sc)
|
|
return sc;
|
|
|
|
// load data (do not trace by default - it is used to inspect the document as well)
|
|
SC sc_no_trace = xmlDocument.ScLoad(spStream, bSilentOnErrors);
|
|
if(sc_no_trace)
|
|
{
|
|
if ( !bSilentOnErrors )
|
|
sc = sc_no_trace;
|
|
return sc_no_trace;
|
|
}
|
|
|
|
return sc;
|
|
}
|