windows-nt/Source/XPSP1/NT/inetsrv/query/ntciutil/oleprop.cxx
2020-09-26 16:20:57 +08:00

1208 lines
36 KiB
C++

//+------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1995-2000.
//
// File: oleprop.cxx
//
// Contents: OLE Property Manager.
//
// Classes: COLEPropManager
//
// History: 13-Dec-95 dlee Created from KyleP's FCB Manager
//
//-------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <shlobj.h>
#include <nserror.h>
#include <propstm.hxx>
#include <oleprop.hxx>
#include <propvar.h>
#include <pmalloc.hxx>
//+---------------------------------------------------------------------------
//
// Function: FunnyToNormalPath
//
// Synopsis: Gets an interface over a file using the shell
//
// Arguments: [pwcPath] -- Path of the file to be opened
// [riid] -- Interface being requested
// [ppv] -- Where the interface is returned
//
// Returns: SCODE result of the open
//
// History: 1-Feb-01 dlee Created
//
//----------------------------------------------------------------------------
void FunnyToNormalPath( const CFunnyPath & funnyPath,
WCHAR * awcPath )
{
WCHAR const * pwcPath = funnyPath.GetPath();
#if CIDBG == 1
unsigned cwc = wcslen( pwcPath );
Win4Assert( cwc <= MAX_PATH );
#endif // CIDBG
// The shell doesn't understand the \\?\ syntax
// Local: \\?\c:\foo
// Remote: \\?\UNC\server\share
if ( !wcsncmp( pwcPath, L"\\\\?\\", 4 ) )
{
pwcPath += 4;
if ( !wcsncmp( pwcPath, L"UNC\\", 4 ) )
pwcPath += 2;
}
wcscpy( awcPath, pwcPath );
if ( awcPath[1] == L'\\' )
awcPath[0] = L'\\';
} //FunnyToNormalPath
//+---------------------------------------------------------------------------
//
// Function: ShellBindToItemByName
//
// Synopsis: Gets an interface over a file using the shell
//
// Arguments: [pwcPath] -- Path of the file to be opened
// [riid] -- Interface being requested
// [ppv] -- Where the interface is returned
//
// Returns: SCODE result of the open
//
// History: 1-Feb-01 dlee Created
//
//----------------------------------------------------------------------------
SCODE ShellBindToItemByName(
WCHAR const * pwcPath,
REFIID riid,
void ** ppv )
{
Win4Assert( wcsncmp( pwcPath, L"\\\\?\\", 4 ) );
XInterface<IShellFolder> xDesktop;
SCODE sc = SHGetDesktopFolder( xDesktop.GetPPointer() );
if ( SUCCEEDED( sc ) )
{
XInterface<IBindCtx> xBindCtx;
sc = CreateBindCtx( 0, xBindCtx.GetPPointer() );
if ( FAILED( sc ) )
return sc;
BIND_OPTS bo = {sizeof(bo), 0};
bo.grfFlags = BIND_JUSTTESTEXISTENCE; // skip all junctions
sc = xBindCtx->SetBindOptions( &bo );
if ( FAILED( sc ) )
return sc;
LPITEMIDLIST pidl;
// cast needed for bad interface def
sc = xDesktop->ParseDisplayName( 0,
xBindCtx.GetPointer(),
(LPWSTR) pwcPath,
0,
&pidl,
0 );
if ( SUCCEEDED( sc ) )
{
XInterface<IShellFolder> xSF;
LPCITEMIDLIST pidlChild;
// Note: apparently pidlChild isn't leaked here
sc = SHBindToParent( pidl,
IID_IShellFolder,
xSF.GetQIPointer(),
&pidlChild );
if (SUCCEEDED(sc))
sc = xSF->BindToObject( pidlChild, 0, riid, ppv );
CoTaskMemFree( pidl );
}
}
return sc;
} //ShellBindToItemByName
//+---------------------------------------------------------------------------
//
// Member: COLEPropManager::Open, public
//
// Synopsis: Open object corresponding to a path.
//
// Arguments: [pwcPath] - path of the file to be opened
//
// Returns: TRUE if failed due to sharing violation, FALSE otherwise
//
// History: 13-Dec-95 dlee Created
//
//----------------------------------------------------------------------------
BOOL COLEPropManager::Open( const CFunnyPath & funnyPath )
{
Win4Assert( 0 == _ppsstg );
// if we've tried to open this file before, don't try again.
if ( _fStgOpenAttempted )
return _fSharingViolation;
_fStgOpenAttempted = TRUE;
// NTRAID#DB-NTBUG9-84131-2000/07/31-dlee Indexing Service contains workarounds for StgOpenStorage AV on > MAX_PATH paths
if ( funnyPath.GetLength() >= MAX_PATH )
{
ciDebugOut(( DEB_WARN, "Not calling StgOpenStorage in COLEPropManager::Open for paths > MAX_PATH: \n(%ws)\n",
funnyPath.GetPath() ));
return FALSE;
}
//
// Make sure the file isn't offline.
//
ULONG ulAttributes = GetFileAttributes( funnyPath.GetPath() );
SCODE sc;
if ( 0 == (ulAttributes & FILE_ATTRIBUTE_OFFLINE) ) // Note: attrib is also *on* in the error return
{
//
// Get an IPropertSetStorage.
//
#if 0
sc = StgOpenStorageEx( funnyPath.GetPath(), // Path
STGM_DIRECT |
STGM_READ |
STGM_SHARE_DENY_WRITE, // Flags (BChapman said use these)
STGFMT_ANY, // Format
0, // Reserved
0, // Reserved
0, // Reserved
IID_IPropertySetStorage, // IID
(void **)&_ppsstg );
#else
//
// Note: Because of the above MAX_PATH check, we can copy into a fixed-length buffer
// We need to hold onto the path because the shell open function expects the path to
// remain valid for the life of the IPropertySetStorage we get back.
//
FunnyToNormalPath( funnyPath, _awcPath );
// Use the shell's open to get additional properties from .mp3 files and others
sc = ShellBindToItemByName( _awcPath,
IID_IPropertySetStorage,
(void **) &_ppsstg );
#endif
}
else
sc = HRESULT_FROM_WIN32(ERROR_FILE_OFFLINE);
if ( FAILED( sc ) )
{
vqDebugOut(( DEB_IWARN, "StgOpenStorage %ws returned 0x%x\n",
funnyPath.GetPath(), sc ));
// For these nonfatal errors, fail silently.
// Assuming STG_E_INVALIDNAME means the file didn't exist, but
// even if it means the string is malformed it's ok to ignore.
if ( STG_E_LOCKVIOLATION == sc ||
STG_E_SHAREVIOLATION == sc )
{
_fSharingViolation = TRUE;
return TRUE;
}
if ( STG_E_INVALIDFUNCTION == sc || // Common StgOpenStorageEx error (FAT volumes)
STG_E_FILEALREADYEXISTS == sc || // Common StgOpenStorage error (FAT volumes)
STG_E_ACCESSDENIED == sc ||
STG_E_OLDFORMAT == sc ||
STG_E_OLDDLL == sc ||
STG_E_PATHNOTFOUND == sc ||
STG_E_FILENOTFOUND == sc ||
STG_E_INVALIDHEADER == sc ||
STG_E_INVALIDNAME == sc ||
HRESULT_FROM_WIN32(ERROR_FILE_OFFLINE) == sc )
return FALSE;
// these errors would mean a CI coding bug
Win4Assert( STG_E_INVALIDPOINTER != sc );
Win4Assert( STG_E_INVALIDFLAG != sc );
// Almost any error code can be returned from StgOpenStorage.
// The doc has little relation to the set of return codes.
// This assert is just here so we know when we hit it whether
// to put the new error in the ignore list (above) or the throw
// list (here).
Win4Assert( E_UNEXPECTED == sc ||
E_OUTOFMEMORY == sc ||
ERROR_NO_SYSTEM_RESOURCES ||
STG_E_TOOMANYOPENFILES == sc ||
STG_E_INSUFFICIENTMEMORY == sc ||
( HRESULT_FROM_WIN32( ERROR_NOT_READY ) == sc ) );
THROW( CException( sc ) );
}
return FALSE;
} //Open
//+---------------------------------------------------------------------------
//
// Member: COLEPropManager::PropSetToOrdinal, public
//
// Synopsis: Find property set for specified GUID.
//
// Arguments: [guidPS] -- GUID of property set.
//
// Returns: Ordinal of property set.
//
// History: 13-Dec-95 dlee Created
//
//----------------------------------------------------------------------------
unsigned COLEPropManager::PropSetToOrdinal(
GUID const & guidPS )
{
//
// This is likely to be a very small array. Just go for a linear search.
//
for ( unsigned i = 0; i < _aPropSets.Count(); i++ )
{
if ( _aPropSets[ i ].GetGuid() == guidPS )
return i;
}
//
// New property set. Need to add entry.
//
_aPropSets[ i ] = COLEPropManager::CPropSetMap( guidPS );
return i;
} //PropSetToOrdinal
//+---------------------------------------------------------------------------
//
// Function: IsNullPointerVariant
//
// Synopsis: Determines if a variant looks like it should have a
// pointer but doesn't.
//
// Arguments: [ppv] -- the variant to test
//
// Returns: TRUE if a variant with a 0 pointer, FALSE otherwise
//
// History: 04-Mar-96 dlee Created
//
//----------------------------------------------------------------------------
BOOL IsNullPointerVariant(
PROPVARIANT *ppv )
{
if ( (VT_VECTOR & ppv->vt) &&
0 != ppv->cal.cElems &&
0 == ppv->cal.pElems )
return TRUE;
switch (ppv->vt)
{
case VT_CLSID:
return ( 0 == ppv->puuid );
case VT_BLOB:
case VT_BLOB_OBJECT:
return ( ( 0 != ppv->blob.cbSize ) &&
( 0 == ppv->blob.pBlobData ) );
case VT_CF:
return ( ( 0 == ppv->pclipdata ) ||
( 0 == ppv->pclipdata->pClipData ) );
case VT_BSTR:
case VT_LPSTR:
case VT_LPWSTR:
return ( 0 == ppv->pszVal );
case VT_VECTOR | VT_BSTR:
case VT_VECTOR | VT_LPSTR:
case VT_VECTOR | VT_LPWSTR:
{
for (unsigned i = 0; i < ppv->calpstr.cElems; i++)
{
if ( 0 == ppv->calpstr.pElems[i] )
return TRUE;
}
break;
}
case VT_VECTOR | VT_VARIANT:
{
for (unsigned i = 0; i < ppv->capropvar.cElems; i++)
{
if ( IsNullPointerVariant( & (ppv->capropvar.pElems[i]) ) )
return TRUE;
}
break;
}
}
return FALSE;
} //IsNullPointerVariant
//+---------------------------------------------------------------------------
//
// Member: COLEPropManager::FetchProperty, public
//
// Synopsis: Retrieve a property from a property set.
//
// Arguments: [guidPS] -- property set to read from
// [psProperty] -- PROPSPEC for property.
// [pbData] -- Property stored here.
// [pcb] -- Size in bytes of [pbData]. On output, size
// actually required. An output <= input
// implies property successfully retrieved.
//
// History: 13-Dec-95 dlee Created
// 27-Aug-97 kylep Convert Ole summary info to Unicode
//
//----------------------------------------------------------------------------
inline BYTE * PastHeader(
PROPVARIANT * ppv )
{
return( (BYTE *)ppv + sizeof( PROPVARIANT ) );
}
#define VCASE( vvar, type ) \
cb += var[0].vvar.cElems * sizeof(var[0].vvar.pElems[0]); \
if ( cb <= *pcb ) \
{ \
pbData->vvar.pElems = (type *)PastHeader(pbData); \
memcpy( pbData->vvar.pElems, \
var[0].vvar.pElems, \
var[0].vvar.cElems * sizeof(var[0].vvar.pElems[0]) ); \
}
void COLEPropManager::FetchProperty(
GUID const & guidPS,
PROPSPEC const & psProperty,
PROPVARIANT * pbData,
unsigned * pcb )
{
if ( 0 == _ppsstg )
{
pbData->vt = VT_EMPTY;
*pcb = sizeof( *pbData );
return;
}
//
// The Office custom property set may not be opened when any other
// property set is opened, so treat this as a special case.
// Close all other property sets when opening it, and close it
// when opening any other property set.
//
if ( FMTID_UserDefinedProperties == guidPS )
{
if ( !_fOfficeCustomPropsetOpen )
{
_aPropSets.Clear();
_fOfficeCustomPropsetOpen = TRUE;
}
}
else if ( _fOfficeCustomPropsetOpen )
{
_aPropSets.Clear();
_fOfficeCustomPropsetOpen = FALSE;
}
unsigned iPropSet = PropSetToOrdinal( guidPS );
Win4Assert( iPropSet < _aPropSets.Count() );
COLEPropManager::CPropSetMap & sm = _aPropSets[ iPropSet ];
//
// May have to open the set.
//
if ( !sm.isOpen( _ppsstg ) )
{
pbData->vt = VT_EMPTY;
*pcb = sizeof( *pbData );
return;
}
PROPVARIANT var[2];
PropVariantInit( &var[0] );
PropVariantInit( &var[1] );
PROPSPEC aps[2];
aps[0] = psProperty;
aps[1].ulKind = PRSPEC_PROPID;
aps[1].propid = PID_CODEPAGE;
SCODE sc = sm.GetPS().ReadMultiple( 2,
aps,
var );
// A few specific failures should throw and and cause the
// entire query to fail.
if ( E_OUTOFMEMORY == sc ||
STG_E_INSUFFICIENTMEMORY == sc ||
E_UNEXPECTED == sc )
THROW( CException( sc ) );
if ( FAILED( sc ) )
{
// the first three would be due to invalid parameters, which
// would be a programming bug.
Win4Assert( STG_E_INVALIDPARAMETER != sc );
Win4Assert( STG_E_INVALIDPOINTER != sc );
Win4Assert( HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION) != sc );
// access-denied is the only legal error that maps to VT_EMPTY
Win4Assert( STG_E_ACCESSDENIED == sc );
pbData->vt = VT_EMPTY;
*pcb = sizeof( *pbData );
return;
}
// The rest of the query code doesn't understand variants that
// require a pointer but don't have one. Such variants are legal,
// but we map them to VT_EMPTY.
if ( IsNullPointerVariant( &var[0] ) )
{
FreePropVariantArray( 1, &var[0] );
pbData->vt = VT_EMPTY;
*pcb = sizeof( *pbData );
return;
}
//
// Maybe we didn't get a codepage?
//
DWORD dwCodepage = ( var[1].vt == VT_I2 && CP_WINUNICODE != var[1].iVal ) ?
(unsigned short) var[1].iVal :
CP_ACP;
*pbData = var[0];
unsigned cb = sizeof PROPVARIANT;
//
// Deal with variable length portion.
//
switch ( var[0].vt )
{
case VT_EMPTY:
case VT_NULL:
case VT_I1:
case VT_UI1:
case VT_I2:
case VT_UI2:
case VT_I4:
case VT_UI4:
case VT_INT:
case VT_UINT:
case VT_DECIMAL:
case VT_I8:
case VT_UI8:
case VT_R4:
case VT_R8:
case VT_BOOL:
case VT_ILLEGAL:
case VT_ERROR:
case VT_CY:
case VT_DATE:
case VT_FILETIME:
break; // fixed length types -- nothing to do
case VT_BSTR:
cb += BSTRLEN( var[0].bstrVal ) + sizeof(OLECHAR) + sizeof(DWORD);
if (cb <= *pcb)
{
BSTR bstr = (BSTR) PastHeader(pbData);
memcpy( bstr, &BSTRLEN(var[0].bstrVal), cb - sizeof(PROPVARIANT) );
pbData->bstrVal = (BSTR) (((DWORD *)bstr) + 1);
}
break;
case VT_LPSTR:
//
// HACK #274: Translate the Ole summary information LPSTR in LPWSTR
// Makes these properties compatible with HTML filter
// equivalents.
//
if ( FMTID_SummaryInformation == guidPS )
{
unsigned cc = strlen( var[0].pszVal ) + 1;
unsigned ccT = MultiByteToWideChar( dwCodepage,
0, // precomposed used if the codepage supports it
var[0].pszVal,
cc,
(WCHAR *)PastHeader(pbData),
(*pcb - sizeof(PROPVARIANT)) / sizeof(WCHAR) );
if ( 0 == ccT )
{
if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
{
unsigned ccNeeded = MultiByteToWideChar( dwCodepage,
0, // precomposed used if the codepage supports it
var[0].pszVal,
cc,
0,
0 );
Win4Assert( ccNeeded > 0 );
cb += ccNeeded * sizeof(WCHAR);
}
else
{
ciDebugOut(( DEB_ERROR, "Error %d converting \"%s\" to codepage 0x%x\n",
GetLastError(), var[0].pszVal, dwCodepage ));
cb += strlen( var[0].pszVal ) + 1;
if (cb <= *pcb)
{
pbData->pszVal = (char *)PastHeader(pbData);
memcpy( pbData->pszVal, var[0].pszVal, cb - sizeof(PROPVARIANT) );
}
}
}
if ( ccT != 0 )
{
pbData->vt = VT_LPWSTR;
pbData->pwszVal = (WCHAR *)PastHeader(pbData);
cb += ccT * sizeof(WCHAR);
}
}
else
{
cb += strlen( var[0].pszVal ) + 1;
if (cb <= *pcb)
{
pbData->pszVal = (char *)PastHeader(pbData);
memcpy( pbData->pszVal, var[0].pszVal, cb - sizeof(PROPVARIANT) );
}
}
break;
case VT_LPWSTR:
cb += (wcslen( var[0].pwszVal ) + 1) * sizeof(WCHAR);
if (cb <= *pcb)
{
pbData->pwszVal = (WCHAR *)PastHeader(pbData);
memcpy( pbData->pwszVal, var[0].pwszVal, cb - sizeof(PROPVARIANT) );
}
break;
case VT_CLSID:
cb += sizeof(GUID);
if (cb <= *pcb)
{
pbData->puuid = (GUID *)PastHeader(pbData);
memcpy( pbData->puuid, var[0].puuid, cb - sizeof(PROPVARIANT) );
}
break;
case VT_BLOB:
cb += var[0].blob.cbSize;
if (cb <= *pcb)
{
pbData->blob.pBlobData = PastHeader(pbData);
memcpy( pbData->blob.pBlobData, var[0].blob.pBlobData, cb - sizeof(PROPVARIANT) );
}
break;
case VT_CF:
cb += sizeof(*(var[0].pclipdata)) + CBPCLIPDATA(*var[0].pclipdata);
if ( cb <= *pcb )
{
pbData->pclipdata = (CLIPDATA *)PastHeader(pbData);
memcpy( pbData->pclipdata, var[0].pclipdata, sizeof(*(var[0].pclipdata)) );
pbData->pclipdata->pClipData = PastHeader(pbData) + sizeof(*(var[0].pclipdata));
memcpy( pbData->pclipdata->pClipData,
var[0].pclipdata->pClipData,
CBPCLIPDATA(*var[0].pclipdata) );
}
break;
//
// Vectors (ugh!)
//
case VT_I1|VT_VECTOR:
case VT_UI1|VT_VECTOR:
VCASE( caub, BYTE );
break;
case VT_I2|VT_VECTOR:
VCASE( cai, short );
break;
case VT_UI2|VT_VECTOR:
VCASE( caui, USHORT );
break;
case VT_BOOL|VT_VECTOR:
VCASE( cabool, VARIANT_BOOL );
break;
case VT_I4|VT_VECTOR:
case VT_INT|VT_VECTOR:
VCASE( cal, long );
break;
case VT_UI4|VT_VECTOR:
case VT_UINT|VT_VECTOR:
VCASE( caul, ULONG );
break;
case VT_R4|VT_VECTOR:
VCASE( caflt, float );
break;
case VT_ERROR|VT_VECTOR:
VCASE( cascode, SCODE );
break;
case VT_I8|VT_VECTOR:
VCASE( cah, LARGE_INTEGER );
break;
case VT_UI8|VT_VECTOR:
VCASE( cauh, ULARGE_INTEGER );
break;
case VT_R8|VT_VECTOR:
VCASE( cadbl, double );
break;
case VT_CY|VT_VECTOR:
VCASE( cacy, CY );
break;
case VT_DATE|VT_VECTOR:
VCASE( cadate, DATE );
break;
case VT_FILETIME|VT_VECTOR:
VCASE( cafiletime, FILETIME );
break;
case VT_CLSID|VT_VECTOR:
VCASE( cauuid, CLSID );
break;
case VT_CF|VT_VECTOR:
{
cb += var[0].caclipdata.cElems * sizeof(var[0].caclipdata.pElems[0]);
for ( unsigned i = 0; i < var[0].caclipdata.cElems; i++ )
{
cb += CBPCLIPDATA(var[0].caclipdata.pElems[i]);
}
if ( cb <= *pcb )
{
pbData->caclipdata.pElems = (CLIPDATA *)PastHeader(pbData);
memcpy( pbData->caclipdata.pElems,
var[0].caclipdata.pElems,
var[0].caclipdata.cElems * sizeof(var[0].caclipdata.pElems[0]) );
BYTE * pb = PastHeader(pbData) + var[0].caclipdata.cElems * sizeof(var[0].caclipdata.pElems[0]);
for ( i = 0; i < var[0].caclipdata.cElems; i++ )
{
pbData->caclipdata.pElems[i].pClipData = pb;
memcpy( pbData->caclipdata.pElems[i].pClipData,
var[0].caclipdata.pElems[i].pClipData,
CBPCLIPDATA(var[0].caclipdata.pElems[i]) );
pb += CBPCLIPDATA(var[0].caclipdata.pElems[i]);
}
}
break;
}
case VT_LPSTR|VT_VECTOR:
{
cb += var[0].calpstr.cElems * sizeof( var[0].calpstr.pElems[0] );
for ( unsigned i = 0; i < var[0].calpstr.cElems; i++ )
{
cb += strlen( var[0].calpstr.pElems[i] ) + 1;
}
if ( cb <= *pcb )
{
pbData->calpstr.pElems = (char **)PastHeader(pbData);
char * pc = (char *)PastHeader(pbData) +
var[0].calpstr.cElems * sizeof( var[0].calpstr.pElems[0] );
for ( i = 0; i < var[0].calpstr.cElems; i++ )
{
pbData->calpstr.pElems[i] = pc;
unsigned cc = strlen( var[0].calpstr.pElems[i] ) + 1;
memcpy( pbData->calpstr.pElems[i],
var[0].calpstr.pElems[i],
cc );
pc += cc;
}
}
break;
}
case VT_LPWSTR|VT_VECTOR:
{
cb += var[0].calpwstr.cElems * sizeof( var[0].calpwstr.pElems[0] );
for ( unsigned i = 0; i < var[0].calpwstr.cElems; i++ )
{
cb += (wcslen( var[0].calpwstr.pElems[i] ) + 1) * sizeof(WCHAR);
}
if ( cb <= *pcb )
{
pbData->calpwstr.pElems = (WCHAR **)PastHeader(pbData);
WCHAR * pwc = (WCHAR *)(PastHeader(pbData) +
var[0].calpwstr.cElems * sizeof( var[0].calpwstr.pElems[0] ));
for ( i = 0; i < var[0].calpwstr.cElems; i++ )
{
pbData->calpwstr.pElems[i] = pwc;
unsigned cc = wcslen( var[0].calpwstr.pElems[i] ) + 1;
memcpy( pbData->calpwstr.pElems[i],
var[0].calpwstr.pElems[i],
cc * sizeof(WCHAR) );
pwc += cc;
}
}
break;
}
case VT_BSTR|VT_VECTOR:
{
cb += var[0].cabstr.cElems * sizeof( var[0].cabstr.pElems[0] );
for ( unsigned i = 0; i < var[0].cabstr.cElems; i++ )
{
cb += AlignBlock( BSTRLEN( var[0].cabstr.pElems[i] ) +
sizeof OLECHAR + sizeof DWORD,
sizeof DWORD );
}
if ( cb <= *pcb )
{
pbData->cabstr.pElems = (BSTR *)PastHeader(pbData);
BSTR pwc = (BSTR)(PastHeader(pbData) +
var[0].cabstr.cElems * sizeof( var[0].cabstr.pElems[0] ));
for ( i = 0; i < var[0].cabstr.cElems; i++ )
{
pbData->cabstr.pElems[i] = (BSTR) (((DWORD *) pwc) + 1);
unsigned cbbstr = BSTRLEN( var[0].cabstr.pElems[i] ) +
sizeof(OLECHAR) + sizeof (DWORD);
memcpy( pwc,
&BSTRLEN(var[0].cabstr.pElems[i]),
cbbstr);
pwc += AlignBlock( cbbstr, sizeof DWORD ) / sizeof OLECHAR;
}
}
break;
}
case VT_ARRAY | VT_I4:
case VT_ARRAY | VT_UI1:
case VT_ARRAY | VT_I2:
case VT_ARRAY | VT_R4:
case VT_ARRAY | VT_R8:
case VT_ARRAY | VT_BOOL:
case VT_ARRAY | VT_ERROR:
case VT_ARRAY | VT_CY:
case VT_ARRAY | VT_DATE:
case VT_ARRAY | VT_I1:
case VT_ARRAY | VT_UI2:
case VT_ARRAY | VT_UI4:
case VT_ARRAY | VT_INT:
case VT_ARRAY | VT_UINT:
case VT_ARRAY | VT_DECIMAL:
case VT_ARRAY | VT_BSTR:
case VT_ARRAY | VT_VARIANT:
{
SAFEARRAY * pSaSrc = var[0].parray;
SAFEARRAY * pSaDest = 0;
cb += SaComputeSize( var[0].vt & ~VT_ARRAY, *pSaSrc );
ciDebugOut(( DEB_ITRACE, "fetch safearray, *pcb, cb: %d, %d\n", *pcb, cb ));
if ( cb <= *pcb )
{
CNonAlignAllocator ma( *pcb - sizeof (PROPVARIANT), PastHeader(pbData) );
if ( SaCreateAndCopy( ma, pSaSrc, &pSaDest ) &&
SaCreateDataUsingMA( ma,
var[0].vt & ~VT_ARRAY,
*pSaSrc,
*pSaDest,
TRUE) )
{
pbData->parray = pSaDest;
ciDebugOut(( DEB_ITRACE, " pSaDest: %#x\n", pSaDest ));
}
else
{
// We've been guaranteed by SaComputeSize and *pcb that there
// is sufficient memory to copy the array. There must be a
// bug if we can't.
ciDebugOut(( DEB_ERROR,
" can't copy SA %#x using %d bytes\n",
pSaSrc, cb ));
Win4Assert( !"can't copy safearray" );
}
}
break;
}
case VT_VARIANT|VT_VECTOR:
{
vqDebugOut(( DEB_WARN,
"COLEPropManager::FetchProperty - variant vector fetch %x\n",
&var[0] ));
//Win4Assert( !"Fetch of variant vector not yet implemented" );
pbData->vt = VT_EMPTY;
*pcb = sizeof( *pbData );
break;
}
case VT_STREAM:
case VT_STREAMED_OBJECT:
case VT_STORED_OBJECT:
case VT_BLOB_OBJECT:
{
//
// We don't support these datatypes. Some make since to support
// some day, but users can always retrieve the path and load the
// values themselves. Performance-wise it makes no since for us
// to remote the values from our process.
//
pbData->vt = VT_EMPTY;
cb = sizeof( *pbData );
break;
}
default:
Win4Assert( !"Unhandled variant type!" );
}
//
// Cleanup
//
FreePropVariantArray( 1, &var[0] );
*pcb = cb;
} //FetchProperty
//+---------------------------------------------------------------------------
//
// Member: COLEPropManager::CPropSetMap::isOpen, public
//
// Synopsis: Opens a property storage if it isn't already open
//
// Arguments: [ppsstg] - property set storage from which property
// storage is opened.
//
// History: 13-Dec-95 dlee Created
//
//----------------------------------------------------------------------------
BOOL COLEPropManager::CPropSetMap::isOpen(
IPropertySetStorage * ppsstg )
{
if ( 0 != _pstg )
return TRUE;
if ( _fOpenAttempted )
return FALSE;
_fOpenAttempted = TRUE;
SCODE sc = ppsstg->Open( _guid,
STGM_READ | STGM_SHARE_EXCLUSIVE, // BChapman said to use these
& _pstg );
if ( FAILED( sc ) )
{
// don't fail the entire query if the property set doesn't
// exist or if access is not allowed for this user, or if the
// file was reverted after being opened.
if ( STG_E_FILENOTFOUND != sc &&
STG_E_ACCESSDENIED != sc &&
NS_E_UNRECOGNIZED_STREAM_TYPE != sc && // The Shell enumeration routines do this
NS_E_INVALID_DATA != sc && //
NS_E_FILE_INIT_FAILED != sc && // ""
NS_E_FILE_OPEN_FAILED != sc && // ""
E_FAIL != sc && // ""
STG_E_REVERTED != sc )
{
vqDebugOut(( DEB_WARN, "psstg->open failed: 0x%x\n", sc ));
// this would be a coding bug in CI
Win4Assert( STG_E_INVALIDPARAMETER != sc );
Win4Assert( E_UNEXPECTED == sc ||
E_OUTOFMEMORY == sc ||
STG_E_INSUFFICIENTMEMORY == sc ||
STG_E_INVALIDHEADER == sc ||
STG_E_DOCFILECORRUPT == sc ||
( HRESULT_FROM_WIN32( ERROR_NOT_READY ) == sc ) );
THROW( CException( sc ) );
}
}
return SUCCEEDED( sc );
} //isOpen
//+---------------------------------------------------------------------------
//
// Member: COLEPropManager::CPropSetMap::Close, public
//
// Synopsis: Closes a property storage
//
// History: 13-Dec-95 dlee Created
//
//----------------------------------------------------------------------------
void COLEPropManager::CPropSetMap::Close()
{
if ( 0 != _pstg )
{
_pstg->Release();
_pstg = 0;
}
_fOpenAttempted = FALSE;
} //Close
//+---------------------------------------------------------------------------
//
// Member: COLEPropManager::ReadProperty, public
//
// Synopsis: Reads an OLE property value using CoTaskMem memory
//
// Arguments: [ps] -- property spec to read
// [Var] -- where to put the value
//
// Returns: TRUE if a value found, FALSE otherwise, or throws on error
//
// History: 18-Dec-97 dlee Created
//
//----------------------------------------------------------------------------
BOOL COLEPropManager::ReadProperty(
CFullPropSpec const & ps,
PROPVARIANT & Var )
{
Var.vt = VT_EMPTY;
if ( 0 == _ppsstg )
return FALSE;
//
// Open the property storage for the given guid
//
IPropertyStorage * pPropStg;
SCODE sc = _ppsstg->Open( ps.GetPropSet(), // guid
STGM_READ | STGM_SHARE_EXCLUSIVE, // BChapman said to use these...
&pPropStg );
if ( SUCCEEDED(sc) )
{
XInterface<IPropertyStorage> xPropStg( pPropStg );
//
// Read the value
//
//
// HACK #274: Translate the Ole summary information LPSTR in LPWSTR
// Makes these properties compatible with HTML filter
// equivalents.
//
if ( ps.GetPropSet() == FMTID_SummaryInformation )
{
PROPVARIANT var[2];
PropVariantInit( &var[0] );
PropVariantInit( &var[1] );
PROPSPEC aps[2];
aps[0] = ps.GetPropSpec();
aps[1].ulKind = PRSPEC_PROPID;
aps[1].propid = PID_CODEPAGE;
sc = xPropStg->ReadMultiple( 2,
aps,
var );
if ( FAILED(sc) )
return DBSTATUS_S_ISNULL;
//
// Did we get a codepage?
//
DWORD dwCodepage = ( var[1].vt == VT_I2 && CP_WINUNICODE != var[1].iVal ) ?
(unsigned short) var[1].iVal :
CP_ACP;
if ( VT_LPWSTR == var[0].vt )
{
//
// Convert to Unicode
//
unsigned cc = strlen( var[0].pszVal ) + 1;
XGrowable<WCHAR> xwcsProp( cc + (cc * 10 / 100) ); // 10% fluff
unsigned ccT = 0;
while ( 0 == ccT )
{
ccT = MultiByteToWideChar( dwCodepage,
0, // precomposed implied if the codepage supports it
var[0].pszVal,
cc,
xwcsProp.Get(),
xwcsProp.Count() );
if ( 0 == ccT )
{
if ( ERROR_INSUFFICIENT_BUFFER == GetLastError() )
{
unsigned ccNeeded = MultiByteToWideChar( dwCodepage,
0, // precomposed implied if the codepage supports it
var[0].pszVal,
cc,
0,
0 );
Win4Assert( ccNeeded > 0 );
xwcsProp.SetSize( ccNeeded );
}
else
{
vqDebugOut(( DEB_ERROR, "Error %d converting %s to codepage 0x%x\n",
GetLastError(), var[0].pszVal, dwCodepage ));
ccT = 0;
break;
}
}
}
if ( ccT != 0 )
{
Var.vt = VT_LPWSTR;
Var.pwszVal = (WCHAR *)CoTaskMemAlloc( ccT * sizeof(WCHAR) );
if ( 0 == Var.pwszVal )
{
FreePropVariantArray( 2, var );
THROW( CException( E_OUTOFMEMORY ) );
}
RtlCopyMemory( Var.pwszVal, xwcsProp.Get(), ccT * sizeof(WCHAR) );
FreePropVariantArray( 2, var );
}
else
Var = var[0];
}
else
Var = var[0];
}
else
{
sc = xPropStg->ReadMultiple( 1, // 1 value to retrieve
&ps.GetPropSpec(),
&Var );
}
}
if ( STG_E_FILENOTFOUND == sc ||
STG_E_ACCESSDENIED == sc )
return FALSE;
if ( FAILED( sc ) )
THROW( CException( sc ) );
return TRUE;
} //ReadProperty