|
|
//+------------------------------------------------------------------
//
// 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
|