UTILPROP.CPP

//-------------------------------------------------------------------- 
// Microsoft OLE DB Sample Provider
// (C) Copyright 1994 - 1996 Microsoft Corporation. All Rights Reserved.
//
// @doc
//
// @module UTILPROP.CPP | Properties utility object implementation
//


//
// Notes - there are two main methods in this module:
// - CUtilProps::GetPropertyInfo, a helper function for IDBInfo::GetPropertyInfo
// - CUtilProps::GetProperties, a helper function for IRowsetInfo::GetProperties
//
// Our property implementation is simplified considerably by the fact that we
// only support reading\getting the properties, we do not support
// writing\setting them. This makes sense because we are a simple provider,
// and our rowset object always creates all the interfaces it exposes. In
// other words, there are really no properties the consumer could set.
//
// The implementation is very simple - we keep a global table of the
// properties we support in s_rgprop. We search this table sequentially.
//
// Note that a full-featured provider would probably need to use a more
// sophisticated implementation. We keep the entire GUID for each property in
// our struct, which would become a waste of space if we had a lot more
// properties. Similarly, with large numbers of properties some sort of
// hashing would be better than the sequential search used here.
//

// Includes ------------------------------------------------------------------

#include "headers.h"



// Struct containing the properties we know about. The GUID and string fields are
// initialized in the constructor, because C++ makes it awkward to do so at declaration
// time. So, if you change this table, be sure to make parallel changes in CUtilProp::CUtilProp.
PROPSTRUCT s_rgprop[] =
{
/* 0 */ {DBPROP_IAccessor,FLAGS_ROWSETRO, VT_BOOL, TRUE, 0, NULL},
/* 1 */ {DBPROP_IColumnsInfo,FLAGS_ROWSETRO, VT_BOOL, TRUE, 0, NULL},
/* 2 */ {DBPROP_IRowset,FLAGS_ROWSETRW, VT_BOOL, TRUE, 0, NULL},
/* 3 */ {DBPROP_IRowsetChange,FLAGS_ROWSETRW, VT_BOOL, TRUE, 0, NULL},
/* 4 */ {DBPROP_IRowsetInfo,FLAGS_ROWSETRO, VT_BOOL, TRUE, 0, NULL},
/* 5 */ {DBPROP_DBMSNAME,FLAGS_DATASOURCE,VT_BSTR, FALSE, 0, NULL},
/* 6 */ {DBPROP_DBMSVER,FLAGS_DATASOURCE,VT_BSTR, FALSE, 0, NULL},
/* 7 */ {DBPROP_INIT_DATASOURCE,FLAGS_DBINITRW,VT_BSTR, FALSE, 0, NULL}
};

PROPSTRUCT s_rgDBInitProp[] =
{
/* 0 */ {DBPROP_INIT_DATASOURCE,FLAGS_DBINITRW,VT_BSTR, FALSE, 0, NULL},
};

PROPSTRUCT s_rgDataSourceInfoProp[] =
{
/* 0 */ {DBPROP_DBMSNAME,FLAGS_DATASOURCE,VT_BSTR, FALSE, 0, NULL},
/* 1 */ {DBPROP_DBMSVER,FLAGS_DATASOURCE,VT_BSTR, FALSE, 0, NULL}
};

PROPSTRUCT s_rgRowsetProp[] =
{
/* 0 */ {DBPROP_IAccessor,FLAGS_ROWSETRO, VT_BOOL, TRUE, 0, NULL},
/* 1 */ {DBPROP_IColumnsInfo,FLAGS_ROWSETRW, VT_BOOL, TRUE, 0, NULL},
/* 2 */ {DBPROP_IRowset,FLAGS_ROWSETRW, VT_BOOL, TRUE, 0, NULL},
/* 3 */ {DBPROP_IRowsetChange,FLAGS_ROWSETRW, VT_BOOL, TRUE, 0, NULL},
/* 4 */ {DBPROP_IRowsetInfo,FLAGS_ROWSETRO, VT_BOOL, TRUE, 0, NULL},
};

// Number of supported properties per property set
#defineNUMBER_OF_SUPPORTED_PROPERTY_SETS3

#define NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES1
#defineNUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES2
#defineNUMBER_OF_SUPPORTED_ROWSET_PROPERTIES5

#defineNUMBER_OF_SUPPORTED_PROPERTIES\
(NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES +\
NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES +\
NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES)



// Code ----------------------------------------------------------------------

// CUtilProp::CUtilProp ----------------------------------------------------------
//
// @mfunc Constructor for this class
//
// @rdesc NONE
//
CUtilProp::CUtilProp
(
void
)
{
// initialize the members of our global table that we couldn't initialize
// in the declaration
s_rgprop[0].pwstrDescBuffer,L"IAccessor";
s_rgprop[1].pwstrDescBuffer,L"IColumnsInfo";
s_rgprop[2].pwstrDescBuffer,L"IRowset";
s_rgprop[3].pwstrDescBuffer,L"IRowsetChange";
s_rgprop[4].pwstrDescBuffer,L"IRowsetInfo";

s_rgprop[5].pwstrVal = L"Sampprov";
s_rgprop[5].pwstrDescBuffer,L"DBMS Name";

s_rgprop[6].pwstrVal = L"00.99.0000";
s_rgprop[6].pwstrDescBuffer,L"DBMS Version";

s_rgprop[7].pwstrVal = L"";
s_rgprop[7].pwstrDescBuffer,L"Data Source";

s_rgRowsetProp[0].pwstrDescBuffer,L"IAccessor";
s_rgRowsetProp[1].pwstrDescBuffer,L"IColumnsInfo";
s_rgRowsetProp[2].pwstrDescBuffer,L"IRowset";
s_rgRowsetProp[3].pwstrDescBuffer,L"IRowsetChange";
s_rgRowsetProp[4].pwstrDescBuffer,L"IRowsetInfo";

s_rgDataSourceInfoProp[0].pwstrVal = L"Sampprov";
s_rgDataSourceInfoProp[0].pwstrDescBuffer,L"DBMS Name";

s_rgDataSourceInfoProp[1].pwstrVal = L"00.99.0000";
s_rgDataSourceInfoProp[1].pwstrDescBuffer,L"DBMS Version";

s_rgDBInitProp[0].pwstrVal,L"";
s_rgDBInitProp[0].pwstrDescBuffer,L"Data Source";

return;
}


// CUtilProp::~CUtilProp ---------------------------------------------------------
//
// @mfunc Destructor for this class
//
// @rdesc NONE
//
CUtilProp:: ~CUtilProp
(
void
)
{
if ( s_rgDBInitProp[0].pwstrVal )
g_pIMalloc->Free( s_rgDBInitProp[0].pwstrVal );

return;
}



// CUtilProp::GetPropIndex ----------------------------------------------------
//
// @mfunc Returns index of the given property in our global table of properties
//
// @rdesc BOOL
// @flag TRUE | found match, copied it to pulIndex out-param
// @flag FALSE | no match. In this case, pulIndex has no meaning
//
BOOL CUtilProp::GetPropIndex
(
DBPROPIDdwPropertyID, //@parm IN | PROPID of desired property
ULONG*pulIndex//@parm OUT | index of desired property if return was TRUE
)
{
ULONG cNumberOfProperties;
assert( pulIndex );

for (cNumberOfProperties = 0;
cNumberOfProperties < NUMBER_OF_SUPPORTED_PROPERTIES;
cNumberOfProperties++)
{
if (dwPropertyID == s_rgprop[cNumberOfProperties].dwPropertyID )
{
// found a match
*pulIndex = cNumberOfProperties;
return TRUE;
}
}
// found no matches
return FALSE;
}



// CUtilProp::LoadDBPROPINFO ----------------------------------------------------
//
// @mfunc Helper for GetPropertyInfo. Loads field of DBPROPINFO structure.
//
// @rdesc BOOL
// @flag TRUE | Method succeeded
// @flag FALSE | Method failed (couldn't allocate memory)
//
BOOL CUtilProp::LoadDBPROPINFO
(
PROPSTRUCT*pPropStruct,
DBPROPINFO*pPropInfo
)
{
// asserts
assert( pPropStruct );
assert( pPropInfo );

// init the variant
VariantInit( &pPropInfo->vValues );

// set the easy fields..
pPropInfo->dwPropertyID= pPropStruct->dwPropertyID;
pPropInfo->dwFlags= pPropStruct->dwFlags;
pPropInfo->vtType= pPropStruct->vtType;

switch (pPropStruct->vtType)
{
case VT_BOOL:
V_VT( &pPropInfo->vValues ) = VT_BOOL;
V_BOOL( &pPropInfo->vValues ) = pPropStruct->boolVal;
break;

case VT_I4:
V_VT( &pPropInfo->vValues ) = VT_I4;
V_I4( &pPropInfo->vValues ) = pPropStruct->longVal;
break;

case VT_BSTR:
V_VT( &pPropInfo->vValues ) = VT_BSTR;

V_BSTR( &pPropInfo->vValues ) = SysAllocString( pPropStruct->pwstrVal );

if (NULL == V_BSTR( &pPropInfo->vValues ))
{
VariantClear( &pPropInfo->vValues );
return FALSE;
}
break;

default:
assert( !"LoadDBPROPINFO unknown variant type!\n\r" );
break;
}
// all went well
return TRUE;
}


// CUtilProp::LoadDBPROP ----------------------------------------------------
//
// @mfunc Helper for GetProperties. Loads field of DBPROP structure.
//
// @rdesc BOOL
// @flag TRUE | Method succeeded
// @flag FALSE | Method failed (couldn't allocate memory)
//
BOOL CUtilProp::LoadDBPROP
(
PROPSTRUCT*pPropStruct,
DBPROP*pPropSupport
)
{
// asserts
assert( pPropStruct );
assert( pPropSupport );

// init the variant
VariantInit( &pPropSupport->vValue );

// set the easy fields..
pPropSupport->dwPropertyID = pPropStruct->dwPropertyID;
pPropSupport->colid= DB_NULLID;

// set pPropSupport->vValue based on Variant type
switch (pPropStruct->vtType)
{
case VT_BOOL:
V_VT( &pPropSupport->vValue ) = VT_BOOL;
V_BOOL( &pPropSupport->vValue ) = pPropStruct->boolVal;
break;

case VT_I4:
V_VT( &pPropSupport->vValue ) = VT_I4;
V_I4( &pPropSupport->vValue ) = pPropStruct->longVal;
break;

case VT_BSTR:
V_VT( &pPropSupport->vValue ) = VT_BSTR;

V_BSTR( &pPropSupport->vValue ) = SysAllocString( pPropStruct->pwstrVal );

if (NULL == V_BSTR( &pPropSupport->vValue ))
{
VariantClear( &pPropSupport->vValue );
return FALSE;
}
break;

default:
assert( !"LoadDBPROP unknown variant type!\n\r" );
break;
}
// all went well
return TRUE;
}



// CUtilProp::GetPropertyInfo -----------------------------------------
//
// @mfuncReturns information about rowset and data source properties
//supported by the provider
//
// @rdesc HRESULT
// @flag S_OK | The method succeeded
// @flag E_INVALIDARG | pcPropertyIDSets or prgPropertyInfo was NULL
// @flag E_OUTOFMEMORY | Out of memory
//

STDMETHODIMP CUtilProp::GetPropertyInfo
(
ULONGcPropertyIDSets,//@parm IN | # properties
const DBPROPIDSETrgPropertyIDSets[],//@parm IN | Array of property sets
ULONG*pcPropertyInfoSets,//@parm OUT | # DBPROPSET structures
DBPROPINFOSET**prgPropertyInfoSets,//@parm OUT | DBPROPSET structures property
//| information returned
WCHAR**ppDescBuffer//@parm OUT| Property descriptions
)
{
ULONGcNumberOfPropertySets, cNumberOfProperties;
ULONGcCount;
ULONGcProps = 0;
DBPROPINFO*pPropInfo;
DBPROPINFOSET*pPropInfoSet;
BOOLfRet;

// asserts
assert( s_rgprop );

// check params
if (!pcPropertyInfoSets || !prgPropertyInfoSets)
return ResultFromScode( E_INVALIDARG );

if ((cPropertyIDSets != 0) && !rgPropertyIDSets)
return ResultFromScode( E_INVALIDARG );

// if no restriction array, count our properties
if (cPropertyIDSets == 0)
{
cProps = NUMBER_OF_SUPPORTED_PROPERTY_SETS;
}
// restriction array specified, so get the count
else
{
if (cPropertyIDSets > NUMBER_OF_SUPPORTED_PROPERTY_SETS)
return ( E_INVALIDARG );
else
cProps = cPropertyIDSets;
}

// init out params
*pcPropertyInfoSets= 0;
*prgPropertyInfoSets= NULL;

// use task memory allocater to alloc a DBPROPINFOSET struct
pPropInfoSet = (DBPROPINFOSET*) g_pIMalloc->Alloc
(cProps *
sizeof( DBPROPINFOSET ));
if (!pPropInfoSet)
return ResultFromScode( E_OUTOFMEMORY );

memset( pPropInfoSet, 0, (cProps * sizeof( DBPROPINFOSET )));


// For each supported Property Set
for (cNumberOfPropertySets=0; cNumberOfPropertySets < cProps; cNumberOfPropertySets++)
{

// If no restrictions return all properties from the three supported property sets
if (cPropertyIDSets == 0)
{
if (cNumberOfPropertySets == 0)
pPropInfoSet[cNumberOfPropertySets].guidPropertySet = DBPROPSET_DBINIT;
else if (cNumberOfPropertySets == 1)
pPropInfoSet[cNumberOfPropertySets].guidPropertySet = DBPROPSET_DATASOURCEINFOALL;
else if (cNumberOfPropertySets == 2)
pPropInfoSet[cNumberOfPropertySets].guidPropertySet = DBPROPSET_ROWSET;
}
else
{
pPropInfoSet[cNumberOfPropertySets].guidPropertySet =
rgPropertyIDSets[cNumberOfPropertySets].guidPropertySet;
}

// Check supported property sets
if (IsEqualGUID(pPropInfoSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DBINIT))
{
cNumberOfProperties = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;
}
else if (IsEqualGUID(pPropInfoSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DATASOURCEINFOALL))
{
cNumberOfProperties = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
}
else if (IsEqualGUID(pPropInfoSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_ROWSET))
{
cNumberOfProperties = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
}
else
{
cNumberOfProperties = 0;
}

if (0 != cNumberOfProperties)
{
// use task memory allocater to alloc array of DBPROPINFO structs
pPropInfo = (DBPROPINFO*) g_pIMalloc->Alloc
( cNumberOfProperties *
sizeof( DBPROPINFO ));

if (!pPropInfo)
return ResultFromScode( E_OUTOFMEMORY );

memset( pPropInfo, 0,
(cNumberOfProperties * sizeof( DBPROPINFO )));
}

// for each prop in our table..
for (cCount=0; cCount < cNumberOfProperties; cCount++)
{

// init the Variant right up front
// that way we can VariantClear with no worried (if we need to)
VariantInit( &pPropInfo[cCount].vValues );

// Check supported property sets
if (IsEqualGUID(pPropInfoSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DBINIT))
{
// load up their DBPROPINFO from our table
fRet = LoadDBPROPINFO( &s_rgDBInitProp[cCount], &pPropInfo[cCount] );
}
else if (IsEqualGUID(pPropInfoSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DATASOURCEINFOALL))
{
// load up their DBPROPINFO from our table
fRet = LoadDBPROPINFO( &s_rgDataSourceInfoProp[cCount], &pPropInfo[cCount] );
}
else if (IsEqualGUID(pPropInfoSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_ROWSET))
{
// load up their DBPROPINFO from our table
fRet = LoadDBPROPINFO( &s_rgRowsetProp[cCount], &pPropInfo[cCount] );
}

if (!fRet)
{
ULONG ulFor;

// something went wrong
// clear all variants used so far..
for (ulFor = 0; ulFor < cCount; ulFor++)
{
VariantClear( &pPropInfo[ulFor].vValues );
}
// .. delete the pPropInfo array, return failure
g_pIMalloc->Free( pPropInfo );
return ResultFromScode( E_OUTOFMEMORY );
}
pPropInfoSet[cNumberOfPropertySets].rgPropertyInfos[cCount] =
pPropInfo[cCount];
}// for each property
}// for each property set

// set count of properties and property information
*pcPropertyInfoSets= cProps;
*prgPropertyInfoSets= pPropInfoSet;

return ResultFromScode( S_OK );
}

// CUtilProp::GetProperties ----------------------------------------------------
//
// @mfunc Returns current settings of all properties supported by the DSO/rowset
//
// @rdesc HRESULT
// @flag S_OK | The method succeeded
// @flag E_INVALIDARG | pcProperties or prgPropertyInfo was NULL
// @flag E_OUTOFMEMORY | Out of memory
//
STDMETHODIMP CUtilProp::GetProperties
(
ULONGcPropertyIDSets,//@parm IN | # of restiction property IDs
const DBPROPIDSETrgPropertyIDSets[],//@parm IN | restriction guids
ULONG* pcPropertySets,//@parm OUT | count of properties returned
DBPROPSET**prgPropertySets//@parm OUT | property information returned
)
{
ULONGcNumberOfPropertySets, cNumberOfProperties;
ULONGcCount;
ULONGcProps = 0;
DBPROP*pProp;
DBPROPSET*pPropSet;
BOOLfRet;

// asserts
assert( s_rgprop );

// check params
if (!pcPropertySets || !prgPropertySets)
return ResultFromScode( E_INVALIDARG );

if ((cPropertyIDSets != 0) && !rgPropertyIDSets)
return ResultFromScode( E_INVALIDARG );

// if no restriction array, count our properties
if (cPropertyIDSets == 0)
{
cProps = NUMBER_OF_SUPPORTED_PROPERTY_SETS;
}
// restriction array specified, so get the count
else
{
if (cPropertyIDSets > NUMBER_OF_SUPPORTED_PROPERTY_SETS)
return ( E_INVALIDARG );
else
cProps = cPropertyIDSets;
}

// init out params
*pcPropertySets= 0;
*prgPropertySets= NULL;

// use task memory allocater to alloc a DBPROPINFOSET struct
pPropSet = (DBPROPSET*) g_pIMalloc->Alloc
(cProps *
sizeof( DBPROPSET ));
if (!pPropSet)
return ResultFromScode( E_OUTOFMEMORY );

memset( pPropSet, 0, (cProps * sizeof( DBPROPSET )));


// For each supported Property Set
for (cNumberOfPropertySets=0; cNumberOfPropertySets < cProps; cNumberOfPropertySets++)
{

// If no restrictions return all properties from the two supported property sets
if (cPropertyIDSets == 0)
{
if (cNumberOfPropertySets == 0)
pPropSet[cNumberOfPropertySets].guidPropertySet = DBPROPSET_DATASOURCEINFOALL;
else if (cNumberOfPropertySets == 1)
pPropSet[cNumberOfPropertySets].guidPropertySet = DBPROPSET_ROWSET;
}
else
{
pPropSet[cNumberOfPropertySets].guidPropertySet =
rgPropertyIDSets[cNumberOfPropertySets].guidPropertySet;
}

// Check supported property sets
if (IsEqualGUID(pPropSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DBINIT))
{
cNumberOfProperties = NUMBER_OF_SUPPORTED_DBINIT_PROPERTIES;
}
else if (IsEqualGUID(pPropSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DATASOURCEINFOALL))
{
cNumberOfProperties = NUMBER_OF_SUPPORTED_DATASOURCEINFO_PROPERTIES;
}
else if (IsEqualGUID(pPropSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_ROWSET))
{
cNumberOfProperties = NUMBER_OF_SUPPORTED_ROWSET_PROPERTIES;
}
else
{
cNumberOfProperties = 0;
}

if (0 != cNumberOfProperties)
{
// use task memory allocater to alloc array of DBPROP structs
pProp = (DBPROP*) g_pIMalloc->Alloc
( cNumberOfProperties *
sizeof( DBPROP ));

if (!pProp)
{
g_pIMalloc->Free( pPropSet );
return ResultFromScode( E_OUTOFMEMORY );
}

memset( pProp, 0,
(cNumberOfProperties * sizeof( DBPROP )));
}

// for each prop in our table..
for (cCount=0; cCount < cNumberOfProperties; cCount++)
{

// init the Variant right up front
// that way we can VariantClear with no worried (if we need to)
VariantInit( &pProp[cCount].vValue );

// Check supported property sets
if (IsEqualGUID(pPropSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DBINIT))
{
// load up their DBPROP from our table
fRet = LoadDBPROP( &s_rgDBInitProp[cCount], &pProp[cCount] );
}
else if (IsEqualGUID(pPropSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_DATASOURCEINFOALL))
{
// load up their DBPROP from our table
fRet = LoadDBPROP( &s_rgDataSourceInfoProp[cCount], &pProp[cCount] );
}
else if (IsEqualGUID(pPropSet[cNumberOfPropertySets].guidPropertySet,
DBPROPSET_ROWSET))
{
// load up their DBPROP from our table
fRet = LoadDBPROP( &s_rgRowsetProp[cCount], &pProp[cCount] );
}

if (!fRet)
{
ULONG ulFor;

// something went wrong
// clear all variants used so far..
for (ulFor = 0; ulFor < cCount; ulFor++)
{
VariantClear( &pProp[ulFor].vValue );
}
// .. delete the pProp array, return failure
g_pIMalloc->Free( pProp );
g_pIMalloc->Free( pPropSet );
return ResultFromScode( E_OUTOFMEMORY );
}
}// for each property
pPropSet[cNumberOfPropertySets].rgProperties = pProp;
}// for each property set

// set count of properties and property information
*pcPropertySets= cProps;
*prgPropertySets= pPropSet;

return ResultFromScode( S_OK );
}


// CUtilProp::SetProperties ----------------------------------------------------
//
// @mfunc Set current settings of properties supported by the DSO/rowset
//
// @rdesc HRESULT
// @flag S_OK | The method succeeded
// @flag E_INVALIDARG | pcProperties or prgPropertyInfo was NULL
// @flag E_OUTOFMEMORY | Out of memory
//
STDMETHODIMP CUtilProp::SetProperties
(
ULONGcPropertyIDSets,//@parm IN# of DBPROPSET
DBPROPSETrgPropertyIDSets[]//@parm INOUTArray of property sets
)
{
// Quick Exit with 0 PropertyIDSets
if ( cPropertyIDSets == 0 )
return ResultFromScode( S_OK );

// Check params
if ((0 != cPropertyIDSets) && (NULL == rgPropertyIDSets))
return ResultFromScode( E_INVALIDARG );

if (1 != cPropertyIDSets)
return ResultFromScode( E_FAIL );

if (!IsEqualGUID(rgPropertyIDSets[0].guidPropertySet,DBPROPSET_DBINIT))
return ResultFromScode( E_FAIL );

if (rgPropertyIDSets[0].cProperties == 0)
return ResultFromScode( S_OK );

// If rg.cProperties is not 0 and rg.rgProperties is NULL, this is
// an error.
if (rgPropertyIDSets[0].rgProperties == NULL)
return ResultFromScode(E_INVALIDARG);

switch(rgPropertyIDSets[0].rgProperties->dwPropertyID)
{
case DBPROP_INIT_DATASOURCE:

// get the directory path string and convert it to ANSI
s_rgDBInitProp[0].pwstrVal = (WCHAR *) g_pIMalloc->Alloc(MAX_PATH);

WideCharToMultiByte( CP_ACP, 0, V_BSTR( &rgPropertyIDSets[0].rgProperties->vValue ), -1,
(char *)s_rgDBInitProp[0].pwstrVal, MAX_PATH, NULL, NULL );

return ResultFromScode( S_OK );
}
return ResultFromScode( E_FAIL );
}