Leaked source code of windows server 2003
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.
 
 
 
 
 
 

897 lines
26 KiB

//+==================================================================
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 1998.
//
// File: PropAPI.cxx
//
// This file provides the Property Set API routines.
//
// APIs: StgCreatePropSetStg (creates an IPropertySetStorage)
// StgCreatePropStg (creates an IPropertyStorage)
// StgOpenPropStg (opens an IPropertyStorage)
// StgCreateStorageOnHandle (private, not a public API)
// StgOpenStorageOnHandle (private, not a public API)
//
// History:
//
// 3/10/98 MikeHill - Added StgCreate/OpenStorageOnHandle
// 5/6/98 MikeHill - Rewrite StgCreate/OpenStorageOnHandle.
// 5/18/98 MikeHill
// - Use new CPropertySetStorage constructor.
//
//+==================================================================
#include <pch.cxx>
#ifdef _MAC_NODOC
ASSERTDATA // File-specific data for FnAssert
#endif
//+------------------------------------------------------------------
//
// Function: QueryForIStream
//
// Synopsis: This routine queries an IUnknown for
// an IStream interface. This is isolated into
// a separate routine because some workaround code
// is required on the Mac.
//
// Inputs: [IUnknown*] pUnk
// The interface to be queried.
// [IStream**] ppStm
// Location to return the result.
//
// Returns: [HRESULT]
//
// Notes: On older Mac implementations (<=2.08, <=~1996)
// the memory-based IStream implementation
// (created by CreateStreamOnHGlobal) had a bug
// in QueryInterface: when you QI for an
// IStream or IUnknown, an addref is done, but an
// HR of E_NOINTERFACE is returned.
//
// Below, we look for this condition: if we get an
// E_NOINTERFACE on the Mac, we check to see if it's
// an OLE mem-based IStream. If it is, we simply cast
// IUnknown as an IStream. We validate it as an OLE
// the mem-based IStream by creating one of our own, and
// comparing the QueryInterface addresses.
//
// This is some ugly code, but at least it is isolated,
// only runs on the older Macs, and ensures that we
// work on all platforms.
//
//+------------------------------------------------------------------
inline HRESULT QueryForIStream( IUnknown * pUnk, IStream** ppStm )
{
HRESULT hr;
// Attempt to get the interface
hr = pUnk->QueryInterface( IID_IStream, (void**) ppStm );
#ifdef _MAC
// On the Mac, if we get a no-interface error, see if it is really
// a buggy mem-based IStream implementation.
if( E_NOINTERFACE == hr )
{
IStream *pstmMem = NULL;
// Create our own mem-based IStream.
hr = CreateStreamOnHGlobal( NULL, TRUE, &pstmMem );
if( FAILED(hr) ) goto Exit;
// If the mem-based Stream's QI implementation has the same
// address as the Unknown's QI implementation, then the Unknown
// must be an OLE mem-based stream.
if( pUnk->QueryInterface == pstmMem->QueryInterface )
{
// We can just cast the IUnknown* as an IStream* and
// we're done (the original QI, despite returning an
// error, has already done an AddRef).
hr = S_OK;
*ppStm = (IStream*) pUnk;
}
else
{
// This is a real no-interface error, so let's return it.
hr = E_NOINTERFACE;
}
pstmMem->Release();
}
// ----
// Exit
// ----
Exit:
#endif // #ifdef _MAC
return( hr );
} // QueryForIStream()
//+------------------------------------------------------------------
//
// Function: StgCreatePropStg
//
// Synopsis: Given an IStorage or IStream, create an
// IPropertyStorage. This is similar to the
// IPropertySetStorage::Create method. Existing
// contents of the Storage/Stream are removed.
//
// Inputs: [IUnknown*] pUnk
// An IStorage* for non-simple propsets,
// an IStream* for simple. grfFlags is
// used to disambiguate.
// [REFFMTID] fmtid
// The ID of the property set.
// [DWORD] grfFlags
// From the PROPSETFLAG_* enumeration.
// [IPropertySetStorage**] ppPropStg
// The result.
//
// Returns: [HRESULT]
//
// Notes: The caller is responsible for maintaining
// thread-safety between the original
// IStorage/IStream and this IPropertyStorage.
//
//+------------------------------------------------------------------
STDAPI StgCreatePropStg( IUnknown *pUnk,
REFFMTID fmtid,
const CLSID *pclsid,
DWORD grfFlags,
DWORD dwReserved,
IPropertyStorage **ppPropStg)
{
// ------
// Locals
// ------
HRESULT hr;
IStream *pstm = NULL;
IStorage *pstg = NULL;
CPropertyStorage *pPropStg = NULL;
// ----------
// Validation
// ----------
propXTraceStatic( "StgCreatePropStg" );
GEN_VDATEIFACE_LABEL( pUnk, E_INVALIDARG, Exit, hr );
GEN_VDATEREADPTRIN_LABEL(&fmtid, FMTID, E_INVALIDARG, Exit, hr );
GEN_VDATEPTRIN_LABEL(pclsid, CLSID, E_INVALIDARG, Exit, hr );
// grfFlags is validated by CPropertyStorage
GEN_VDATEPTROUT_LABEL( ppPropStg, IPropertyStorage*, E_INVALIDARG, Exit, hr );
*ppPropStg = NULL;
propTraceParameters(( "pUnk=%p, fmtid=%s, clsid=%s, grfFlags=%s, dwReserved=0x%x, ppPropStg=%p",
pUnk, static_cast<const char*>(CStringize(fmtid)),
static_cast<const char*>(CStringize(*pclsid)),
static_cast<const char*>(CStringize(SGrfFlags(grfFlags))),
dwReserved, ppPropStg ));
// -----------------------
// Non-Simple Property Set
// -----------------------
if( grfFlags & PROPSETFLAG_NONSIMPLE )
{
// Get the IStorage*
hr = pUnk->QueryInterface( IID_IStorage, (void**) &pstg );
if( FAILED(hr) ) goto Exit;
// Create the IPropertyStorage implementation
pPropStg = new CPropertyStorage( MAPPED_STREAM_CREATE );
if( NULL== pPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage
hr = pPropStg->Create( pstg, fmtid, pclsid, grfFlags,
0 ); // We don't know the grfMode
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE )
// -------------------
// Simple Property Set
// -------------------
else
{
// Get the IStream*
hr = QueryForIStream( pUnk, &pstm );
if( FAILED(hr) ) goto Exit;
// Create an IPropertyStorage implementation.
pPropStg = new CPropertyStorage( MAPPED_STREAM_CREATE );
if( NULL == pPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage (which
// is responsible for sizing and seeking the
// stream).
hr = pPropStg->Create( pstm, fmtid, pclsid, grfFlags,
0 ); // We don't know the grfMode
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE ) ... else
// ----
// Exit
// ----
*ppPropStg = static_cast<IPropertyStorage*>(pPropStg);
pPropStg = NULL;
Exit:
// If we created pPropStg, and there was an error, delete it.
#if DBG
if( FAILED(hr) )
{
propDbg((DEB_ERROR, "StgCreatePropStg returns %08X\n", hr ));
}
#endif
if( NULL != pPropStg )
delete pPropStg;
if( NULL != pstm )
pstm->Release();
if( NULL != pstg )
pstg->Release();
return( hr );
} // StgCreatePropStg()
//+------------------------------------------------------------------
//
// Function: StgOpenPropStg
//
// Synopsis: Given an IStorage or IStream which hold a
// serialized property set, create an
// IPropertyStorage. This is similar to the
// IPropertySetStorage::Open method.
//
// Inputs: [IUnknown*] pUnk
// An IStorage* for non-simple propsets,
// an IStream* for simple. grfFlags is
// used to disambiguate.
// [REFFMTID] fmtid
// The ID of the property set.
// [DWORD] grfFlags
// From the PROPSETFLAG_* enumeration.
// [IPropertySetStorage**] ppPropStg
// The result.
//
// Returns: [HRESULT]
//
// Notes: The caller is responsible for maintaining
// thread-safety between the original
// IStorage/IStream and this IPropertyStorage.
//
//+------------------------------------------------------------------
STDAPI StgOpenPropStg( IUnknown* pUnk,
REFFMTID fmtid,
DWORD grfFlags,
DWORD dwReserved,
IPropertyStorage **ppPropStg)
{
// ------
// Locals
// ------
HRESULT hr;
IStream *pstm = NULL;
IStorage *pstg = NULL;
CPropertyStorage *pPropStg = NULL;
// ----------
// Validation
// ----------
propXTraceStatic( "StgOpenPropStg" );
GEN_VDATEIFACE_LABEL( pUnk, E_INVALIDARG, Exit, hr );
GEN_VDATEREADPTRIN_LABEL(&fmtid, FMTID, E_INVALIDARG, Exit, hr);
// grfFlags is validated by CPropertyStorage
GEN_VDATEPTROUT_LABEL( ppPropStg, IPropertyStorage*, E_INVALIDARG, Exit, hr );
propTraceParameters(( "pUnk=%p, fmtid=%s, grfFlags=%s, dwReserved=0x%x, ppPropStg=%p",
pUnk, static_cast<const char*>(CStringize(fmtid)),
static_cast<const char*>(CStringize(SGrfFlags(grfFlags))),
dwReserved, ppPropStg ));
// -----------------------
// Non-Simple Property Set
// -----------------------
*ppPropStg = NULL;
if( grfFlags & PROPSETFLAG_NONSIMPLE )
{
// Get the IStorage*
hr = pUnk->QueryInterface( IID_IStorage, (void**) &pstg );
if( FAILED(hr) ) goto Exit;
// Create an IPropertyStorage* implementation.
pPropStg = new CPropertyStorage( MAPPED_STREAM_CREATE );
if( NULL == pPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage by reading
// the serialized property set.
hr = pPropStg->Open( pstg, fmtid, grfFlags,
0 ); // We don't know the grfMode
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE )
// -------------------
// Simple Property Set
// -------------------
else
{
// Get the IStream*
hr = QueryForIStream( pUnk, &pstm );
if( FAILED(hr) ) goto Exit;
// Create an IPropertyStorage* implementation.
pPropStg = new CPropertyStorage(MAPPED_STREAM_CREATE );
if( NULL == pPropStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Initialize the IPropertyStorage by reading
// the serialized property set (the CPropertyStorage
// is responsible for seeking to the stream start).
hr = pPropStg->Open( pstm, fmtid, grfFlags,
0, // We don't know the grfMode
FALSE ); // Not deleting
if( FAILED(hr) ) goto Exit;
} // if( grfFlags & PROPSETFLAG_NONSIMPLE ) ... else
// ----
// Exit
// ----
*ppPropStg = static_cast<IPropertyStorage*>(pPropStg);
pPropStg = NULL;
Exit:
#if DBG
if( FAILED(hr) )
{
propDbg((DEB_ERROR, "StgOpenPropStg returns %08X\n", hr ));
}
#endif
if( NULL != pPropStg )
delete pPropStg;
if( NULL != pstm )
pstm->Release();
if( NULL != pstg )
pstg->Release();
return( hr );
} // StgOpenPropStg()
//+------------------------------------------------------------------
//
// Function: StgCreatePropSetStg
//
// Synopsis: Given an IStorage, create an IPropertySetStorage.
// This is similar to QI-ing a DocFile IStorage for
// the IPropertySetStorage interface.
//
// Inputs: [IStorage*] pStorage
// Will be held by the propsetstg and used
// for create/open.
// [IPropertySetStorage**] ppPropSetStg
// Receives the result.
//
// Returns: [HRESULT]
//
// Notes: The caller is responsible for maintaining
// thread-safety between the original
// IStorage and this IPropertySetStorage.
//
//+------------------------------------------------------------------
STDAPI
StgCreatePropSetStg( IStorage *pStorage,
DWORD dwReserved,
IPropertySetStorage **ppPropSetStg)
{
HRESULT hr = S_OK;
CPropertySetStorage *pPropSetStg = NULL;
// Validation
propXTraceStatic( "StgCreatePropSetStg" );
GEN_VDATEIFACE_LABEL( pStorage, E_INVALIDARG, Exit, hr );
GEN_VDATEPTROUT_LABEL( ppPropSetStg, IPropertySetStorage*, E_INVALIDARG, Exit, hr );
propTraceParameters(( "pStorage=%p, dwReserved=0x%x, ppPropSetStg=%p",
pStorage, dwReserved, ppPropSetStg ));
// Create the IPropertySetStorage implementation.
pPropSetStg = new CPropertySetStorage( MAPPED_STREAM_CREATE );
if( NULL == pPropSetStg )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Pass the caller-provided storage into the CPropertySetStorage
pPropSetStg->Init( pStorage, /*IBlockingLock*/ NULL,
TRUE ); // fControlLifetime (=> addref)
// ----
// Exit
// ----
hr = S_OK;
*ppPropSetStg = static_cast<IPropertySetStorage*>(pPropSetStg);
pPropSetStg = NULL;
Exit:
RELEASE_INTERFACE(pPropSetStg);
if( FAILED(hr) )
propDbg((DEB_ERROR, "StgCreatePropSetStg() returns %08X\n", hr ));
return( hr );
} // StgCreatePropSetStg()
//+----------------------------------------------------------------------------
//
// Function: FmtIdToPropStgName
//
// Synopsis: This function maps a property set's FMTID to the name of
// the Stream or Storage which contains it. This name
// is 27 characters (including the terminator).
//
// Inputs: [const FMTID*] pfmtid (in)
// The FMTID of the property set.
// [LPOLESTR] oszName (out)
// The name of the Property Set's Stream/Storage
//
// Returns: [HRESULT] S_OK or E_INVALIDARG
//
//+----------------------------------------------------------------------------
STDAPI
FmtIdToPropStgName( const FMTID *pfmtid, LPOLESTR oszName )
{
HRESULT hr = S_OK;
// Validate Inputs
propXTraceStatic( "FmtIdToPropStgName" );
GEN_VDATEREADPTRIN_LABEL(pfmtid, FMTID, E_INVALIDARG, Exit, hr);
VDATESIZEPTROUT_LABEL(oszName,
sizeof(OLECHAR) * (CCH_MAX_PROPSTG_NAME+1),
Exit, hr);
propTraceParameters(( "fmtid=%s, oszName=%p",
static_cast<const char*>(CStringize(*pfmtid)), oszName ));
// Make the Conversion
PrGuidToPropertySetName( pfmtid, oszName );
// Exit
Exit:
if( FAILED(hr) )
{
propDbg((DEB_ERROR, "FmtIdToPropStgName returns %08X\n", hr ));
}
return( hr );
} // FmtIdToPropStgName()
//+----------------------------------------------------------------------------
//
// Function: PropStgNameToFmtId
//
// Synopsis: This function maps a property set's Stream/Storage name
// to its FMTID.
//
// Inputs: [const LPOLESTR] oszName (in)
// The name of the Property Set's Stream/Storage
// [FMTID*] pfmtid (out)
// The FMTID of the property set.
//
//
// Returns: [HRESULT] S_OK or E_INVALIDARG
//
//+----------------------------------------------------------------------------
STDAPI
PropStgNameToFmtId( const LPOLESTR oszName, FMTID *pfmtid )
{
HRESULT hr = S_OK;
// Validate Inputs
propXTraceStatic( "PropStgNameToFmtId" );
GEN_VDATEPTROUT_LABEL(pfmtid, FMTID, E_INVALIDARG, Exit, hr);
propTraceParameters(( "oszName=%p, *pfmtid=%s", oszName,
static_cast<const char*>(CStringize(*pfmtid)) ));
#ifdef OLE2ANSI
if( FAILED(hr = ValidateNameA(oszName, CCH_MAX_PROPSTG_NAME )))
goto Exit;
#else
if( FAILED(hr = ValidateNameW(oszName, CCH_MAX_PROPSTG_NAME )))
goto Exit;
#endif
// Make the Conversion, passing in the name and its character-length
// (not including the null-terminator).
PrPropertySetNameToGuid( ocslen(oszName), oszName, pfmtid );
// Exit
Exit:
propDbg(( DbgFlag(hr,DEB_TRACE), "PropStgNameToFmtId returns %08x", hr ));
return( hr );
} // PropStgNameToFmtId()
//+----------------------------------------------------------------------------
//
// Function: CreateOrOpenDocfileOnHandle
//
// Create or open a Docfile IStorage (or QI-able interface) on a given
// handle.
//
//+----------------------------------------------------------------------------
CreateOrOpenDocfileOnHandle( IN BOOL fCreate,
IN DWORD grfMode,
IN HANDLE *phStream,
IN REFIID riid,
OUT void ** ppObjectOpen)
{
HRESULT hr = S_OK;
NTSTATUS status = STATUS_SUCCESS;
CNtfsStream *pnffstm = NULL;
CNFFTreeMutex *pmutex = NULL;
IStorage *pstg = NULL;
propITraceStatic( "CreateOrOpenDocfileOnHandle" );
// --------------------
// Create an ILockBytes
// --------------------
// Instantiate a mutex
pmutex = new CNFFTreeMutex();
if( NULL == pmutex )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
hr = pmutex->Init();
if ( FAILED(hr) ) goto Exit;
// Use the mutex to instantiate an NFF stream object
pnffstm = new CNtfsStream( NULL, pmutex );
if( NULL == pnffstm )
{
hr = E_OUTOFMEMORY;
goto Exit;
}
// Put the stream handle and grfMode into the NFF stream object.
// We now have our ILockBytes implementation for the given handle.
hr = pnffstm->Init( *phStream, grfMode, NULL, NULL );
if( FAILED(hr) ) goto Exit;
*phStream = INVALID_HANDLE_VALUE;
// ----------------
// Open the Storage
// ----------------
if( fCreate )
{
hr = StgCreateDocfileOnILockBytes( pnffstm, grfMode, 0, &pstg );
}
else
{
hr = StgOpenStorageOnILockBytes( pnffstm, NULL, grfMode, NULL, 0, &pstg );
// STG_E_INVALIDHEADER in some paths of the above call gets converted into
// STG_E_FILEALREADYEXISTS, which doesn't make a whole lot of sense from
// from our point of view (we already knew it existed, we wanted to open it). So,
// translate it back.
if( STG_E_FILEALREADYEXISTS == hr )
hr = STG_E_INVALIDHEADER;
}
if( FAILED(hr) ) goto Exit;
// QI for the caller-requested IID
hr = pstg->QueryInterface( riid, ppObjectOpen );
if( FAILED(hr) ) goto Exit;
hr = S_OK;
Exit:
RELEASE_INTERFACE(pnffstm);
RELEASE_INTERFACE(pstg);
RELEASE_INTERFACE(pmutex);
return( hr );
}
//+----------------------------------------------------------------------------
//
// CreateOrOpenStorageOnHandle
// StgCreateStorageOnHandle
// StgOpenStorageOnHandle
//
// Given a handle, create or open a storage.
// The caller-provided handle is duplicated.
//
//+----------------------------------------------------------------------------
CreateOrOpenStorageOnHandle( IN BOOL fCreate,
IN DWORD grfMode,
IN DWORD stgfmt,
IN HANDLE hStream,
IN REFIID riid,
OUT void ** ppObjectOpen)
{
HRESULT hr = S_OK;
HANDLE hStreamInternal = INVALID_HANDLE_VALUE;
NTSTATUS status = STATUS_SUCCESS;
BOOL fIsStorageFile = FALSE;
OVERLAPPED olpTemp;
propXTraceStatic( "CreateOrOpenStorageOnHandle" );
ZeroMemory( &olpTemp, sizeof(OVERLAPPED) );
propTraceParameters(( "fCreate=%s, grfMode=%s, stgfmt=0x%x, hStream=%p, riid=%s, ppObjectOpen=%p",
fCreate?"TRUE":"FALSE",
static_cast<const char*>(CStringize(SGrfMode(grfMode))),
stgfmt, hStream,
static_cast<const char*>(CStringize(riid)), ppObjectOpen ));
hr = VerifyPerms (grfMode, TRUE);
if (FAILED(hr))
return hr;
// Make a copy of the handle so that the caller can still call
// CloseHandle.
if( !DuplicateHandle( GetCurrentProcess(), hStream,
GetCurrentProcess(), &hStreamInternal,
0, // dwDesiredAccess, ignored because of DUPLICATE_SAME_ACCESS below
FALSE, // bInheritHandle
DUPLICATE_SAME_ACCESS ))
{
hr = HRESULT_FROM_WIN32(GetLastError());
hStreamInternal = INVALID_HANDLE_VALUE;
goto Exit;
}
// Set up an overlapped structure in preparation to call
// StgIsStorageFileHandle
olpTemp.hEvent = CreateEvent( NULL, // Security Attributes.
TRUE, // Manual Reset Flag.
FALSE, // Inital State = Signaled, Flag.
NULL ); // Name
if( NULL == olpTemp.hEvent )
{
hr = HRESULT_FROM_WIN32( GetLastError() );
goto Exit;
}
// Does this handle represent a docfile?
hr = StgIsStorageFileHandle( hStreamInternal, &olpTemp );
if( HRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION) == hr )
{
// This is the error we get when the handle is to a directory.
// See if that's really the case, and if so assume that this isn't
// a docfile.
// Do not move this in StgIsStorageFileHandle for compatibility
BY_HANDLE_FILE_INFORMATION ByHandleFileInformation;
if( GetFileInformationByHandle( hStreamInternal, &ByHandleFileInformation ))
{
if( FILE_ATTRIBUTE_DIRECTORY & ByHandleFileInformation.dwFileAttributes )
hr = S_FALSE;
}
}
if( FAILED(hr) ) goto Exit;
if( S_OK == hr )
fIsStorageFile = TRUE;
else
DfpAssert( S_FALSE == hr );
// Is this the create of a docfile/storage, or the open of an
// existing docfile?
if( fCreate && ( STGFMT_DOCFILE == stgfmt || STGFMT_STORAGE == stgfmt )
||
!fCreate && fIsStorageFile )
{
// In the open path, the caller must request
// either any, docfile, or storage.
if( !fCreate
&&
STGFMT_ANY != stgfmt
&&
STGFMT_DOCFILE != stgfmt
&&
STGFMT_STORAGE != stgfmt )
{
hr = STG_E_INVALIDPARAMETER;
goto Exit;
}
// Create/Open the docfile. hStreamInternal may be changed
// to INVALID_HANDLE_VALUE.
hr = CreateOrOpenDocfileOnHandle( fCreate, grfMode, &hStreamInternal,
riid, ppObjectOpen );
if( FAILED(hr) ) goto Exit;
}
// Otherwise, this should be the create/open of an NFF
else if( fCreate && STGFMT_FILE == stgfmt
||
!fCreate && !fIsStorageFile )
{
// In the open path, the caller must request either any or file.
if( !fCreate && STGFMT_ANY != stgfmt && STGFMT_FILE != stgfmt )
{
hr = STG_E_INVALIDPARAMETER;
goto Exit;
}
// Instantiate the NFF IStorage.
hr = NFFOpenOnHandle( fCreate, grfMode, STGFMT_FILE,
&hStreamInternal, riid, ppObjectOpen );
if( FAILED(hr) ) goto Exit;
}
else
{
hr = STG_E_INVALIDPARAMETER;
goto Exit;
}
hr = S_OK;
Exit:
if( INVALID_HANDLE_VALUE != hStreamInternal )
CloseHandle( hStreamInternal );
if( NULL != olpTemp.hEvent )
CloseHandle( olpTemp.hEvent );
if( STG_E_INVALIDFUNCTION == hr // This happens e.g. when we try to get NFF propsets on FAT
|| // This happens when we try to read a FAT directory file
HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER) == hr )
{
propSuppressExitErrors();
}
return( hr );
} // CreateOrOpenStorageOnHandle
STDAPI
StgCreateStorageOnHandle( IN HANDLE hStream,
IN DWORD grfMode,
IN DWORD stgfmt,
IN void *reserved1,
IN void *reserved2,
IN REFIID riid,
OUT void **ppObjectOpen )
{
return( CreateOrOpenStorageOnHandle( TRUE, grfMode, stgfmt, hStream, riid, ppObjectOpen ));
}
STDAPI
StgOpenStorageOnHandle( IN HANDLE hStream,
IN DWORD grfMode,
IN void *reserved1,
IN void *reserved2,
IN REFIID riid,
OUT void **ppObjectOpen )
{
return( CreateOrOpenStorageOnHandle( FALSE, grfMode, STGFMT_ANY, hStream, riid, ppObjectOpen ));
}