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.
2139 lines
69 KiB
2139 lines
69 KiB
/*++
|
|
|
|
|
|
Copyright (c) 1998-1999 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
SaveSchema.cpp
|
|
|
|
Abstract:
|
|
|
|
Implementation of the helper functions that are used to determine
|
|
if a schema compilation is needed, and if needed they invoke the
|
|
appropriate classes to create a schema extensions file
|
|
(MD_SCHEMA_EXTENSION_FILE_NAMEW), that contains the schema extension
|
|
descriptions, and invoke a schema compile to generate the schema bin format.
|
|
|
|
Author:
|
|
|
|
Varsha Jayasimha (varshaj) 30-Nov-1999
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
#include "precomp.hxx"
|
|
|
|
|
|
#define cMaxContainedClass 75
|
|
#define cMaxProperty 250
|
|
|
|
int _cdecl MyCompareStrings(const void *a,
|
|
const void *b)
|
|
{
|
|
return _wcsicmp(*(LPWSTR*)a, *(LPWSTR*)b);
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Helper function used by qsort. Compares to strings, but compares it only
|
|
up until the first comma.
|
|
|
|
|
|
--***************************************************************************/
|
|
int _cdecl MyCompareCommaDelimitedStrings(const void *a,
|
|
const void *b)
|
|
{
|
|
LPWSTR wszStringAStart = ((DELIMITEDSTRING*)a)->pwszStringStart;
|
|
LPWSTR wszStringBStart = ((DELIMITEDSTRING*)b)->pwszStringStart;
|
|
LPWSTR wszStringAEnd = ((DELIMITEDSTRING*)a)->pwszStringEnd;
|
|
LPWSTR wszStringBEnd = ((DELIMITEDSTRING*)b)->pwszStringEnd;
|
|
int iret = 0;
|
|
SIZE_T cchA = wszStringAEnd - wszStringAStart;
|
|
SIZE_T cchB = wszStringBEnd - wszStringBStart;
|
|
|
|
//
|
|
// Do not attempt to null terminate the string because you may be
|
|
// hitting a read-only page.
|
|
//
|
|
|
|
iret = _wcsnicmp(wszStringAStart, wszStringBStart, __min(cchA, cchB));
|
|
|
|
if((0 == iret) &&
|
|
(cchA != cchB)
|
|
)
|
|
{
|
|
iret = cchA < cchB ? -1 : 1;
|
|
}
|
|
|
|
return iret;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Gets the global helper object that has the pointer to the meta tables.
|
|
|
|
Arguments:
|
|
|
|
[in] Bool that indicates whether to fail or not.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT InitializeGlobalISTHelper(BOOL i_bFailIfBinFileAbsent)
|
|
{
|
|
return ::GetGlobalHelper(i_bFailIfBinFileAbsent,
|
|
&g_pGlobalISTHelper);
|
|
}
|
|
|
|
void ReleaseGlobalISTHelper()
|
|
{
|
|
if(NULL != g_pGlobalISTHelper)
|
|
{
|
|
delete g_pGlobalISTHelper;
|
|
g_pGlobalISTHelper = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function saves the schema only if something has changed in the schema
|
|
since the last save.
|
|
|
|
Arguments:
|
|
|
|
[in] Schema file name.
|
|
[in] Security attributes for the file.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT SaveSchemaIfNeeded(LPCWSTR i_wszSchemaFileName,
|
|
PSECURITY_ATTRIBUTES i_pSecurityAtrributes)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if(NULL == g_pGlobalISTHelper)
|
|
{
|
|
//
|
|
// g_pGlobalISTHelper will not be initialized if
|
|
// ReadAllDataFromXML is not called. This can happen
|
|
// during an upgrade scneario i.e IIS5.0/5.1 to IIS6.0
|
|
// We attempt to initialize it here. Note that we do
|
|
// not fail if bin file is absent - just go with
|
|
// the shipped schema.
|
|
//
|
|
|
|
BOOL bFailIfBinFileAbsent = FALSE;
|
|
|
|
hr = InitializeGlobalISTHelper(bFailIfBinFileAbsent);
|
|
|
|
}
|
|
|
|
if(g_dwSchemaChangeNumber != g_dwLastSchemaChangeNumber)
|
|
{
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchemaIfNeeded] Calling SaveSchema. Last save schema change number: %d. Current schema change number: %d.\n",
|
|
g_dwLastSchemaChangeNumber,
|
|
g_dwSchemaChangeNumber));
|
|
|
|
hr = SaveSchema(i_wszSchemaFileName,
|
|
i_pSecurityAtrributes);
|
|
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
g_dwLastSchemaChangeNumber = g_dwSchemaChangeNumber;
|
|
|
|
//
|
|
// SaveSchema will reinitialize the GlobalISTHelper if the schema has changed.
|
|
//
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchemaIfNeeded] Done Saving schema. Updating last save schema change number to: %d. Current schema change number: %d.\n",
|
|
g_dwLastSchemaChangeNumber,
|
|
g_dwSchemaChangeNumber));
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchemaIfNeeded] No need saving schema because schema has not changed since last save. Last save schema change number: %d. Current schema change number: %d.\n",
|
|
g_dwLastSchemaChangeNumber,
|
|
g_dwSchemaChangeNumber));
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function saves the schema and compiles schema information into the
|
|
bin file.
|
|
|
|
Arguments:
|
|
|
|
[in] Schema file name.
|
|
[in] Security attributes for the file.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT SaveSchema(LPCWSTR i_wszSchemaFileName,
|
|
PSECURITY_ATTRIBUTES i_pSecurityAtrributes)
|
|
{
|
|
HRESULT hr;
|
|
CMDBaseObject* pObjSchema = NULL;
|
|
CMDBaseObject* pObjProperties = NULL;
|
|
CWriter* pCWriter = NULL;
|
|
CMBSchemaWriter* pSchemaWriter = NULL;
|
|
LPWSTR wszSchema = L"Schema";
|
|
LPWSTR wszProperties = L"Properties";
|
|
ISimpleTableDispenser2* pISTDisp = NULL;
|
|
IMetabaseSchemaCompiler* pCompiler = NULL;
|
|
|
|
//
|
|
// Get a pointer to the compiler to get the bin file name.
|
|
//
|
|
|
|
hr = DllGetSimpleObjectByIDEx( eSERVERWIRINGMETA_TableDispenser, IID_ISimpleTableDispenser2, (VOID**)&pISTDisp, WSZ_PRODUCT_IIS );
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] DllGetSimpleObjectByIDEx failed with hr = 0x%x.\n",hr));
|
|
goto exit;
|
|
}
|
|
|
|
hr = pISTDisp->QueryInterface(IID_IMetabaseSchemaCompiler,
|
|
(LPVOID*)&pCompiler);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] QueryInterface on compiler failed with hr = 0x%x.\n",hr));
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the Properties object
|
|
//
|
|
|
|
pObjSchema = g_pboMasterRoot->GetChildObject((LPSTR&)wszSchema,
|
|
&hr,
|
|
TRUE);
|
|
|
|
if(FAILED(hr) || (NULL == pObjSchema))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to open /Schema. GetChildObject failed with hr = 0x%x.\n",hr));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
pObjProperties = pObjSchema->GetChildObject((LPSTR&)wszProperties,
|
|
&hr,
|
|
TRUE);
|
|
|
|
if(FAILED(hr) || (NULL == pObjProperties))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to open /Schema/Properties. GetChildObject failed with hr = 0x%x.\n",hr));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create the writer object
|
|
//
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Initializing writer with write file: %s bin file: %s.\n",
|
|
g_wszSchemaExtensionFile,
|
|
g_pGlobalISTHelper->m_wszBinFileForMeta));
|
|
|
|
//
|
|
// Assert the g_GlobalISTHelper are valid
|
|
//
|
|
|
|
MD_ASSERT(g_pGlobalISTHelper != NULL);
|
|
|
|
pCWriter = new CWriter();
|
|
if(NULL == pCWriter)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Bug #512868 If someone had attrib-ed an existing extenstion file
|
|
// be read-only then we could land up in a state where we will not be
|
|
// able to recreate the extensions file. Hence attrib the extensions
|
|
// file to be read-write if it exists. An extensions file can be
|
|
// around only if a previous compile has failed.
|
|
//
|
|
|
|
ResetFileAttributesIfNeeded((LPTSTR)g_wszSchemaExtensionFile,
|
|
TRUE);
|
|
|
|
hr = pCWriter->Initialize(g_wszSchemaExtensionFile,
|
|
g_pGlobalISTHelper,
|
|
NULL);
|
|
}
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving schema tree. Cannot initialize writer. Failed with hr = 0x%x.\n", hr));
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// First create the IIsConfigObject collection
|
|
//
|
|
|
|
hr = CreateIISConfigObjectCollection(pObjProperties,
|
|
pCWriter,
|
|
&pSchemaWriter);
|
|
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Create all other collections
|
|
//
|
|
|
|
hr = CreateNonIISConfigObjectCollections(pObjSchema,
|
|
pCWriter,
|
|
&pSchemaWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
if(pSchemaWriter)
|
|
{
|
|
//
|
|
// If pSchemaWriter has a valid Value, then some extensions were found - write it.
|
|
//
|
|
|
|
hr = pCWriter->BeginWrite(eWriter_Schema,
|
|
i_pSecurityAtrributes);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving schema tree. CWriter::BeginWrite failed with hr = 0x%x.\n", hr));
|
|
goto exit;
|
|
}
|
|
|
|
hr = pSchemaWriter->WriteSchema();
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving schema tree. CMBSchemaWriter::WriteSchema. Failed with hr = 0x%x.\n", hr));
|
|
goto exit;
|
|
|
|
}
|
|
|
|
hr = pCWriter->EndWrite(eWriter_Schema);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving schema tree. CWriter::EndWrite Failed with hr = 0x%x.\n", hr));
|
|
goto exit;
|
|
}
|
|
|
|
//
|
|
// Trigger compilation
|
|
//
|
|
|
|
//
|
|
// Must close the file prior to calling compile schema, else will get a sharing violation.
|
|
//
|
|
|
|
delete pCWriter;
|
|
pCWriter = NULL;
|
|
|
|
hr = CompileIntoBin(pCompiler,
|
|
g_wszSchemaExtensionFile,
|
|
i_wszSchemaFileName);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] CompileSchema from %s failed with hr = 0x%x.\n",
|
|
g_wszSchemaExtensionFile, hr));
|
|
}
|
|
else
|
|
{
|
|
if(!DeleteFileW(g_wszSchemaExtensionFile))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[CompileIfNeeded] Compile from schema extensions file: %s succeeded, but cannot cleanup the extensions file:%s. Delete file failed with hr = 0x%x.\n",
|
|
g_wszSchemaExtensionFile,
|
|
g_wszSchemaExtensionFile,
|
|
hr));
|
|
|
|
hr = S_OK;
|
|
}
|
|
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] No extensions found. - Either schema tree was changed, but no extensions added, or all extensions were deleted.\n"));
|
|
}
|
|
|
|
//
|
|
// If you reach here it means that:
|
|
// A. Schema changes occured to the in-memory /Schema tree, but either
|
|
// there were no extensions or all extensions were deleted.
|
|
// (This is inferred when pSchemaWriter in NULL)
|
|
// or
|
|
// B. Schema compile failed.
|
|
// For A: Compile from shipped schema.
|
|
// For B: Check for an existing schema file. If found make sure that the
|
|
// bin file is valid. If bin file is not valid try compiling from the
|
|
// schema file. If schema file is not found, compile from shipped schema.
|
|
//
|
|
|
|
if(pSchemaWriter)
|
|
{
|
|
//
|
|
// This is case B.
|
|
//
|
|
|
|
if(-1 != GetFileAttributesW(i_wszSchemaFileName))
|
|
{
|
|
if(NULL == g_pGlobalISTHelper)
|
|
{
|
|
BOOL bFailIfBinFileAbsent = TRUE;
|
|
|
|
hr = InitializeGlobalISTHelper(bFailIfBinFileAbsent);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
else
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to get the the bin file name. (Assuming file missing or invalid). InitializeGlobalISTHelper failed with hr = 0x%x.\n",hr));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Schema file present and valid - goto exit
|
|
// As long as we have a valid g_pGlobalISTHelper, it holds on
|
|
// to a reference to the bin file and the bin file cannot be
|
|
// invalidated.
|
|
//
|
|
|
|
goto exit;
|
|
}
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Compiling from schema file %s\n", i_wszSchemaFileName));
|
|
|
|
|
|
hr = CompileIntoBinFromSchemaFile(pCompiler,
|
|
i_wszSchemaFileName);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If you reach here it is either case A or the last part of case B.
|
|
// Recreate from shipped schema
|
|
//
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Schema file not found. Compiling from shipped schema\n"));
|
|
|
|
hr = CompileIntoBin(pCompiler,
|
|
NULL,
|
|
i_wszSchemaFileName);
|
|
|
|
exit:
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(NULL == g_pGlobalISTHelper)
|
|
{
|
|
BOOL bFailIfBinFileAbsent = TRUE;
|
|
|
|
hr = InitializeGlobalISTHelper(bFailIfBinFileAbsent);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to get the the bin file name. (Assuming file is invalid). InitializeGlobalISTHelper failed with hr = 0x%x.\n",hr));
|
|
}
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = UpdateTimeStamp((LPWSTR)i_wszSchemaFileName,
|
|
g_pGlobalISTHelper->m_wszBinFileForMeta);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[CompileIfNeeded] UpdateTimeStamp failed with hr = 0x%x.\n",hr));
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(NULL != pSchemaWriter)
|
|
{
|
|
delete pSchemaWriter;
|
|
}
|
|
|
|
if(NULL != pCWriter)
|
|
{
|
|
delete pCWriter;
|
|
}
|
|
|
|
if(NULL != pCompiler)
|
|
{
|
|
pCompiler->Release();
|
|
}
|
|
|
|
if(NULL != pISTDisp)
|
|
{
|
|
pISTDisp->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function creates the non-IIsConfigObject collection *extensions* to
|
|
the schema.
|
|
|
|
Arguments:
|
|
|
|
[in] Object that contains the schema tree.
|
|
[in] The writter object.
|
|
[in,out] The schema object - this gets created if it is not already created
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT CreateNonIISConfigObjectCollections(CMDBaseObject* i_pObjSchema,
|
|
CWriter* i_pCWriter,
|
|
CMBSchemaWriter** io_pSchemaWriter)
|
|
{
|
|
CMDBaseObject* pObjClasses = NULL;
|
|
CMDBaseObject* pObjClass = NULL;
|
|
DWORD dwEnumClassIndex = 0;
|
|
static LPCWSTR wszSeparator = L",";
|
|
LPWSTR wszClasses = L"Classes";
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Open the Classes key
|
|
//
|
|
|
|
pObjClasses = i_pObjSchema->GetChildObject((LPSTR&)wszClasses,
|
|
&hr,
|
|
TRUE);
|
|
|
|
if(FAILED(hr) || (NULL == pObjClasses))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to open /Schema/Classes. GetChildObject failed with hr = 0x%x.\n",hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
for(dwEnumClassIndex=0,
|
|
pObjClass=pObjClasses->EnumChildObject(dwEnumClassIndex++);
|
|
(SUCCEEDED(hr)) && (pObjClass!=NULL);
|
|
pObjClass=pObjClasses->EnumChildObject(dwEnumClassIndex++))
|
|
{
|
|
//
|
|
// Save all the properties for this class in temp variables
|
|
//
|
|
|
|
LPCWSTR wszOptProp = NULL;
|
|
LPCWSTR wszMandProp = NULL;
|
|
LPCWSTR wszContainedClassList = NULL;
|
|
BOOL bContainer = FALSE;
|
|
BOOL* pbContainer = &bContainer;
|
|
LPCWSTR wszClassName = (LPCWSTR)pObjClass->GetName(TRUE);
|
|
CMBCollectionWriter* pCollectionWriter = NULL;
|
|
CMDBaseData* pObjData = NULL;
|
|
DWORD dwEnumIndex = 0;
|
|
|
|
for(dwEnumIndex=0,
|
|
pObjData=pObjClass->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA);
|
|
(SUCCEEDED(hr)) && (pObjData!=NULL);
|
|
pObjData=pObjClass->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA))
|
|
{
|
|
DWORD dwID = pObjData->GetIdentifier();
|
|
|
|
if(MD_SCHEMA_CLASS_OPT_PROPERTIES == dwID)
|
|
{
|
|
wszOptProp = (LPCWSTR)pObjData->GetData(TRUE);
|
|
}
|
|
else if(MD_SCHEMA_CLASS_MAND_PROPERTIES == dwID)
|
|
{
|
|
wszMandProp = (LPCWSTR)pObjData->GetData(TRUE);
|
|
}
|
|
else if(dwID == MD_SCHEMA_CLASS_CONTAINER)
|
|
{
|
|
pbContainer = (BOOL*)pObjData->GetData(TRUE);
|
|
}
|
|
else if(dwID == MD_SCHEMA_CLASS_CONTAINMENT)
|
|
{
|
|
wszContainedClassList = (LPCWSTR)pObjData->GetData(TRUE);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Get collection writer for IIsConfigObject
|
|
//
|
|
|
|
// DBGINFOW((DBG_CONTEXT,
|
|
// L"[CreateNonIISConfigObjectCollections] Class %s Mand Prop:%s. Opt Prop: %s\n",
|
|
// wszClassName,
|
|
// wszMandProp,
|
|
// wszOptProp));
|
|
|
|
// Assert that pbContainer is non-null.
|
|
MD_ASSERT(pbContainer != NULL);
|
|
|
|
if(ClassDiffersFromShippedSchema(wszClassName,
|
|
*pbContainer,
|
|
(LPWSTR)wszContainedClassList) ||
|
|
ClassPropertiesDifferFromShippedSchema(wszClassName,
|
|
(LPWSTR)wszOptProp,
|
|
(LPWSTR)wszMandProp)
|
|
)
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Saving collection: %s.\n",wszClassName));
|
|
|
|
hr = GetCollectionWriter(i_pCWriter,
|
|
io_pSchemaWriter,
|
|
&pCollectionWriter,
|
|
wszClassName,
|
|
*pbContainer,
|
|
wszContainedClassList);
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = ParseAndAddPropertiesToNonIISConfigObjectCollection(wszOptProp,
|
|
FALSE,
|
|
pCollectionWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving classes tree. Could not add optional properties %s for class %s failed with hr = 0x%x.\n",wszOptProp, wszClassName, hr));
|
|
return hr;
|
|
}
|
|
|
|
hr = ParseAndAddPropertiesToNonIISConfigObjectCollection(wszMandProp,
|
|
TRUE,
|
|
pCollectionWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving classes tree. Could not add manditory properties %s for class %s failed with hr = 0x%x.\n",wszMandProp, wszClassName, hr));
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
It parses the list of properties and adds it to a Non_IIsConfigObject
|
|
collection.
|
|
|
|
Arguments:
|
|
|
|
[in] List of properties
|
|
[in] Bool that indicates manditory or optional.
|
|
[in,out] The collection object - this gets created if it is not already created
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT
|
|
ParseAndAddPropertiesToNonIISConfigObjectCollection(LPCWSTR i_wszProperties,
|
|
BOOL i_bManditory,
|
|
CMBCollectionWriter* i_pCollectionWriter)
|
|
{
|
|
CMBPropertyWriter *pProperty = NULL;
|
|
HRESULT hr = S_OK;
|
|
static WCHAR wchSeparator = L',';
|
|
WCHAR* pwszStartProperty = NULL;
|
|
WCHAR* pwszEndProperty = NULL;
|
|
|
|
if(NULL == i_wszProperties || 0 == *i_wszProperties)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
pwszStartProperty = (LPWSTR)i_wszProperties;
|
|
|
|
for ( ; ; )
|
|
{
|
|
pwszEndProperty = wcschr(pwszStartProperty, wchSeparator);
|
|
|
|
if(0 != *pwszStartProperty)
|
|
{
|
|
hr = i_pCollectionWriter->GetMBPropertyWriter(pwszStartProperty,
|
|
i_bManditory,
|
|
&pProperty);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
if(NULL != pwszEndProperty)
|
|
{
|
|
pwszStartProperty = ++pwszEndProperty;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the schema and determines if a property is in the shipped
|
|
schema or not.
|
|
|
|
Arguments:
|
|
|
|
[in] Writer object.
|
|
[in] property id.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL PropertyNotInShippedSchema(CWriter* i_pCWriter,
|
|
DWORD i_dwIdentifier)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
ULONG aColSearch[] = {iCOLUMNMETA_Table,
|
|
iCOLUMNMETA_ID
|
|
};
|
|
ULONG cColSearch = sizeof(aColSearch)/sizeof(ULONG);
|
|
LPVOID apvSearch[cCOLUMNMETA_NumberOfColumns];
|
|
apvSearch[iCOLUMNMETA_Table] = (LPVOID)i_pCWriter->m_pCWriterGlobalHelper->m_wszTABLE_IIsConfigObject;
|
|
apvSearch[iCOLUMNMETA_ID] = (LPVOID)&i_dwIdentifier;
|
|
BOOL bPropertyNotInShippedSchema = TRUE;
|
|
ULONG iRow = 0;
|
|
ULONG iStartRow = 0;
|
|
ULONG iCol = iCOLUMNMETA_SchemaGeneratorFlags;
|
|
DWORD* pdwMetaFlagsEx = NULL;
|
|
|
|
|
|
hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTColumnMetaByTableAndID->GetRowIndexBySearch(iStartRow,
|
|
cColSearch,
|
|
aColSearch,
|
|
NULL,
|
|
apvSearch,
|
|
&iRow);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
|
|
hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTColumnMetaByTableAndID->GetColumnValues(iRow,
|
|
1,
|
|
&iCol,
|
|
NULL,
|
|
(LPVOID*)&pdwMetaFlagsEx);
|
|
|
|
if(SUCCEEDED(hr) && (((*pdwMetaFlagsEx)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0))
|
|
{
|
|
bPropertyNotInShippedSchema = FALSE;
|
|
}
|
|
}
|
|
|
|
if(E_ST_NOMOREROWS == hr)
|
|
{
|
|
//
|
|
// See if property is a shipped tag.
|
|
//
|
|
|
|
bPropertyNotInShippedSchema = TagNotInShippedSchema(i_pCWriter,
|
|
i_dwIdentifier);
|
|
|
|
|
|
}
|
|
else if(FAILED(hr))
|
|
{
|
|
//
|
|
// Trace a message saying internal catalog error
|
|
//
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[PropertyNotInShippedSchema] Internal catalog error. Could not determine if property was not in shipped schema. Assuming it is not. hr = 0x%x.\n", hr));
|
|
|
|
}
|
|
|
|
return bPropertyNotInShippedSchema;
|
|
|
|
} // PropertyNotInShippedSchema
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the schema and determines if a tag is in the shipped
|
|
schema or not.
|
|
|
|
Arguments:
|
|
|
|
[in] Writer object.
|
|
[in] property id.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL TagNotInShippedSchema(CWriter* i_pCWriter,
|
|
DWORD i_dwIdentifier)
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
ULONG aColSearch[] = {iTAGMETA_Table,
|
|
iTAGMETA_ID
|
|
};
|
|
ULONG cColSearch = sizeof(aColSearch)/sizeof(ULONG);
|
|
LPVOID apvSearch[cCOLUMNMETA_NumberOfColumns];
|
|
BOOL bTagNotInShippedSchema = TRUE;
|
|
ULONG iStartRow = 0;
|
|
ULONG iColIndex = iTAGMETA_ColumnIndex;
|
|
DWORD* pdwColIndex = NULL;
|
|
|
|
apvSearch[iTAGMETA_Table] = (LPVOID)g_pGlobalISTHelper->m_wszTABLE_IIsConfigObject;
|
|
apvSearch[iTAGMETA_ID] = (LPVOID)&i_dwIdentifier;
|
|
|
|
|
|
|
|
hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTTagMetaByTableAndID->GetRowIndexBySearch(iStartRow,
|
|
cColSearch,
|
|
aColSearch,
|
|
NULL,
|
|
apvSearch,
|
|
&iStartRow);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
|
|
hr = i_pCWriter->m_pCWriterGlobalHelper->m_pISTTagMetaByTableAndID->GetColumnValues(iStartRow,
|
|
1,
|
|
&iColIndex,
|
|
NULL,
|
|
(LPVOID*)&pdwColIndex);
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// Lookup the property to see if it is shipped.
|
|
//
|
|
|
|
LPVOID a_Identity[] = {(LPVOID)g_pGlobalISTHelper->m_wszTABLE_IIsConfigObject,
|
|
(LPVOID)pdwColIndex
|
|
};
|
|
ULONG iRow=0;
|
|
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetRowIndexByIdentity(NULL,
|
|
a_Identity,
|
|
&iRow);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
DWORD* pdwExtended = NULL;
|
|
ULONG iColPropertyMetaFlagsEx = iCOLUMNMETA_SchemaGeneratorFlags;
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetColumnValues(iRow,
|
|
1,
|
|
&iColPropertyMetaFlagsEx,
|
|
NULL,
|
|
(LPVOID*)&pdwExtended);
|
|
|
|
if(SUCCEEDED(hr) && (((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0))
|
|
{
|
|
//
|
|
// Found at least one property that is not in the shipped schema
|
|
//
|
|
bTagNotInShippedSchema = FALSE;
|
|
}
|
|
|
|
//
|
|
// Else condition means it failed or it was a shipped property
|
|
// if(FAILED(hr) || ( (((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) != 0))
|
|
//
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // If GetRowIndexBySearch succeeds
|
|
|
|
return bTagNotInShippedSchema;
|
|
|
|
} // TagNotInShippedSchema
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the schema and determines if a class has any extensions or if it
|
|
an extended (ie new) class.
|
|
|
|
Arguments:
|
|
|
|
[in] Class name
|
|
[in] Container class or not.
|
|
[in] Container class list.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL ClassDiffersFromShippedSchema(LPCWSTR i_wszClassName,
|
|
BOOL i_bIsContainer,
|
|
LPWSTR i_wszContainedClassList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL bClassDiffersFromShippedSchema = TRUE;
|
|
ULONG aiCol [] = {iTABLEMETA_SchemaGeneratorFlags,
|
|
iTABLEMETA_ContainerClassList
|
|
};
|
|
ULONG cCol = sizeof(aiCol)/sizeof(ULONG);
|
|
LPVOID apv[cTABLEMETA_NumberOfColumns];
|
|
ULONG iRow = 0;
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTTableMetaForMetabaseTables->GetRowIndexByIdentity(NULL,
|
|
(LPVOID*)&i_wszClassName,
|
|
&iRow);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTTableMetaForMetabaseTables->GetColumnValues(iRow,
|
|
cCol,
|
|
aiCol,
|
|
NULL,
|
|
apv);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
if(((*(DWORD*)apv[iTABLEMETA_SchemaGeneratorFlags]) & (fTABLEMETA_EXTENDED|fTABLEMETA_USERDEFINED)) == 0)
|
|
{
|
|
if(MatchClass(i_bIsContainer,
|
|
i_wszContainedClassList,
|
|
apv)
|
|
)
|
|
{
|
|
bClassDiffersFromShippedSchema = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Trace a message saying internal catalog error
|
|
//
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[ClassDiffersFromShippedSchema] Internal catalog error. Could not determine if class was not in shipped schema. Assuming it is not. hr = 0x%x.\n", hr));
|
|
|
|
}
|
|
}
|
|
else if(E_ST_NOMOREROWS != hr)
|
|
{
|
|
//
|
|
// Trace a message saying internal catalog error
|
|
//
|
|
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[ClassDiffersFromShippedSchema] Internal catalog error. Could not determine if class was not in shipped schema. Assuming it is not. hr = 0x%x.\n", hr));
|
|
}
|
|
|
|
return bClassDiffersFromShippedSchema;
|
|
|
|
} // ClassDiffersFromShippedSchema
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the schema and matches a class.
|
|
|
|
Arguments:
|
|
|
|
[in] Container class or not.
|
|
[in] Container class list.
|
|
[in] Class attributes.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL MatchClass(BOOL i_bIsContainer,
|
|
LPWSTR i_wszContainedClassList,
|
|
LPVOID* i_apv)
|
|
{
|
|
BOOL bMatch = TRUE;
|
|
DWORD fIsContained = (*(DWORD*)i_apv[iTABLEMETA_SchemaGeneratorFlags]) & fTABLEMETA_CONTAINERCLASS;
|
|
|
|
//
|
|
// Compare the container property 1st and only if they equal compare the container class list
|
|
//
|
|
|
|
if( i_bIsContainer &&
|
|
(fIsContained != fTABLEMETA_CONTAINERCLASS)
|
|
)
|
|
{
|
|
bMatch = FALSE;
|
|
}
|
|
else if (!i_bIsContainer &&
|
|
(fIsContained == fTABLEMETA_CONTAINERCLASS)
|
|
)
|
|
{
|
|
bMatch = FALSE;
|
|
}
|
|
else
|
|
{
|
|
bMatch = MatchCommaDelimitedStrings(i_wszContainedClassList,
|
|
(LPWSTR)i_apv[iTABLEMETA_ContainerClassList]);
|
|
}
|
|
|
|
return bMatch;
|
|
|
|
} // MatchClass
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Checks to see if two comma delimited strings match.
|
|
|
|
Arguments:
|
|
|
|
[in] Comma delimited string.
|
|
[in] Comma delimited string.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL MatchCommaDelimitedStrings(LPWSTR i_wszString1,
|
|
LPWSTR i_wszString2)
|
|
{
|
|
BOOL bMatch = FALSE;
|
|
|
|
DELIMITEDSTRING aStringFixed1[cMaxContainedClass];
|
|
DELIMITEDSTRING aStringFixed2[cMaxContainedClass];
|
|
DELIMITEDSTRING* aString1 = aStringFixed1;
|
|
DELIMITEDSTRING* aString2 = aStringFixed2;
|
|
ULONG cString1 = cMaxContainedClass;
|
|
ULONG cString2 = cMaxContainedClass;
|
|
ULONG iString1 = 0;
|
|
ULONG iString2 = 0;
|
|
BOOL bReAlloced1 = FALSE;
|
|
BOOL bReAlloced2 = FALSE;
|
|
HRESULT hr = S_OK;
|
|
|
|
if(NULL == i_wszString1 || 0 == *i_wszString1)
|
|
{
|
|
if(NULL == i_wszString2 || 0 == *i_wszString2)
|
|
{
|
|
bMatch = TRUE;
|
|
}
|
|
}
|
|
else if(NULL == i_wszString2 || 0 == *i_wszString2)
|
|
{
|
|
bMatch = FALSE; // Means i_wszString1 != NULL and i_wszString2 == NULL
|
|
}
|
|
else if(wcscmp(i_wszString1, i_wszString2) == 0)
|
|
{
|
|
bMatch = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Construct an array of individual strings
|
|
// and compare the array
|
|
//
|
|
|
|
hr = CommaDelimitedStringToArray(i_wszString1,
|
|
&aString1,
|
|
&iString1,
|
|
&cString1,
|
|
&bReAlloced1);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
hr = CommaDelimitedStringToArray(i_wszString2,
|
|
&aString2,
|
|
&iString2,
|
|
&cString2,
|
|
&bReAlloced2);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
bMatch = MatchDelimitedStringArray(aString1,
|
|
iString1,
|
|
aString2,
|
|
iString2);
|
|
|
|
}
|
|
|
|
exit:
|
|
|
|
if(aString1 != aStringFixed1)
|
|
{
|
|
delete [] aString1;
|
|
}
|
|
|
|
if(aString2 != aStringFixed2)
|
|
{
|
|
delete [] aString2;
|
|
}
|
|
|
|
return bMatch;
|
|
|
|
} // MatchCommaDelimitedStrings
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Converts a comma delimited string to an array.
|
|
|
|
Arguments:
|
|
|
|
[in] Comma delimited string.
|
|
[in,out] Array.
|
|
[in,out] Current index in the array.
|
|
[in,out] Max count of the array. (i.e. max it can hold)
|
|
[in,out] Bool which indecates if the array has been realloced.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT CommaDelimitedStringToArray(LPWSTR i_wszString,
|
|
DELIMITEDSTRING** io_apDelimitedString,
|
|
ULONG* io_piDelimitedString,
|
|
ULONG* io_pcMaxDelimitedString,
|
|
BOOL* io_pbReAlloced)
|
|
{
|
|
LPWSTR wszSubStringStart = NULL;
|
|
LPWSTR wszSubStringEnd = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
wszSubStringStart = i_wszString;
|
|
|
|
while(NULL != wszSubStringStart)
|
|
{
|
|
DELIMITEDSTRING DelimitedString;
|
|
|
|
wszSubStringEnd = wcschr(wszSubStringStart, L',');
|
|
|
|
DelimitedString.pwszStringStart = wszSubStringStart;
|
|
|
|
if(NULL != wszSubStringEnd)
|
|
{
|
|
DelimitedString.pwszStringEnd = wszSubStringEnd;
|
|
}
|
|
else
|
|
{
|
|
// Point to the terminating NULL.
|
|
|
|
DelimitedString.pwszStringEnd = wszSubStringStart + wcslen(wszSubStringStart);
|
|
}
|
|
|
|
hr = AddDelimitedStringToArray(&DelimitedString,
|
|
io_piDelimitedString,
|
|
io_pcMaxDelimitedString,
|
|
io_pbReAlloced,
|
|
io_apDelimitedString);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
if(wszSubStringEnd != NULL)
|
|
{
|
|
wszSubStringStart = ++wszSubStringEnd;
|
|
}
|
|
else
|
|
{
|
|
wszSubStringStart = wszSubStringEnd;
|
|
}
|
|
}
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Adds the string to the array.
|
|
|
|
Arguments:
|
|
|
|
[in] String to add.
|
|
[in,out] Current index in the array.
|
|
[in,out] Max count of the array. (i.e. max it can hold)
|
|
[in,out] Bool which indecates if the array has been realloced.
|
|
[in,out] Array.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT AddDelimitedStringToArray(DELIMITEDSTRING* i_pDelimitedString,
|
|
ULONG* io_piDelimitedString,
|
|
ULONG* io_pcMaxDelimitedString,
|
|
BOOL* io_pbReAlloced,
|
|
DELIMITEDSTRING** io_apDelimitedString)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if(*io_piDelimitedString >= *io_pcMaxDelimitedString)
|
|
{
|
|
hr = ReAllocate(*io_piDelimitedString,
|
|
*io_pbReAlloced,
|
|
io_apDelimitedString,
|
|
io_pcMaxDelimitedString);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
*io_pbReAlloced = TRUE;
|
|
}
|
|
|
|
(*io_apDelimitedString)[(*io_piDelimitedString)++] = (*i_pDelimitedString);
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
HRESULT ReAllocate(ULONG i_iDelimitedString,
|
|
BOOL i_bReAlloced,
|
|
DELIMITEDSTRING** io_apDelimitedString,
|
|
ULONG* io_pcDelimitedString)
|
|
{
|
|
DELIMITEDSTRING* pSav = NULL;
|
|
|
|
pSav = new DELIMITEDSTRING[*io_pcDelimitedString + cMaxContainedClass];
|
|
if(NULL == pSav)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
*io_pcDelimitedString = *io_pcDelimitedString + cMaxContainedClass;
|
|
memset(pSav, 0, sizeof(DELIMITEDSTRING)*(*io_pcDelimitedString));
|
|
|
|
if(NULL != *io_apDelimitedString)
|
|
{
|
|
memcpy(pSav, *io_apDelimitedString, sizeof(DELIMITEDSTRING)*i_iDelimitedString);
|
|
if(i_bReAlloced)
|
|
{
|
|
delete [] *io_apDelimitedString;
|
|
*io_apDelimitedString = NULL;
|
|
}
|
|
}
|
|
|
|
*io_apDelimitedString = pSav;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Compares two string arrays.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL MatchDelimitedStringArray(DELIMITEDSTRING* i_aString1,
|
|
ULONG i_cString1,
|
|
DELIMITEDSTRING* i_aString2,
|
|
ULONG i_cString2)
|
|
{
|
|
DBG_ASSERT((i_cString1 > 0) && (i_cString2 >0));
|
|
|
|
if(i_cString1 != i_cString2)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
qsort((void*)i_aString1, i_cString1, sizeof(DELIMITEDSTRING), MyCompareCommaDelimitedStrings);
|
|
qsort((void*)i_aString2, i_cString2, sizeof(DELIMITEDSTRING), MyCompareCommaDelimitedStrings);
|
|
|
|
for(ULONG i=0; i<i_cString1; i++)
|
|
{
|
|
if(0 != MyCompareCommaDelimitedStrings(&(i_aString1[i]),
|
|
&(i_aString2[i]))
|
|
)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Looks at the optinal and manditory properties of a class and determines
|
|
if ti differs fromt the shipped schema.
|
|
|
|
Arguments:
|
|
|
|
[in] Class name.
|
|
[in] Optional properties.
|
|
[in] Manditory properties.
|
|
[in] Writer object.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
BOOL ClassPropertiesDifferFromShippedSchema(LPCWSTR i_wszClassName,
|
|
LPWSTR i_wszOptProperties,
|
|
LPWSTR i_wszMandProperties)
|
|
{
|
|
|
|
BOOL bClassPropertiesDifferFromShippedSchema = FALSE;
|
|
ULONG i = 0;
|
|
HRESULT hr = S_OK;
|
|
DELIMITEDSTRING aPropertyFixed[cMaxProperty];
|
|
ULONG cProperty = cMaxProperty;
|
|
ULONG iProperty = 0;
|
|
DELIMITEDSTRING* aProperty = aPropertyFixed;
|
|
BOOL bReAlloced = FALSE;
|
|
|
|
if( ((NULL == i_wszOptProperties) || (0 == *i_wszOptProperties)) &&
|
|
((NULL == i_wszMandProperties) || (0 == *i_wszMandProperties))
|
|
)
|
|
{
|
|
//
|
|
// It is true that you cannot delete all shipped properties from a shipped class,
|
|
// because every shipped class that we know of has at least one location property.
|
|
// But there may be previously added extension that were deleted, so assume something
|
|
// changed, when there are no properties.
|
|
//
|
|
bClassPropertiesDifferFromShippedSchema = TRUE;
|
|
goto exit;
|
|
}
|
|
|
|
|
|
//
|
|
// Now create an array of
|
|
// mand + opt properties
|
|
//
|
|
|
|
if((NULL != i_wszOptProperties) && (0 != *i_wszOptProperties))
|
|
{
|
|
|
|
hr = CommaDelimitedStringToArray(i_wszOptProperties,
|
|
&aProperty,
|
|
&iProperty,
|
|
&cProperty,
|
|
&bReAlloced);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if((NULL != i_wszMandProperties) && (0 != *i_wszMandProperties))
|
|
{
|
|
hr = CommaDelimitedStringToArray(i_wszMandProperties,
|
|
&aProperty,
|
|
&iProperty,
|
|
&cProperty,
|
|
&bReAlloced);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
for(i=0; i<iProperty; i++ )
|
|
{
|
|
LPWSTR wszPropertyName = aProperty[i].pwszStringStart;
|
|
LPWSTR wszEnd = aProperty[i].pwszStringEnd;
|
|
WCHAR wchEndSav;
|
|
ULONG aColSearchProperty[] = {iCOLUMNMETA_Table,
|
|
iCOLUMNMETA_InternalName
|
|
};
|
|
ULONG cColSearchProperty = sizeof(aColSearchProperty)/sizeof(ULONG);
|
|
LPVOID apvSearchProperty[cCOLUMNMETA_NumberOfColumns];
|
|
ULONG iStartRowProperty = 0;
|
|
ULONG iColPropertyMetaFlagsEx = iCOLUMNMETA_SchemaGeneratorFlags;
|
|
DWORD* pdwExtended = NULL;
|
|
|
|
//
|
|
// Null terminate the property name and initialize it.
|
|
// Hence on gotos until you reset it.
|
|
//
|
|
|
|
wchEndSav = *wszEnd;
|
|
*wszEnd = L'\0';
|
|
|
|
apvSearchProperty[iCOLUMNMETA_Table] = (LPVOID)i_wszClassName;
|
|
apvSearchProperty[iCOLUMNMETA_InternalName] = (LPVOID)wszPropertyName;
|
|
|
|
|
|
//
|
|
// See if the property is found in the class and see if it is shipped
|
|
//
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTColumnMetaByTableAndName->GetRowIndexBySearch(iStartRowProperty,
|
|
cColSearchProperty,
|
|
aColSearchProperty,
|
|
NULL,
|
|
apvSearchProperty,
|
|
&iStartRowProperty);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = g_pGlobalISTHelper->m_pISTColumnMetaByTableAndName->GetColumnValues(iStartRowProperty,
|
|
1,
|
|
&iColPropertyMetaFlagsEx,
|
|
NULL,
|
|
(LPVOID*)&pdwExtended);
|
|
|
|
if(FAILED(hr) || ( ((*pdwExtended) & (fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) != 0))
|
|
{
|
|
//
|
|
// Found at least one property that is not in the shipped schema
|
|
//
|
|
|
|
bClassPropertiesDifferFromShippedSchema = TRUE;
|
|
}
|
|
|
|
//
|
|
// Else condition means it succeeded and it was a shipped property
|
|
// if(SUCCEEDED(hr) && ( ((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0))
|
|
//
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// May be its a tag value. Check if it is in the tag meta
|
|
//
|
|
|
|
ULONG aColSearchTag[] = {iTAGMETA_Table,
|
|
iTAGMETA_InternalName
|
|
};
|
|
ULONG cColSearchTag = sizeof(aColSearchTag)/sizeof(ULONG);
|
|
LPVOID apvSearchTag[cTAGMETA_NumberOfColumns];
|
|
ULONG iStartRowTag = 0;
|
|
ULONG iColIndex = iTAGMETA_ColumnIndex;
|
|
DWORD* pdwColumnIndex = NULL;
|
|
|
|
apvSearchTag[iTAGMETA_Table] = (LPVOID)i_wszClassName;
|
|
apvSearchTag[iTAGMETA_InternalName] = (LPVOID)wszPropertyName;
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTTagMetaByTableAndName->GetRowIndexBySearch(iStartRowTag,
|
|
cColSearchTag,
|
|
aColSearchTag,
|
|
NULL,
|
|
apvSearchTag,
|
|
&iStartRowTag);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
bClassPropertiesDifferFromShippedSchema = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Check if the parent property of this tag is shipped, if it is not,
|
|
// then this becomes a non-shipped tag
|
|
//
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTTagMetaByTableAndName->GetColumnValues(iStartRowTag,
|
|
1,
|
|
&iColIndex,
|
|
NULL,
|
|
(LPVOID*)&pdwColumnIndex);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
LPVOID a_Identity[] = {(LPVOID)i_wszClassName,
|
|
(LPVOID)pdwColumnIndex
|
|
};
|
|
ULONG iRow = 0;
|
|
|
|
hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetRowIndexByIdentity(NULL,
|
|
a_Identity,
|
|
&iRow);
|
|
|
|
if(SUCCEEDED(hr))
|
|
{
|
|
hr = g_pGlobalISTHelper->m_pISTColumnMeta->GetColumnValues(iRow,
|
|
1,
|
|
&iColPropertyMetaFlagsEx,
|
|
NULL,
|
|
(LPVOID*)&pdwExtended);
|
|
|
|
if(FAILED(hr) || ( ((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) != 0))
|
|
{
|
|
//
|
|
// Found at least one property that is not in the shipped schema
|
|
//
|
|
bClassPropertiesDifferFromShippedSchema = TRUE;
|
|
}
|
|
|
|
//
|
|
// Else condition means it succeeded and it was a shipped property
|
|
// if(SUCCEEDED(hr) && ( ((*pdwExtended)&(fCOLUMNMETA_EXTENDED|fCOLUMNMETA_USERDEFINED)) == 0))
|
|
//
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bClassPropertiesDifferFromShippedSchema = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Restore the property name
|
|
//
|
|
|
|
*wszEnd = wchEndSav;
|
|
|
|
if(FAILED(hr) || bClassPropertiesDifferFromShippedSchema)
|
|
{
|
|
goto exit;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
exit:
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
bClassPropertiesDifferFromShippedSchema = TRUE;
|
|
}
|
|
|
|
if(aProperty != aPropertyFixed)
|
|
{
|
|
delete [] aProperty;
|
|
}
|
|
|
|
return bClassPropertiesDifferFromShippedSchema;
|
|
|
|
} // ClassPropertiesDifferFromShippedSchema
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Creates the IIsConfigObject collection - This collection has complete
|
|
definitions of all properties.
|
|
|
|
Arguments:
|
|
|
|
[in] Properties object.
|
|
[in,out] Writer object.
|
|
[in,out] Schema Writer object.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT CreateIISConfigObjectCollection(CMDBaseObject* i_pObjProperties,
|
|
CWriter* i_pCWriter,
|
|
CMBSchemaWriter** io_pSchemaWriter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMBCollectionWriter* pCollectionWriter = NULL;
|
|
|
|
hr = SaveNames(i_pObjProperties,
|
|
i_pCWriter,
|
|
io_pSchemaWriter,
|
|
&pCollectionWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
|
|
hr = SaveTypes(i_pObjProperties,
|
|
i_pCWriter,
|
|
io_pSchemaWriter,
|
|
&pCollectionWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = SaveDefaults(i_pObjProperties,
|
|
i_pCWriter,
|
|
io_pSchemaWriter,
|
|
&pCollectionWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Saves the extended roperty name
|
|
|
|
Arguments:
|
|
|
|
[in] Metabase properties object.
|
|
[in,out] Writer object.
|
|
[in,out] Schema Writer object.
|
|
[in,out] Collection Writer object.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT SaveNames(CMDBaseObject* i_pObjProperties,
|
|
CWriter* i_pCWriter,
|
|
CMBSchemaWriter** io_pSchemaWriter,
|
|
CMBCollectionWriter** io_pCollectionWriter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMDBaseObject* pObjNames = NULL;
|
|
CMDBaseData* pObjData = NULL;
|
|
DWORD dwEnumIndex = 0;
|
|
LPWSTR wszNames = L"Names";
|
|
|
|
//
|
|
// Get the names object
|
|
//
|
|
|
|
pObjNames = i_pObjProperties->GetChildObject((LPSTR&)wszNames,
|
|
&hr,
|
|
TRUE);
|
|
|
|
if(FAILED(hr) || (NULL == pObjNames))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to open /Schema/Properties/Names. GetChildObject failed with hr = 0x%x.\n",hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Populate the Column meta array by enumerating the names key.
|
|
//
|
|
|
|
for(dwEnumIndex=0,
|
|
pObjData=pObjNames->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA);
|
|
(SUCCEEDED(hr)) && (pObjData!=NULL);
|
|
pObjData=pObjNames->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA))
|
|
{
|
|
CMBPropertyWriter *pProperty = NULL;
|
|
|
|
if(pObjData->GetDataType() != STRING_METADATA)
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Encountered non-string data in the names tree of the schema. Ignoring entry for this ID: %d \n",pObjData->GetIdentifier()));
|
|
continue;
|
|
}
|
|
|
|
if(PropertyNotInShippedSchema(i_pCWriter,
|
|
pObjData->GetIdentifier())
|
|
)
|
|
{
|
|
if(NULL == *io_pCollectionWriter)
|
|
{
|
|
hr = GetCollectionWriter(i_pCWriter,
|
|
io_pSchemaWriter,
|
|
io_pCollectionWriter,
|
|
wszTABLE_IIsConfigObject,
|
|
FALSE,
|
|
NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
|
|
hr = (*io_pCollectionWriter)->GetMBPropertyWriter(pObjData->GetIdentifier(),
|
|
&pProperty);
|
|
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving names tree. GetPropertyWriter for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
pProperty->AddNameToProperty((LPCWSTR)(pObjData->GetData(TRUE)));
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Must call create index else it will keep adding duplicate property entries.
|
|
//
|
|
|
|
return hr;
|
|
|
|
} // SaveNames
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Saves the extended roperty type
|
|
|
|
Arguments:
|
|
|
|
[in] Metabase properties object.
|
|
[in,out] Writer object.
|
|
[in,out] Schema Writer object.
|
|
[in,out] Collection Writer object.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT SaveTypes(CMDBaseObject* i_pObjProperties,
|
|
CWriter* i_pCWriter,
|
|
CMBSchemaWriter** io_pSchemaWriter,
|
|
CMBCollectionWriter** io_pCollectionWriter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMDBaseObject* pObjTypes = NULL;
|
|
CMDBaseData* pObjData = NULL;
|
|
DWORD dwEnumIndex = 0;
|
|
LPWSTR wszTypes = L"Types";
|
|
|
|
//
|
|
// Get the Types object
|
|
//
|
|
|
|
pObjTypes = i_pObjProperties->GetChildObject((LPSTR&)wszTypes,
|
|
&hr,
|
|
TRUE);
|
|
|
|
if(FAILED(hr) || (NULL == pObjTypes))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to open /Schema/Properties/Types. GetChildObject failed with hr = 0x%x.\n",hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
for(dwEnumIndex=0,
|
|
pObjData=pObjTypes->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA);
|
|
(SUCCEEDED(hr)) && (pObjData!=NULL);
|
|
pObjData=pObjTypes->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA))
|
|
{
|
|
CMBPropertyWriter *pProperty = NULL;
|
|
|
|
if(pObjData->GetDataType() != BINARY_METADATA ||
|
|
pObjData->GetDataLen(TRUE) != sizeof(PropValue))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Encountered non-binary data in the type tree of the schema.\nIgnoring type entry for this ID: %d.\nType: %d.(Expected %d)\nLength: %d(Expected %d).\n",
|
|
pObjData->GetIdentifier(),
|
|
pObjData->GetDataType(),
|
|
BINARY_METADATA,
|
|
pObjData->GetDataLen(TRUE),
|
|
sizeof(PropValue)));
|
|
if(pObjData->GetDataType() == STRING_METADATA )
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"Data: %s.\n",
|
|
pObjData->GetData(TRUE)
|
|
));
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if(PropertyNotInShippedSchema(i_pCWriter,
|
|
pObjData->GetIdentifier())
|
|
)
|
|
{
|
|
|
|
if(NULL == *io_pCollectionWriter)
|
|
{
|
|
hr = GetCollectionWriter(i_pCWriter,
|
|
io_pSchemaWriter,
|
|
io_pCollectionWriter,
|
|
wszTABLE_IIsConfigObject,
|
|
FALSE,
|
|
NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
hr = (*io_pCollectionWriter)->GetMBPropertyWriter(pObjData->GetIdentifier(),
|
|
&pProperty);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving types tree. GetPropertyWriter for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = pProperty->AddTypeToProperty((PropValue*)(pObjData->GetData(TRUE)));
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving types tree. AddTypeToProperty for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr));
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Saves the extended roperty default
|
|
|
|
Arguments:
|
|
|
|
[in] Metabase properties object.
|
|
[in,out] Writer object.
|
|
[in,out] Schema Writer object.
|
|
[in,out] Collection Writer object.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT SaveDefaults(CMDBaseObject* i_pObjProperties,
|
|
CWriter* i_pCWriter,
|
|
CMBSchemaWriter** io_pSchemaWriter,
|
|
CMBCollectionWriter** io_pCollectionWriter)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CMDBaseObject* pObjDefaults = NULL;
|
|
CMDBaseData* pObjData = NULL;
|
|
DWORD dwEnumIndex = 0;
|
|
LPWSTR wszDefaults = L"Defaults";
|
|
|
|
//
|
|
// Get the Defaults object
|
|
//
|
|
|
|
pObjDefaults = i_pObjProperties->GetChildObject((LPSTR&)wszDefaults,
|
|
&hr,
|
|
TRUE);
|
|
|
|
if(FAILED(hr) || (NULL == pObjDefaults))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Unable to open /Schema/Properties/Defaults. GetChildObject failed with hr = 0x%x.\n",hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
for(dwEnumIndex=0,
|
|
pObjData=pObjDefaults->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA);
|
|
(SUCCEEDED(hr)) && (pObjData!=NULL);
|
|
pObjData=pObjDefaults->EnumDataObject(dwEnumIndex++,
|
|
0,
|
|
ALL_METADATA,
|
|
ALL_METADATA))
|
|
{
|
|
CMBPropertyWriter *pProperty = NULL;
|
|
|
|
if(PropertyNotInShippedSchema(i_pCWriter,
|
|
pObjData->GetIdentifier())
|
|
)
|
|
{
|
|
if(NULL == *io_pCollectionWriter)
|
|
{
|
|
hr = GetCollectionWriter(i_pCWriter,
|
|
io_pSchemaWriter,
|
|
io_pCollectionWriter,
|
|
wszTABLE_IIsConfigObject,
|
|
FALSE,
|
|
NULL);
|
|
if(FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
hr = (*io_pCollectionWriter)->GetMBPropertyWriter(pObjData->GetIdentifier(),
|
|
&pProperty);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving defaults tree. GetPropertyWriter for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = pProperty->AddDefaultToProperty((BYTE*)(pObjData->GetData(TRUE)),
|
|
pObjData->GetDataLen(TRUE));
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving types tree. AddDefaultToProperty for ID:%d failed with hr = 0x%x.\n",pObjData->GetIdentifier(), hr));
|
|
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Saves the extended roperty name
|
|
|
|
Arguments:
|
|
|
|
[in,out] Writer object.
|
|
[in,out] Schema Writer object.
|
|
[in,out] Collection Writer object.
|
|
[in] Collection name
|
|
[in] Bool that indicates container
|
|
[in] Container class list
|
|
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--***************************************************************************/
|
|
HRESULT GetCollectionWriter(CWriter* i_pCWriter,
|
|
CMBSchemaWriter** io_pSchemaWriter,
|
|
CMBCollectionWriter** io_pCollectionWriter,
|
|
LPCWSTR i_wszCollectionName,
|
|
BOOL i_bContainer,
|
|
LPCWSTR i_wszContainerClassList)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if(NULL != *io_pCollectionWriter)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// Get the schema writer if it has not been created
|
|
//
|
|
|
|
if(NULL == *io_pSchemaWriter)
|
|
{
|
|
hr = i_pCWriter->GetMetabaseSchemaWriter(io_pSchemaWriter);
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] Error while saving schema tree. Unable to get schema writer failed with hr = 0x%x.\n", hr));
|
|
return hr;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Get collection writer for the collection
|
|
//
|
|
|
|
hr = (*io_pSchemaWriter)->GetCollectionWriter(i_wszCollectionName,
|
|
i_bContainer,
|
|
i_wszContainerClassList,
|
|
io_pCollectionWriter);
|
|
if(FAILED(hr))
|
|
{
|
|
DBGINFOW((DBG_CONTEXT,
|
|
L"[SaveSchema] GetCollectionWriter for %s failed with hr = 0x%x.\n",
|
|
i_wszCollectionName, hr));
|
|
|
|
return hr;
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|