//+--------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1992.
//
//  File:       expdf.cxx
//
//  Contents:   Exposed DocFile implementation
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------

#include <exphead.cxx>
#pragma hdrstop

#include <expdf.hxx>
#include <expst.hxx>
#include <expiter.hxx>
#include <pbstream.hxx>
#include <lock.hxx>
#include <marshl.hxx>
#include <logfile.hxx>
#include <rpubdf.hxx>
#include <expparam.hxx>

#include <olepfn.hxx>

#include <ole2sp.h>

#if WIN32 == 300
IMPLEMENT_UNWIND(CSafeAccess);
IMPLEMENT_UNWIND(CSafeSem);
#endif

extern WCHAR const wcsContents[];



//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::CExposedDocFile, public
//
//  Synopsis:   Constructor
//
//  Arguments:  [pdf] - Public DocFile
//              [pdfb] - DocFile basis
//              [ppc] - Context
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


CExposedDocFile::CExposedDocFile(CPubDocFile *pdf,
                                 CDFBasis *pdfb,
                                 CPerContext *ppc)
    : CPropertySetStorage(MAPPED_STREAM_QI),
      _PropertyBagEx( DFlagsToMode( pdf->GetDFlags() ))
{
    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::CExposedDocFile("
                "%p, %p, %p)\n", pdf, pdfb, ppc));


    _ppc = ppc;
    _pdf = P_TO_BP(CBasedPubDocFilePtr, pdf);
    _pdfb = P_TO_BP(CBasedDFBasisPtr, pdfb);
    _pdfb->vAddRef();
    _cReferences = 1;
    _sig = CEXPOSEDDOCFILE_SIG;
#if WIN32 >= 300
    _pIAC = NULL;
#endif

    // Initialize CPropertySetStorage and CPropertyBagEx

    CPropertySetStorage::Init( static_cast<IStorage*>(this), static_cast<IBlockingLock*>(this),
                               FALSE ); // fControlLifetimes (=> Don't addref)

    _PropertyBagEx.Init( static_cast<IPropertySetStorage*>(this),   // Not addref-ed
                         static_cast<IBlockingLock*>(this) );       // Not addref-ed


    //
    // CoQueryReleaseObject needs to have the address of the exposed docfiles
    // query interface routine.
    //
    if (adwQueryInterfaceTable[QI_TABLE_CExposedDocFile] == 0)
    {
        adwQueryInterfaceTable[QI_TABLE_CExposedDocFile] =
            **(ULONG_PTR **)((IStorage *)this);
    }

#ifdef COORD
    _ulLock = _cbSizeBase = _cbSizeOrig = 0;
    _sigMSF = 0;
#endif

    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::CExposedDocFile\n"));
}


SCODE CExposedDocFile::InitMarshal(DWORD dwAsyncFlags,
                                   IDocfileAsyncConnectionPoint *pdacp)
{
    return _cpoint.InitMarshal(this, dwAsyncFlags, pdacp);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::~CExposedDocFile, public
//
//  Synopsis:   Destructor
//
//  History:    23-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


CExposedDocFile::~CExposedDocFile(void)
{
    BOOL fClose = FALSE;

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::~CExposedDocFile\n"));
    olAssert(_cReferences == 0);

    //In order to call into the tree, we need to take the mutex.
    //The mutex may get deleted in _ppc->Release(), so we can't
    //release it here.  The mutex actually gets released in
    //CPerContext::Release() or in the CPerContext destructor.

    //If _ppc is NULL, we're partially constructed and don't need to
    //worry.
    SCODE sc;

#if WIN32 >= 300
     if (_pIAC != NULL)
     {
        _pIAC->Release();
        _pIAC = NULL;
     }
#endif

#if !defined(MULTIHEAP)
    // TakeSem and ReleaseSem are moved to the Release Method
    // so that the deallocation for this object is protected
    if (_ppc)
    {
        sc = TakeSem();
        SetWriteAccess();
        olAssert(SUCCEEDED(sc));
    }


#ifdef ASYNC
    IDocfileAsyncConnectionPoint *pdacp = _cpoint.GetMarshalPoint();
#endif
#endif //MULTIHEAP

    if (_pdf)
    {
        // If we're the last reference on a root object
        // we close the context because all children will become
        // reverted so it is no longer necessary
        if (_pdf->GetRefCount() == 1 && _pdf->IsRoot())
            fClose = TRUE;

        _pdf->CPubDocFile::vRelease();
    }
    if (_pdfb)
        _pdfb->CDFBasis::vRelease();
#if !defined(MULTIHEAP)
    if (_ppc)
    {
        if (fClose)
            _ppc->Close();
        if (_ppc->Release() > 0)
            ReleaseSem(sc);
    }
#ifdef ASYNC
    //Mutex has been released, so we can release the connection point
    //  without fear of deadlock.
    if (pdacp != NULL)
        pdacp->Release();
#endif
#endif // MULTIHEAP

    _sig = CEXPOSEDDOCFILE_SIGDEL;
    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::~CExposedDocFile\n"));
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::Release, public
//
//  Synopsis:   Releases resources for a CExposedDocFile
//
//  Returns:    Appropriate status code
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP_(ULONG) CExposedDocFile::Release(void)
{
    LONG lRet;

    olLog(("%p::In  CExposedDocFile::Release()\n", this));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::Release()\n"));

    if (FAILED(Validate()))
        return 0;
    olAssert(_cReferences > 0);
    lRet = InterlockedDecrement(&_cReferences);
    if (lRet == 0)
    {
        SCODE sc = S_OK;

        sc = _PropertyBagEx.Commit( STGC_DEFAULT );
        olAssert(SUCCEEDED(sc));

        sc = _PropertyBagEx.ShutDown();
        olAssert(SUCCEEDED(sc));

#ifdef MULTIHEAP
        CSafeMultiHeap smh(_ppc);
        CPerContext *ppc = _ppc;

        if (_ppc)
        {
            sc = TakeSem();
            SetWriteAccess();
            olAssert(SUCCEEDED(sc));
        }
#ifdef ASYNC
        IDocfileAsyncConnectionPoint *pdacp = _cpoint.GetMarshalPoint();
#endif
        BOOL fClose = (_pdf) && (_pdf->GetRefCount()==1) && _pdf->IsRoot();
#endif //MULTIHEAP
        delete this;
#ifdef MULTIHEAP
        if (ppc)
        {
            if (fClose)
                ppc->Close();
            if (ppc->Release() == 0)
                g_smAllocator.Uninit();
            else
                if (SUCCEEDED(sc)) ppc->UntakeSem(); 
        }
#ifdef ASYNC
        //Mutex has been released, so we can release the connection point
        //  without fear of deadlock.
        if (pdacp != NULL)
            pdacp->Release();
#endif
#endif
    }
    else if (lRet < 0)
        lRet = 0;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::Release %p() => %lu\n", 
                this, lRet));
    olLog(("%p::Out CExposedDocFile::Release().  ret == %lu\n", this, lRet));
    FreeLogFile();
    return (ULONG)lRet;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::CheckCopyTo, private
//
//  Synopsis:   Checks for CopyTo legality
//
//  Returns:    Appropriate status code
//
//  History:    07-Jul-92       DrewB   Created
//
//---------------------------------------------------------------

inline SCODE CExposedDocFile::CheckCopyTo(void)
{
    return _pdfb->GetCopyBase() != NULL &&
        _pdf->IsAtOrAbove(_pdfb->GetCopyBase()) ?
            STG_E_ACCESSDENIED : S_OK;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::ConvertInternalStream, private
//
//  Synopsis:   Converts an internal stream to a storage
//
//  Arguments:  [pwcsName] - Name
//              [pdfExp] - Destination docfile
//
//  Returns:    Appropriate status code
//
//  History:    23-Jun-92       DrewB   Created
//
//---------------------------------------------------------------


static WCHAR const wcsIllegalName[] = {'\\', '\0'};

SCODE CExposedDocFile::ConvertInternalStream(CExposedDocFile *pdfExp)
{
    CPubStream *pstFrom, *pstTo;
    SCODE sc;
    CDfName const dfnIllegal(wcsIllegalName);
    CDfName const dfnContents(wcsContents);

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::ConvertInternalStream(%p)\n",
                pdfExp));
    olChk(_pdf->GetStream(&dfnIllegal, DF_READWRITE | DF_DENYALL, &pstFrom));
    olChkTo(EH_pstFrom, pdfExp->GetPub()->CreateStream(&dfnContents,
                                                       DF_WRITE | DF_DENYALL,
                                                       &pstTo));
    olChkTo(EH_pstTo, CopySStreamToSStream(pstFrom->GetSt(),
                                           pstTo->GetSt()));
    olChkTo(EH_pstTo, _pdf->DestroyEntry(&dfnIllegal, FALSE));
    sc = S_OK;
    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::ConvertInternalStream\n"));
    // Fall through
EH_pstTo:
    pstTo->CPubStream::vRelease();
EH_pstFrom:
    pstFrom->CPubStream::vRelease();
EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::CreateEntry, private
//
//  Synopsis:   Creates elements, used in CreateStream, CreateStorage.
//
//  Arguments:  [pdfn] - Name
//              [dwType] - Entry type
//              [grfMode] - Access mode
//              [ppv] - Object return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppv]
//
//  History:    18-Dec-92       DrewB   Created
//
//----------------------------------------------------------------------------


SCODE CExposedDocFile::CreateEntry(CDfName const *pdfn,
                                   DWORD dwType,
                                   DWORD grfMode,
                                   void **ppv)
{
    SCODE sc;
    SEntryBuffer eb;
    BOOL fRenamed = FALSE;
    CPubStream *pst;
    CExposedStream *pstExp;
    CPubDocFile *pdf;
    CExposedDocFile *pdfExp;

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::CreateEntry:%p("
                "%p, %lX, %lX, %p)\n", this, pdfn, dwType, grfMode, ppv));
    olChk(EnforceSingle(grfMode));

    //  3/11/93 - Demand scratch when opening/creating transacted
    if ((grfMode & STGM_TRANSACTED) == STGM_TRANSACTED)
    {
        olChk(_ppc->GetDirty()->InitScratch());
    }

    if (grfMode & (STGM_CREATE | STGM_CONVERT))
    {
        if (FAILED(sc = _pdf->IsEntry(pdfn, &eb)))
        {
            if (sc != STG_E_FILENOTFOUND)
                olErr(EH_Err, sc);
        }
        else if (eb.dwType == dwType && (grfMode & STGM_CREATE))
            olChk(_pdf->DestroyEntry(pdfn, FALSE));
        else if (eb.dwType == STGTY_STREAM && (grfMode & STGM_CONVERT) &&
                 dwType == STGTY_STORAGE)
        {
            CDfName const dfnIllegal(wcsIllegalName);

            olChk(_pdf->RenameEntry(pdfn, &dfnIllegal));
            fRenamed = TRUE;
        }
        else
            olErr(EH_Err, STG_E_FILEALREADYEXISTS);
    }
    if (REAL_STGTY(dwType) == STGTY_STREAM)
    {
        olChk(_pdf->CreateStream(pdfn, ModeToDFlags(grfMode), &pst));
        olMemTo(EH_pst, pstExp =  new (_pdfb->GetMalloc()) CExposedStream);
        olChkTo(EH_pstExp, pstExp->Init(pst, BP_TO_P(CDFBasis *, _pdfb),
                                        _ppc, NULL));
        _ppc->AddRef();
#ifdef ASYNC
        if (_cpoint.IsInitialized())
        {
            olChkTo(EH_connSt, pstExp->InitConnection(&_cpoint));
        }
#endif
        *ppv = pstExp;
    }
    else
    {
        olAssert(REAL_STGTY(dwType) == STGTY_STORAGE);
        olChk(_pdf->CreateDocFile(pdfn, ModeToDFlags(grfMode), &pdf));
        olMemTo(EH_pdf, pdfExp = new (_pdfb->GetMalloc())
                CExposedDocFile(pdf, BP_TO_P(CDFBasis *, _pdfb), _ppc));
        _ppc->AddRef();

        if (_cpoint.IsInitialized())
        {
            olChkTo(EH_pdfExpInit, pdfExp->InitConnection(&_cpoint));
        }

        // If we've renamed the original stream for conversion, convert
        if (fRenamed)
        {
            olChkTo(EH_pdfExpInit, ConvertInternalStream(pdfExp));
            sc = STG_S_CONVERTED;
        }

        *ppv = pdfExp;
    }
    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::CreateEntry\n"));
    return sc;

 EH_connSt:
    pstExp->Release();
    goto EH_Del;

 EH_pstExp:
    delete pstExp;
 EH_pst:
    pst->CPubStream::vRelease();
    goto EH_Del;

 EH_pdfExpInit:
    pdfExp->Release();
    goto EH_Del;
 EH_pdf:
    pdf->CPubDocFile::vRelease();
    // Fall through
 EH_Del:
    olVerSucc(_pdf->DestroyEntry(pdfn, TRUE));
 EH_Err:
    return sc;
}

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::OpenEntry, private
//
//  Synopsis:   Opens elements, used in OpenStream, OpenStorage.
//
//  Arguments:  [pdfn] - Name
//              [dwType] - Entry type
//              [grfMode] - Access mode
//              [ppv] - Object return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppv]
//
//  History:    18-Dec-92       DrewB   Created
//
//----------------------------------------------------------------------------


SCODE CExposedDocFile::OpenEntry(CDfName const *pdfn,
                                 DWORD dwType,
                                 DWORD grfMode,
                                 void **ppv)
{
    SCODE sc;
    CPubDocFile *pdf;
    CExposedDocFile *pdfExp;
    CPubStream *pst;
    CExposedStream *pstExp;

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::OpenEntry:%p("
                "%p, %lX, %lX, %p)\n", this, pdfn, dwType, grfMode, ppv));
    olChk(EnforceSingle(grfMode));

    //  3/11/93 - Demand scratch when opening/creating transacted
    if ((grfMode & STGM_TRANSACTED) == STGM_TRANSACTED)
    {
        olChk(_ppc->GetDirty()->InitScratch());
    }

    if (REAL_STGTY(dwType) == STGTY_STREAM)
    {
        olChk(_pdf->GetStream(pdfn, ModeToDFlags(grfMode), &pst));
        olMemTo(EH_pst, pstExp = new (_pdfb->GetMalloc()) CExposedStream);
        olChkTo(EH_pstExp, pstExp->Init(pst, BP_TO_P(CDFBasis *, _pdfb),
                                        _ppc, NULL));
        _ppc->AddRef();

        if (_cpoint.IsInitialized())
        {
            olChkTo(EH_connSt, pstExp->InitConnection(&_cpoint));
        }

        *ppv = pstExp;
    }
    else
    {
        olAssert(REAL_STGTY(dwType) == STGTY_STORAGE);
        olChk(_pdf->GetDocFile(pdfn, ModeToDFlags(grfMode), &pdf));
        olMemTo(EH_pdf, pdfExp = new (_pdfb->GetMalloc())
                CExposedDocFile(pdf, BP_TO_P(CDFBasis *, _pdfb), _ppc));
        _ppc->AddRef();

        if (_cpoint.IsInitialized())
        {
            olChkTo(EH_connDf, pdfExp->InitConnection(&_cpoint));
        }

        *ppv = pdfExp;
    }

    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::OpenEntry\n"));
    return S_OK;
 EH_connSt:
    pstExp->Release();
    return sc;

 EH_pstExp:
    delete pstExp;
    //  Fall through to clean up CPubStream
 EH_pst:
    pst->CPubStream::vRelease();
    return sc;

EH_connDf:
    pdfExp->Release();
    return sc;

 EH_pdf:
    pdf->CPubDocFile::vRelease();
    // Fall through
 EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::CreateStream, public
//
//  Synopsis:   Creates a stream
//
//  Arguments:  [pwcsName] - Name
//              [grfMode] - Permissions
//              [reserved1]
//              [reserved2]
//              [ppstm] - Stream return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppstm]
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::CreateStream(WCHAR const *pwcsName,
                                            DWORD grfMode,
                                            DWORD reserved1,
                                            DWORD reserved2,
                                            IStream **ppstm)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    SafeCExposedStream pestm;
    CDfName dfn;

    olDebugOut((DEB_TRACE, "In  CExposedDocFile::CreateStream("
                "%ws, %lX, %lu, %lu, %p)\n", pwcsName, grfMode, reserved1,
                reserved2, ppstm));
    olLog(("%p::In  CExposedDocFile::CreateStream(%ws, %lX, %lu, %lu, %p)\n",
           this, pwcsName, grfMode, reserved1, reserved2, ppstm));

    OL_VALIDATE(CreateStream(pwcsName,
                             grfMode,
                             reserved1,
                             reserved2,
                             ppstm));
    
    olChk(Validate());

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    olChk(CheckCopyTo());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    dfn.Set(pwcsName);
    sc = CreateEntry(&dfn, STGTY_STREAM, grfMode, (void **)&pestm);
    END_PENDING_LOOP;

    if (SUCCEEDED(sc))
        TRANSFER_INTERFACE(pestm, IStream, ppstm);

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::CreateStream => %p\n",
                *ppstm));
 EH_Err:
    olLog(("%p::Out CExposedDocFile::CreateStream().  "
           "*ppstm == %p, ret == %lx\n", this, *ppstm, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::OpenStream, public
//
//  Synopsis:   Opens an existing stream
//
//  Arguments:  [pwcsName] - Name
//              [reserved1]
//              [grfMode] - Permissions
//              [reserved2]
//              [ppstm] - Stream return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppstm]
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::OpenStream(WCHAR const *pwcsName,
                                          void *reserved1,
                                          DWORD grfMode,
                                          DWORD reserved2,
                                          IStream **ppstm)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    SafeCExposedStream pestm;
    CDfName dfn;

    olDebugOut((DEB_TRACE, "In  CExposedDocFile::OpenStream("
                "%ws, %p, %lX, %lu, %p)\n", pwcsName, reserved1,
                grfMode, reserved2, ppstm));
    olLog(("%p::In  CExposedDocFile::OpenStream(%ws, %lu %lX, %lu, %p)\n",
           this, pwcsName, reserved1, grfMode, reserved2, ppstm));

    OL_VALIDATE(OpenStream(pwcsName,
                           reserved1,
                           grfMode,
                           reserved2,
                           ppstm));
    
    olChk(Validate());

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeReadAccess();

    dfn.Set(pwcsName);
    sc = OpenEntry(&dfn, STGTY_STREAM, grfMode, (void **)&pestm);
    END_PENDING_LOOP;

    if (SUCCEEDED(sc))
        TRANSFER_INTERFACE(pestm, IStream, ppstm);

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::OpenStream => %p\n",
                *ppstm));
 EH_Err:
    olLog(("%p::Out CExposedDocFile::OpenStream().  "
           "*ppstm == %p, ret == %lx\n", this, *ppstm, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::CreateStorage, public
//
//  Synopsis:   Creates an embedded DocFile
//
//  Arguments:  [pwcsName] - Name
//              [grfMode] - Permissions
//              [reserved1]
//              [reserved2]
//              [ppstg] - New DocFile return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppstg]
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::CreateStorage(
        WCHAR const *pwcsName,
        DWORD grfMode,
        DWORD reserved1,
        LPSTGSECURITY reserved2,
        IStorage **ppstg)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    SafeCExposedDocFile pedf;
    CDfName dfn;

    olLog(("%p::In  CExposedDocFile::CreateStorage(%ws, %lX, %lu, %lu, %p)\n",
           this, pwcsName, grfMode, reserved1, reserved2, ppstg));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::CreateStorage:%p("
                "%ws, %lX, %lu, %lu, %p)\n", this, pwcsName, grfMode,
                reserved1, reserved2, ppstg));

    OL_VALIDATE(CreateStorage(pwcsName,
                              grfMode,
                              reserved1,
                              reserved2,
                              ppstg));
    
    olChk(Validate());

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    olChk(CheckCopyTo());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    dfn.Set(pwcsName);
    sc = CreateEntry(&dfn, STGTY_STORAGE, grfMode, (void **)&pedf);
    END_PENDING_LOOP;

    if (SUCCEEDED(sc))
        TRANSFER_INTERFACE(pedf, IStorage, ppstg);

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::CreateStorage => %p\n",
                *ppstg));
EH_Err:
    olLog(("%p::Out CExposedDocFile::CreateStorage().  "
           "*ppstg == %p, ret == %lX\n", this, *ppstg, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::OpenStorage, public
//
//  Synopsis:   Gets an existing embedded DocFile
//
//  Arguments:  [pwcsName] - Name
//              [pstgPriority] - Priority reopens
//              [grfMode] - Permissions
//              [snbExclude] - Priority reopens
//              [reserved]
//              [ppstg] - DocFile return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppstg]
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::OpenStorage(WCHAR const *pwcsName,
                                           IStorage *pstgPriority,
                                           DWORD grfMode,
                                           SNBW snbExclude,
                                           DWORD reserved,
                                           IStorage **ppstg)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    SafeCExposedDocFile pdfExp;
    CDfName dfn;

    olLog(("%p::In  CExposedDocFile::OpenStorage(%ws, %p, %lX, %p, %lu, %p)\n",
           this, pwcsName, pstgPriority, grfMode, snbExclude, reserved,
           ppstg));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::OpenStorage:%p("
                "%ws, %p, %lX, %p, %lu, %p)\n", this, pwcsName, pstgPriority,
                grfMode, snbExclude, reserved, ppstg));


    OL_VALIDATE(OpenStorage(pwcsName,
                            pstgPriority,
                            grfMode,
                            snbExclude,
                            reserved,
                            ppstg));
    
    olChk(Validate());
    if (snbExclude != NULL)
        olErr(EH_Err, STG_E_INVALIDPARAMETER);

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeReadAccess();

    dfn.Set(pwcsName);
    sc = OpenEntry(&dfn, STGTY_STORAGE, grfMode, (void **)&pdfExp);
    END_PENDING_LOOP;

    if (SUCCEEDED(sc))
    {
        TRANSFER_INTERFACE(pdfExp, IStorage, ppstg);
    }

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::OpenStorage => %p\n",
                *ppstg));
 EH_Err:
    olLog(("%p::Out CExposedDocFile::OpenStorage().  "
           "*ppstg == %p, ret == %lX\n", this, *ppstg, sc));
    return _OLERETURN(sc);
}

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::MakeCopyFlags, public
//
//  Synopsis:   Translates IID array into bit fields
//
//  Arguments:  [ciidExclude] - Count of IIDs
//              [rgiidExclude] - IIDs not to copy
//
//  Returns:    Appropriate status code
//
//  History:    23-Dec-92       DrewB   Created
//
//----------------------------------------------------------------------------


DWORD CExposedDocFile::MakeCopyFlags(DWORD ciidExclude,
                                     IID const *rgiidExclude)
{
    DWORD dwCopyFlags;

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::MakeCopyFlags(%lu, %p)\n",
                ciidExclude, rgiidExclude));
    // Copy everything by default
    dwCopyFlags = COPY_ALL;
    for (; ciidExclude > 0; ciidExclude--, rgiidExclude++)
        if (IsEqualIID(*rgiidExclude, IID_IStorage))
            dwCopyFlags &= ~COPY_STORAGES;
        else if (IsEqualIID(*rgiidExclude, IID_IStream))
            dwCopyFlags &= ~COPY_STREAMS;
    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::MakeCopyFlags\n"));
    return dwCopyFlags;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::CopyTo, public
//
//  Synopsis:   Makes a copy of a DocFile
//
//  Arguments:  [ciidExclude] - Length of rgiid array
//              [rgiidExclude] - Array of IIDs to exclude
//              [snbExclude] - Names to exclude
//              [pstgDest] - Parent of copy
//
//  Returns:    Appropriate status code
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::CopyTo(DWORD ciidExclude,
                                      IID const *rgiidExclude,
                                      SNBW snbExclude,
                                      IStorage *pstgDest)
{
    SCODE sc;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif

    SAFE_SEM;
    DWORD i;



    olLog(("%p::In  CExposedDocFile::CopyTo(%lu, %p, %p, %p)\n",
           this, ciidExclude, rgiidExclude, snbExclude, pstgDest));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::CopyTo(%lu, %p, %p, %p)\n",
                ciidExclude, rgiidExclude, snbExclude, pstgDest));

    OL_VALIDATE(CopyTo(ciidExclude,
                       rgiidExclude,
                       snbExclude,
                       pstgDest));
    olChk(Validate());

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    olChk(_pdf->CheckReverted());

    olAssert(_pdfb->GetCopyBase() == NULL);
    _pdfb->SetCopyBase(BP_TO_P(CPubDocFile *, _pdf));

    // Flush all descendant property set buffers so that their
    // underlying Streams (which are about to be copied) are
    // up to date.

    SetWriteAccess();
    olChkTo(EH_Loop, _pdf->FlushBufferedData(0));
    ClearWriteAccess();

    // Perform the copy.

    sc = CopyDocFileToIStorage(_pdf->GetDF(), pstgDest, snbExclude,
                               MakeCopyFlags(ciidExclude, rgiidExclude));
EH_Loop:
    _pdfb->SetCopyBase(NULL);
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::CopyTo\n"));
EH_Err:
    _pdfb->SetCopyBase(NULL);
    olLog(("%p::Out ExposedDocFile::CopyTo().  ret == %lX\n", this, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::Commit, public
//
//  Synopsis:   Commits transacted changes
//
//  Arguments:  [dwFlags] - DFC_*
//
//  Returns:    Appropriate status code
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::Commit(DWORD dwFlags)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;

    olLog(("%p::In  CExposedDocFile::Commit(%lX)\n",this, dwFlags));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::Commit(%lX)\n", dwFlags));

    OL_VALIDATE(Commit(dwFlags));
    olChk(Validate());

    BEGIN_PENDING_LOOP;

    olChk( _PropertyBagEx.Commit( dwFlags ));
    olChk(TakeSafeSem());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    if (_pdf->GetTransactedDepth() <= 1) // topmost transacted level or direct
    {
        if (_pdfb->DirectWriterMode() && (*_ppc->GetRecursionCount()) == 0)
            olChk(STG_E_ACCESSDENIED);
    }
#endif

    olChkTo(EH_Loop, _pdf->Commit(dwFlags));

#if WIN32 >= 300
    if (SUCCEEDED(sc) && _pIAC != NULL)
        sc = _pIAC->CommitAccessRights(0);
#endif

EH_Loop:
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::Commit\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::Commit().  ret == %lx\n",this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::Revert, public
//
//  Synopsis:   Reverts transacted changes
//
//  Returns:    Appropriate status code
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::Revert(void)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;

    olLog(("%p::In  CExposedDocFile::Revert()\n", this));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::Revert\n"));

    OL_VALIDATE(Revert());
    olChk(Validate());

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeWriteAccess();

    olChkTo(EH_Loop, _pdf->Revert());

#if WIN32 >= 300
    if (SUCCEEDED(sc) && _pIAC != NULL)
        sc = _pIAC->RevertAccessRights();
#endif
EH_Loop:
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::Revert\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::Revert().  ret == %lx\n", this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::EnumElements, public
//
//  Synopsis:   Starts an iterator
//
//  Arguments:  [reserved1]
//              [reserved2]
//              [reserved3]
//              [ppenm] - Enumerator return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppenm]
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::EnumElements(DWORD reserved1,
                                           void *reserved2,
                                           DWORD reserved3,
                                           IEnumSTATSTG **ppenm)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    SafeCExposedIterator pdiExp;
    CDfName dfnTmp;

    olLog(("%p::In  CExposedDocFile::EnumElements(%lu, %p, %lu, %p)\n",
           this, reserved1, reserved2, reserved3, ppenm));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::EnumElements(%p)\n",
                ppenm));

    OL_VALIDATE(EnumElements(reserved1,
                             reserved2,
                             reserved3,
                             ppenm));

    olChk(Validate());

    //ASYNC Note:  It doesn't appear that there's any way that this
    //  function can fail with STG_E_PENDING, so we don't need a pending
    //  loop here.
    olChk(TakeSafeSem());
    if (!P_READ(_pdf->GetDFlags()))
        olErr(EH_Err, STG_E_ACCESSDENIED);
    olChk(_pdf->CheckReverted());
    SafeReadAccess();

    pdiExp.Attach(new CExposedIterator(BP_TO_P(CPubDocFile *, _pdf),
                                       &dfnTmp,
                                       BP_TO_P(CDFBasis *, _pdfb),
                                       _ppc));
    olMem((CExposedIterator *)pdiExp);
    _ppc->AddRef();
#ifdef ASYNC
    if (_cpoint.IsInitialized())
    {
        olChkTo(EH_Exp, pdiExp->InitConnection(&_cpoint));
    }
#endif
    TRANSFER_INTERFACE(pdiExp, IEnumSTATSTG, ppenm);

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::EnumElements => %p\n",
                *ppenm));
EH_Err:
    olLog(("%p::Out CExposedDocFile::EnumElements().  ret == %lx\n",this, sc));
    return ResultFromScode(sc);
EH_Exp:
    pdiExp->Release();
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::DestroyElement, public
//
//  Synopsis:   Permanently deletes an element of a DocFile
//
//  Arguments:  [pwcsName] - Name of element
//
//  Returns:    Appropriate status code
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::DestroyElement(WCHAR const *pwcsName)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    CDfName dfn;

    olLog(("%p::In  CExposedDocFile::DestroyElement(%ws)\n", this, pwcsName));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::DestroyElement(%ws)\n",
                pwcsName));

    OL_VALIDATE(DestroyElement(pwcsName));
    
    olChk(Validate());
    dfn.Set(pwcsName);

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    sc = _pdf->DestroyEntry(&dfn, FALSE);
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::DestroyElement\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::DestroyElement().  ret == %lx\n",
           this, sc));
    return _OLERETURN(sc);
}

_OLESTDMETHODIMP CExposedDocFile::MoveElementTo(WCHAR const *pwcsName,
                                             IStorage *pstgParent,
                                             OLECHAR const *ptcsNewName,
                                             DWORD grfFlags)
{
    SCODE sc;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif
    olLog(("%p::In  CExposedDocFile::MoveElementTo("
           "%ws, %p, " OLEFMT ", %lu)\n",
           this, pwcsName, pstgParent, ptcsNewName, grfFlags));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::MoveElementTo("
                "%ws, %p, " OLEFMT ", %lu)\n",
                pwcsName, pstgParent, ptcsNewName, grfFlags));

    OL_VALIDATE(MoveElementTo(pwcsName,
                              pstgParent,
                              ptcsNewName,
                              grfFlags));
    
    olChk(Validate());

#ifdef ASYNC
    //ASYNC Note:  We don't use the normal pending loop macros here because
    //  we have no safe sem and need to pass a NULL.
    do
    {
#endif
        sc = MoveElementWorker(pwcsName,
                               pstgParent,
                               ptcsNewName,
                               grfFlags);
#ifdef ASYNC
        if (!ISPENDINGERROR(sc))
        {
            break;
        }
        else
        {
            SCODE sc2;
            sc2 = _cpoint.Notify(sc,
                                 _ppc->GetBase(),
                                 _ppc,
                                 NULL);
            if (sc2 != S_OK)
            {
                return ResultFromScode(sc2);
            }
        }
    } while (TRUE);
#endif
EH_Err:
    olLog(("%p::Out CExposedDocFile::MoveElementTo().  ret == %lx\n",
           this, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::MoveElementToWorker, public
//
//  Synopsis:   Move an element of a DocFile to an IStorage
//
//  Arguments:  [pwcsName] - Current name
//              [ptcsNewName] - New name
//
//  Returns:    Appropriate status code
//
//  Algorithm:  Open source as storage or stream (whatever works)
//              Create appropriate destination
//              Copy source to destination
//              Set create time of destination equal to create time of source
//              If appropriate, delete source
//
//  History:    10-Nov-92       AlexT   Created
//
//---------------------------------------------------------------


SCODE CExposedDocFile::MoveElementWorker(WCHAR const *pwcsName,
                                         IStorage *pstgParent,
                                         OLECHAR const *ptcsNewName,
                                         DWORD grfFlags)
{
    IUnknown *punksrc = NULL;
    SCODE sc;
    IUnknown *punkdst = NULL;
    IStorage *pstgsrc;
    STATSTG statstg;
    BOOL fCreate = TRUE;
    
    // Determine source type
    sc = GetScode(OpenStorage(pwcsName, NULL,
                              STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
                              NULL, NULL, &pstgsrc));
    if (SUCCEEDED(sc))
    {
        HRESULT hr;

        //  It's a storage
        punksrc = pstgsrc;

        IStorage *pstgdst;
        olHChkTo(EH_UnkSrc, pstgsrc->Stat(&statstg, STATFLAG_NONAME));

        hr = pstgParent->CreateStorage(ptcsNewName,
                                       STGM_DIRECT | STGM_WRITE |
                                       STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE,
                                       0, 0, &pstgdst);
        if (DfGetScode(hr) == STG_E_FILEALREADYEXISTS &&
            grfFlags == STGMOVE_COPY)
        {
            fCreate = FALSE;
            //If we're opening an existing thing for merging, we need
            //  read and write permissions so we can traverse the tree.
            hr = pstgParent->OpenStorage(ptcsNewName, NULL,
                                         STGM_DIRECT | STGM_READWRITE |
                                         STGM_SHARE_EXCLUSIVE, NULL,
                                         0, &pstgdst);
        }
        olHChkTo(EH_UnkSrc, hr);

        punkdst = pstgdst;

        sc = DfGetScode(pstgsrc->CopyTo(0, NULL, NULL, pstgdst));
    }
    else if (sc == STG_E_FILENOTFOUND)
    {
        //  Try opening it as a stream
        HRESULT hr;
        
        IStream *pstmsrc, *pstmdst;
        olHChk(OpenStream(pwcsName, NULL,
                          STGM_DIRECT | STGM_READ | STGM_SHARE_EXCLUSIVE,
                          NULL, &pstmsrc));

        //  It's a stream
        punksrc = pstmsrc;

        olHChkTo(EH_UnkSrc, pstmsrc->Stat(&statstg, STATFLAG_NONAME));

        hr = pstgParent->CreateStream(ptcsNewName,
                                      STGM_DIRECT | STGM_WRITE |
                                      STGM_SHARE_EXCLUSIVE |
                                      STGM_FAILIFTHERE,
                                      0, 0, &pstmdst);
        if (DfGetScode(hr) == STG_E_FILEALREADYEXISTS &&
            grfFlags == STGMOVE_COPY)
        {
            ULARGE_INTEGER uli;
            uli.QuadPart = 0;
            
            fCreate = FALSE;
            //Try to open it instead
            //Note:  We do this instead of doing a CreateStream with
            //  STGM_CREATE because CreateStream can open over an already
            //  open stream, which leads to problems when the destination
            //  and the source are the same.
            olHChkTo(EH_UnkSrc, pstgParent->OpenStream(ptcsNewName,
                                                       0,
                                                       STGM_DIRECT |
                                                       STGM_WRITE |
                                                       STGM_SHARE_EXCLUSIVE,
                                                       0,
                                                       &pstmdst));
            sc = pstmdst->SetSize(uli);
            if (FAILED(sc))
            {
                pstmdst->Release();
                olErr(EH_UnkSrc, sc);
            }
        }
        else
            olHChkTo(EH_UnkSrc, hr);

        punkdst = pstmdst;

        ULARGE_INTEGER cb;
        ULISetLow (cb, 0xffffffff);
        ULISetHigh(cb, 0xffffffff);
        sc = DfGetScode(pstmsrc->CopyTo(pstmdst, cb, NULL, NULL));
    }
    else
        olChk(sc);

    punkdst->Release();

    if (SUCCEEDED(sc))
    {
        //  Make destination create time match source create time
        //  Note that we don't really care if this call succeeded.

        pstgParent->SetElementTimes(ptcsNewName, &statstg.ctime,
                                    NULL, NULL);

	//OK to ignore failure from DestroyElement
        if ((grfFlags & STGMOVE_COPY) == STGMOVE_MOVE)
            DestroyElement(pwcsName);
    }
    else
    {
        //  The copy/move failed, so get rid of the partial result.
        //Only do a delete if the object was newly created.
        if (fCreate)
            pstgParent->DestroyElement(ptcsNewName);
    }

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::MoveElementTo\n"));
    // Fall through
EH_UnkSrc:
    if (punksrc)
        punksrc->Release();
EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::RenameElement, public
//
//  Synopsis:   Renames an element of a DocFile
//
//  Arguments:  [pwcsName] - Current name
//              [pwcsNewName] - New name
//
//  Returns:    Appropriate status code
//
//  History:    20-Jan-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::RenameElement(WCHAR const *pwcsName,
                                             WCHAR const *pwcsNewName)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    CDfName dfnOld, dfnNew;

    olLog(("%p::In  CExposedDocFile::RenameElement(%ws, %ws)\n",
           this, pwcsName, pwcsNewName));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::RenameElement(%ws, %ws)\n",
               pwcsName, pwcsNewName));

    OL_VALIDATE(RenameElement(pwcsName,
                              pwcsNewName));
    
    olChk(Validate());
    dfnOld.Set(pwcsName);
    dfnNew.Set(pwcsNewName);

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    sc = _pdf->RenameEntry(&dfnOld, &dfnNew);
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::RenameElement\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::RenameElement().  ret == %lx\n",
           this, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::SetElementTimes, public
//
//  Synopsis:   Sets element time stamps
//
//  Arguments:  [pwcsName] - Name
//              [pctime] - create time
//              [patime] - access time
//              [pmtime] - modify time
//
//  Returns:    Appropriate status code
//
//  History:    05-Oct-92       AlexT   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::SetElementTimes(WCHAR const *pwcsName,
                                               FILETIME const *pctime,
                                               FILETIME const *patime,
                                               FILETIME const *pmtime)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    CDfName dfn;
    CDfName *pdfn = NULL;
    FILETIME ctime, atime, mtime;

    olLog(("%p::In  CExposedDocFile::SetElementTimes(%ws, %p, %p, %p)\n",
           this, pwcsName, pctime, patime, pmtime));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::SetElementTimes:%p("
                "%ws, %p, %p, %p)\n", this, pwcsName, pctime, patime, pmtime));

    OL_VALIDATE(SetElementTimes(pwcsName,
                                pctime,
                                patime,
                                pmtime));
    olChk(Validate());
    // Probe arguments and make local copies if necessary
    if (pctime)
    {
        ctime = *pctime;
        pctime = &ctime;
    }
    if (patime)
    {
        atime = *patime;
        patime = &atime;
    }
    if (pmtime)
    {
        mtime = *pmtime;
        pmtime = &mtime;
    }

    if (pwcsName != NULL)
    {
        dfn.Set(pwcsName);
        pdfn = &dfn;
    }

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    sc = _pdf->SetElementTimes(pdfn, pctime, patime, pmtime);
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::SetElementTimes\n"));
 EH_Err:
    olLog(("%p::Out CExposedDocFile::SetElementTimes().  ret == %lx\n",
           this, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::SetClass, public
//
//  Synopsis:   Sets storage class
//
//  Arguments:  [clsid] - class id
//
//  Returns:    Appropriate status code
//
//  History:    05-Oct-92       AlexT   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::SetClass(REFCLSID rclsid)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;
    CLSID clsid;

    olLog(("%p::In  CExposedDocFile::SetClass(?)\n", this));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::SetClass:%p(?)\n", this));

    OL_VALIDATE(SetClass(rclsid));
    olChk(Validate());
    clsid = rclsid;

    BEGIN_PENDING_LOOP;
        olChk(TakeSafeSem());
        SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
        olChk(ValidateWriteAccess());
#endif

        sc = _pdf->SetClass(clsid);
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::SetClass\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::SetClass().  ret == %lx\n",
           this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::SetStateBits, public
//
//  Synopsis:   Sets state bits
//
//  Arguments:  [grfStateBits] - state bits
//              [grfMask] - state bits mask
//
//  Returns:    Appropriate status code
//
//  History:    05-Oct-92       AlexT   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::SetStateBits(DWORD grfStateBits, DWORD grfMask)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;

    olLog(("%p::In  CExposedDocFile::SetStateBits(%lu, %lu)\n", this,
           grfStateBits, grfMask));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::SetStateBits:%p("
                "%lu, %lu)\n", this, grfStateBits, grfMask));

    OL_VALIDATE(SetStateBits(grfStateBits, grfMask));
    
    olChk(Validate());

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeWriteAccess();

#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    sc = _pdf->SetStateBits(grfStateBits, grfMask);
    END_PENDING_LOOP;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::SetStateBits\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::SetStateBits().  ret == %lx\n",
           this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::Stat, public
//
//  Synopsis:   Fills in a buffer of information about this object
//
//  Arguments:  [pstatstg] - Buffer
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pstatstg]
//
//  History:    24-Mar-92       DrewB   Created
//
//---------------------------------------------------------------


_OLESTDMETHODIMP CExposedDocFile::Stat(STATSTGW *pstatstg, DWORD grfStatFlag)
{
    SAFE_SEM;
    SAFE_ACCESS;
    SCODE sc;
    STATSTGW stat;

    olLog(("%p::In  CExposedDocFile::Stat(%p)\n", this, pstatstg));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::Stat(%p)\n", pstatstg));

    OL_VALIDATE(Stat(pstatstg, grfStatFlag));

    BEGIN_PENDING_LOOP;
    olChk(TakeSafeSem());
    SafeReadAccess();

    sc = _pdf->Stat(&stat, grfStatFlag);
    END_PENDING_LOOP;

    if (SUCCEEDED(sc))
    {
        TRY
        {
            *pstatstg = stat;
            pstatstg->type = STGTY_STORAGE;
            ULISet32(pstatstg->cbSize, 0);
            pstatstg->grfLocksSupported = 0;
            pstatstg->STATSTG_dwStgFmt = 0;
        }
        CATCH(CException, e)
        {
            UNREFERENCED_PARM(e);
            if (stat.pwcsName)
                TaskMemFree(stat.pwcsName);
        }
        END_CATCH
    }

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::Stat\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::Stat().  *pstatstg == %p  ret == %lx\n",
           this, *pstatstg, sc));
    return _OLERETURN(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::AddRef, public
//
//  Synopsis:   Increments the ref count
//
//  Returns:    Appropriate status code
//
//  History:    16-Mar-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP_(ULONG) CExposedDocFile::AddRef(void)
{
    ULONG ulRet;

    olLog(("%p::In  CExposedDocFile::AddRef()\n", this));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::AddRef()\n"));

    if (FAILED(Validate()))
        return 0;
    InterlockedIncrement(&_cReferences);
    ulRet = _cReferences;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::AddRef %p()=> %lu\n",
                this, _cReferences));
    olLog(("%p::Out CExposedDocFile::AddRef().  ret == %lu\n", this, ulRet));
    return ulRet;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::QueryInterface, public
//
//  Synopsis:   Returns an object for the requested interface
//
//  Arguments:  [iid] - Interface ID
//              [ppvObj] - Object return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvObj]
//
//  History:    26-Mar-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif

    olLog(("%p::In  CExposedDocFile::QueryInterface(?, %p)\n",
           this, ppvObj));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::QueryInterface(?, %p)\n",
                ppvObj));

    OL_VALIDATE(QueryInterface(iid, ppvObj));

    olChk(Validate());
    olChk(_pdf->CheckReverted());

    sc = S_OK;
    if (IsEqualIID(iid, IID_IStorage) || IsEqualIID(iid, IID_IUnknown))
    {
        *ppvObj = (IStorage *)this;
        CExposedDocFile::AddRef();
    }
    else if (IsEqualIID(iid, IID_IMarshal))
    {
        if (P_PRIORITY(_pdf->GetDFlags()) && _pdf->IsRoot())
            olErr(EH_Err, E_NOINTERFACE);

        //If the ILockBytes we'd need to marshal doesn't support IMarshal
        //  then we want to do standard marshalling on the storage, mostly
        //  to prevent deadlock problems but also because you'll get better
        //  performance.  So check, then do the right thing.

        IMarshal *pim;
        ILockBytes *plkb;
        plkb = _ppc->GetOriginal();
        if (plkb == NULL)
        {
            plkb = _ppc->GetBase();
        }

        sc = plkb->QueryInterface(IID_IMarshal, (void **)&pim);
        if (FAILED(sc))
        {
            olErr(EH_Err, E_NOINTERFACE);
        }
        pim->Release();

#ifdef MULTIHEAP
        if (_ppc->GetHeapBase() == NULL)
            olErr (EH_Err, E_NOINTERFACE);
#endif

        *ppvObj = (IMarshal *)this;
        CExposedDocFile::AddRef();
    }
    else if (IsEqualIID(iid, IID_IRootStorage))
    {
#ifdef COORD
        if ((!_pdf->IsRoot()) && (!_pdf->IsCoord()))
#else
        if (!_pdf->IsRoot())
#endif
            olErr(EH_Err, E_NOINTERFACE);
        *ppvObj = (IRootStorage *)this;
        CExposedDocFile::AddRef();
    }
#ifdef NEWPROPS
    else if (IsEqualIID(iid, IID_IPropertySetStorage))
    {
        *ppvObj = (IPropertySetStorage *)this;
        CExposedDocFile::AddRef();
    }

#endif
#ifdef ASYNC
    else if (IsEqualIID(iid, IID_IConnectionPointContainer) &&
              _cpoint.IsInitialized())
    {
        *ppvObj = (IConnectionPointContainer *)this;
        CExposedDocFile::AddRef();
    }
#endif
#if WIN32 >= 300
    else if (_pdf->IsRoot() && IsEqualIID(iid, IID_IAccessControl))
    {
        ILockBytes *piLB = _pdf->GetBaseMS()->GetILB();
        olAssert((piLB != NULL));
        SCODE scode = S_OK;
        if (_pIAC == NULL)  // use existing _pIAC if available
            scode = piLB->QueryInterface(IID_IAccessControl,(void **)&_pIAC);
        if (SUCCEEDED(scode))
        {
            *ppvObj = (IAccessControl *)this;
            CExposedDocFile::AddRef();
        }
        else sc = E_NOINTERFACE;
   }
#endif
#ifdef DIRECTWRITERLOCK
    else if (_pdf->IsRoot() && IsEqualIID(iid, IID_IDirectWriterLock) &&
             _pdfb->DirectWriterMode())
    {
        *ppvObj = (IDirectWriterLock *) this;
        CExposedDocFile::AddRef();
    }
#endif // DIRECTWRITERLOCK

    else if( IsEqualIID( iid, IID_IPropertyBagEx ))
    {
        *ppvObj = static_cast<IPropertyBagEx*>(&_PropertyBagEx);
        CExposedDocFile::AddRef();
    }
    else if( IsEqualIID( iid, IID_IPropertyBag ))
    {
        *ppvObj = static_cast<IPropertyBag*>(&_PropertyBagEx);
        CExposedDocFile::AddRef();
    }

    else
        sc = E_NOINTERFACE;

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::QueryInterface => %p\n",
                *ppvObj));
EH_Err:
    olLog(("%p::Out CExposedDocFile::QueryInterface().  "
           "*ppvObj == %p  ret == %lx\n", this, *ppvObj, sc));
    return ResultFromScode(sc);
}


//+--------------------------------------------------------------
//
//  Method:     CExposedDocFile::CopySStreamToIStream, private
//
//  Synopsis:   Copies a PSStream to an IStream
//
//  Arguments:  [psstFrom] - SStream
//              [pstTo] - IStream
//
//  Returns:    Appropriate status code
//
//  History:    07-May-92       DrewB   Created
//              26-Jun-92       AlexT   Moved to CExposedDocFile
//                                      so we can call SetReadAccess
//
//---------------------------------------------------------------


SCODE CExposedDocFile::CopySStreamToIStream(PSStream *psstFrom,
                                            IStream *pstTo)
{
    BYTE *pbBuffer;
    SCODE sc;
#ifdef LARGE_STREAMS
    ULONGLONG cbPos;
    ULONG cbRead, cbWritten;
#else
    ULONG cbRead, cbWritten, cbPos, cbSizeLow;
#endif
    ULONG ulBufferSize;
    ULARGE_INTEGER cbSize;
    cbSize.QuadPart = 0;

    ulBufferSize = (_pdfb->GetOpenFlags() & DF_LARGE) ?
                    LARGESTREAMBUFFERSIZE : STREAMBUFFERSIZE;

    // This is part of CopyTo and therefore we are allowed to
    // fail with out-of-memory
    olChk(GetBuffer(STREAMBUFFERSIZE, ulBufferSize, &pbBuffer, &ulBufferSize));

    // Set destination size for contiguity
    SetReadAccess();
#ifdef LARGE_STREAMS
    psstFrom->GetSize(&cbSize.QuadPart);
#else
    psstFrom->GetSize(&cbSize.LowPart);
    cbSize.HighPart = 0;
#endif
    ClearReadAccess();

    //  Don't need to SetReadAccess here because pstTo is an IStream
    olHChk(pstTo->SetSize(cbSize));
    // Copy between streams
    cbPos = 0;
    for (;;)
    {
        SetReadAccess();
        olChk(psstFrom->ReadAt(cbPos, pbBuffer, ulBufferSize,
                               (ULONG STACKBASED *)&cbRead));
        ClearReadAccess();
        if (cbRead == 0) // EOF
            break;

        //  Don't need to SetReadAccess here because pstTo is an IStream
        olHChk(pstTo->Write(pbBuffer, cbRead, &cbWritten));
        if (cbRead != cbWritten)
            olErr(EH_Err, STG_E_WRITEFAULT);
        cbPos += cbWritten;
    }
    sc = S_OK;

EH_Err:
    DfMemFree(pbBuffer);
    return sc;
}


//+--------------------------------------------------------------
//
//  Method:     CExposedDocFile::CopyDocFileToIStorage, private
//
//  Synopsis:   Copies a docfile's contents to an IStorage
//
//  Arguments:  [pdfFrom] - From
//              [pstgTo] - To
//              [snbExclude] - Names to not copy
//              [dwCopyFlags] - Bitwise flags for types of objects to copy
//
//  Returns:    Appropriate status code
//
//  History:    07-May-92       DrewB   Created
//              26-Jun-92       AlexT   Moved to CExposedDocFile
//                                      so we can call SetReadAccess
//
//---------------------------------------------------------------


// Variables used by CopyDocFileToIStorage that we
// want to allocate dynamically rather than eating stack space
struct SCopyVars : public CLocalAlloc
{
    PSStream *psstFrom;
    IStream *pstTo;
    PDocFile *pdfFromChild;
    IStorage *pstgToChild;
    DWORD grfStateBits;
    CLSID clsid;
    CDfName dfnKey;
    SIterBuffer ib;
    OLECHAR atcName[CWCSTORAGENAME];
};

SCODE CExposedDocFile::CopyDocFileToIStorage(PDocFile *pdfFrom,
                                             IStorage *pstgTo,
                                             SNBW snbExclude,
                                             DWORD dwCopyFlags)
{
    SCODE sc;
    SCopyVars *pcv = NULL;

    olDebugOut((DEB_ITRACE, "In  CopyDocFileToIStorage:%p(%p, %p, %p, %lX)\n",
                this, pdfFrom, pstgTo, snbExclude, dwCopyFlags));

    // Allocate variables dynamically to conserve stack space since
    // this is a recursive call
    olMem(pcv = new SCopyVars);

    SetReadAccess();
    sc = pdfFrom->GetClass(&pcv->clsid);
    ClearReadAccess();
    olChk(sc);

    // Assume STG_E_INVALIDFUNCTION means that the destination storage
    // doesn't support class IDs
    sc = GetScode(pstgTo->SetClass(pcv->clsid));
    if (FAILED(sc) && sc != STG_E_INVALIDFUNCTION)
        olErr(EH_Err, sc);

    SetReadAccess();
    sc = pdfFrom->GetStateBits(&pcv->grfStateBits);
    ClearReadAccess();
    olChk(sc);

    sc = GetScode(pstgTo->SetStateBits(pcv->grfStateBits, 0xffffffff));
    if (FAILED(sc) && sc != STG_E_INVALIDFUNCTION)
        olErr(EH_Err, sc);

    for (;;)
    {
        SetReadAccess();
        sc = pdfFrom->FindGreaterEntry(&pcv->dfnKey, &pcv->ib, NULL);
        ClearReadAccess();

        if (sc == STG_E_NOMOREFILES)
            break;
        else if (FAILED(sc))
            olErr(EH_pdfi, sc);
        pcv->dfnKey.Set(&pcv->ib.dfnName);

        if (snbExclude && NameInSNB(&pcv->ib.dfnName, snbExclude) == S_OK)
            continue;

        if ((pcv->ib.type == STGTY_STORAGE &&
             (dwCopyFlags & COPY_STORAGES) == 0) ||
            (pcv->ib.type == STGTY_STREAM &&
             (dwCopyFlags & COPY_STREAMS) == 0))
            continue;

        switch(pcv->ib.type)
        {
        case STGTY_STORAGE:
            // Embedded DocFile, create destination and recurse

            SetReadAccess();
            sc = pdfFrom->GetDocFile(&pcv->ib.dfnName, DF_READ,
                                     pcv->ib.type, &pcv->pdfFromChild);
            ClearReadAccess();
            olChkTo(EH_pdfi, sc);
            // Not optimally efficient, but reduces #ifdef's
            StringCbCopyW (pcv->atcName, sizeof(pcv->atcName), (WCHAR *)pcv->ib.dfnName.GetBuffer());

            //  Don't need to SetReadAccess here because pstgTo is an IStorage.

#ifdef MULTIHEAP
            {
                CSafeMultiHeap smh(_ppc);
                // if pstgTo is an IStorage proxy, then returned IStorage
                // can be custom marshaled and allocator state is lost
#endif
            sc = DfGetScode(pstgTo->CreateStorage(pcv->atcName, STGM_WRITE |
                                                  STGM_SHARE_EXCLUSIVE |
                                                  STGM_FAILIFTHERE,
                                                  0, 0, &pcv->pstgToChild));

            if (sc == STG_E_FILEALREADYEXISTS)
                //We need read and write permissions so we can traverse
                //  the destination IStorage
                olHChkTo(EH_Get, pstgTo->OpenStorage(pcv->atcName, NULL,
                                                     STGM_READWRITE |
                                                     STGM_SHARE_EXCLUSIVE,
                                                     NULL, 0,
                                                     &pcv->pstgToChild));
            else if (FAILED(sc))
                olErr(EH_Get, sc);
#ifdef MULTIHEAP
            }
#endif
            olChkTo(EH_Create,
                  CopyDocFileToIStorage(pcv->pdfFromChild, pcv->pstgToChild,
                                        NULL, dwCopyFlags));
            pcv->pdfFromChild->Release();
            pcv->pstgToChild->Release();
            break;

        case STGTY_STREAM:
            SetReadAccess();
            sc = pdfFrom->GetStream(&pcv->ib.dfnName, DF_READ,
                                    pcv->ib.type, &pcv->psstFrom);
            ClearReadAccess();
            olChkTo(EH_pdfi, sc);
            // Not optimally efficient, but reduces #ifdef's
            StringCbCopyW (pcv->atcName, sizeof(pcv->atcName), (WCHAR *)pcv->ib.dfnName.GetBuffer());

            //  Don't need to SetReadAccess here because pstgTo is an IStorage.

#ifdef MULTIHEAP
            {
                CSafeMultiHeap smh(_ppc);
                // if pstgTo is an IStorage proxy, then returned IStream
                // can be custom marshaled and allocator state is lost
#endif
            olHChkTo(EH_Get,
                     pstgTo->CreateStream(pcv->atcName, STGM_WRITE |
                                          STGM_SHARE_EXCLUSIVE |
                                          STGM_CREATE,
                                          0, 0, &pcv->pstTo));
#ifdef MULTIHEAP
            }
#endif
            olChkTo(EH_Create,
                    CopySStreamToIStream(pcv->psstFrom, pcv->pstTo));
            pcv->psstFrom->Release();
            pcv->pstTo->Release();
            break;

        default:
            olAssert(!aMsg("Unknown type in CopyDocFileToIStorage"));
            break;
        }
    }
    olDebugOut((DEB_ITRACE, "Out CopyDocFileToIStorage\n"));
    sc = S_OK;

 EH_pdfi:
 EH_Err:
    delete pcv;
    return sc;

 EH_Create:
    if (pcv->ib.type == STGTY_STORAGE)
        pcv->pstgToChild->Release();
    else
        pcv->pstTo->Release();
    olVerSucc(pstgTo->DestroyElement(pcv->atcName));
 EH_Get:
    if (pcv->ib.type == STGTY_STORAGE)
        pcv->pdfFromChild->Release();
    else
        pcv->psstFrom->Release();
    goto EH_Err;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::Unmarshal, public
//
//  Synopsis:   Creates a duplicate DocFile from parts
//
//  Arguments:  [pstm] - Marshal stream
//              [ppv] - Object return
//              [mshlflags] - Marshal flags
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppv]
//
//  History:    26-Feb-92       DrewB   Created
//
//---------------------------------------------------------------


SCODE CExposedDocFile::Unmarshal(IStream *pstm,
                                 void **ppv,
                                 DWORD mshlflags)
{
    SCODE sc;
    CDfMutex mtx;
    CPerContext *ppc;
    CPubDocFile *pdf;
    CGlobalContext *pgc;
    CDFBasis *pdfb;
    CExposedDocFile *pedf;
    IStorage *pstgStd = NULL;
    ULONG_PTR df;
    
#ifdef ASYNC
    DWORD dwAsyncFlags;
    IDocfileAsyncConnectionPoint *pdacp;
#endif
#ifdef POINTER_IDENTITY
    CMarshalList *pml;
#endif

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::Unmarshal(%p, %p)\n",
                pstm, ppv));

#ifdef MULTIHEAP
    void *pvBaseOld;
    void *pvBaseNew;
    ContextId cntxid;
    CPerContext pcSharedMemory (NULL);          // bootstrap object
#endif

    //First unmarshal the standard marshalled version
    sc = CoUnmarshalInterface(pstm, IID_IStorage, (void**)&pstgStd);
    if (FAILED(sc))
    {
        // assume that entire standard marshaling stream has been read
        olAssert (pstgStd == NULL);
        sc = S_OK;
    }
    
#ifdef MULTIHEAP
    sc = UnmarshalSharedMemory(pstm, mshlflags, &pcSharedMemory, &cntxid);
    if (!SUCCEEDED(sc))
    {
#ifdef POINTER_IDENTITY
        UnmarshalPointer(pstm, (void **) &pedf);
#endif
        UnmarshalPointer(pstm, (void **)&pdf);
        UnmarshalPointer(pstm, (void **)&pdfb);
        UnmarshalPointer(pstm, (void **)&df);
#ifdef ASYNC
        ReleaseContext(pstm, TRUE, P_INDEPENDENT(df) != 0, mshlflags);
        ReleaseConnection(pstm, mshlflags);
#else
        ReleaseContext(pstm, P_INDEPENDENT(df), mshlflags);
#endif
        olChkTo(EH_std, sc);
    }
    pvBaseOld = DFBASEPTR;
#endif
#ifdef POINTER_IDENTITY
    olChkTo(EH_mem, UnmarshalPointer(pstm, (void **) &pedf));
#endif
    olChkTo(EH_mem, UnmarshalPointer(pstm, (void **)&pdf));
    olChkTo(EH_mem, CPubDocFile::Validate(pdf));
    olChkTo(EH_pdf, UnmarshalPointer(pstm, (void **)&pdfb));
    olChkTo(EH_pdfb, UnmarshalPointer(pstm, (void **) &df));
    olChkTo(EH_pdfb, UnmarshalPointer(pstm, (void **)&pgc));
    olChkTo(EH_pgc, ValidateBuffer(pgc, sizeof(CGlobalContext)));

    //So far, nothing has called into the tree so we don't really need
    //  to be holding the tree mutex.  The UnmarshalContext call does
    //  call into the tree, though, so we need to make sure this is
    //  threadsafe.  We'll do this my getting the mutex name from the
    //  CGlobalContext, then creating a new CDfMutex object.  While
    //  this is obviously not optimal, since it's possible we could
    //  reuse an existing CDfMutex, the reuse strategy isn't threadsafe
    //  since we can't do a lookup without the possibility of the thing
    //  we're looking for being released by another thread.
    TCHAR atcMutexName[CONTEXT_MUTEX_NAME_LENGTH];
    pgc->GetMutexName(atcMutexName);
    olChkTo(EH_pgc, mtx.Init(atcMutexName));
    olChkTo(EH_pgc, mtx.Take(INFINITE));

    //At this point we're holding the mutex.

#ifdef MULTIHEAP
#ifdef ASYNC
    olChkTo(EH_mtx, UnmarshalContext(pstm,
                                     pgc,
                                     &ppc,
                                     mshlflags,
                                     TRUE,
                                     P_INDEPENDENT(pdf->GetDFlags()),
                                     cntxid,
                                     pdf->IsRoot()));
#else
    olChkTo(EH_mtx, UnmarshalContext(pstm,
                                     pgc,
                                     &ppc,
                                     mshlflags,
                                     P_INDEPENDENT(pdf->GetDFlags()),
                                     cntxid,
                                     pdf->IsRoot()));
#endif
    if ((pvBaseNew = DFBASEPTR) != pvBaseOld)
    {
        pdf = (CPubDocFile*) ((ULONG_PTR)pdf - (ULONG_PTR)pvBaseOld
                                            + (ULONG_PTR)pvBaseNew);
        pedf = (CExposedDocFile*) ((ULONG_PTR)pedf - (ULONG_PTR)pvBaseOld
                                                  + (ULONG_PTR)pvBaseNew);
        pdfb = (CDFBasis*) ((ULONG_PTR)pdfb - (ULONG_PTR)pvBaseOld
                                           + (ULONG_PTR)pvBaseNew);
    }
#else
#ifdef ASYNC
    olChkTo(EH_mtx, UnmarshalContext(pstm,
                                     pgc,
                                     &ppc,
                                     mshlflags,
                                     TRUE,
                                     P_INDEPENDENT(pdf->GetDFlags()),
                                     pdf->IsRoot()));
#else
    olChkTo(EH_mtx, UnmarshalContext(pstm,
                                     pgc,
                                     &ppc,
                                     mshlflags,
                                     P_INDEPENDENT(pdf->GetDFlags()),
                                     pdf->IsRoot()));
#endif //ASYNC
#endif

#ifdef ASYNC
    olChkTo(EH_mtx, UnmarshalConnection(pstm,
                                        &dwAsyncFlags,
                                        &pdacp,
                                        mshlflags));
#endif
    // if we use up 1Gig of address space, use standard unmarshaling
    if (gs_iSharedHeaps > (DOCFILE_SM_LIMIT / DOCFILE_SM_SIZE))
        olErr (EH_ppc, STG_E_INSUFFICIENTMEMORY);

#ifdef POINTER_IDENTITY
    olAssert (pedf != NULL);
    pml = (CMarshalList *) pedf;

    // Warning: these checks must remain valid across processes
    if (SUCCEEDED(pedf->Validate()) && pedf->GetPub() == pdf)
    {
        pedf = (CExposedDocFile *) pml->FindMarshal(GetCurrentContextId());
    }
    else
    {
        pml = NULL;
        pedf = NULL;
    }

    // exposed object is not found or has been deleted
    if (pedf == NULL)
    {
#endif
        olMemTo(EH_ppc, pedf = new (pdfb->GetMalloc())
                               CExposedDocFile(pdf, pdfb, ppc));
        olChkTo(EH_exp, pedf->InitMarshal(dwAsyncFlags, pdacp));
        //InitMarshal adds a reference.
        if (pdacp)
            pdacp->Release();

#ifdef POINTER_IDENTITY
        if (pml) pml->AddMarshal(pedf);
        pdf->vAddRef();  // CExposedDocFile ctor does not AddRef
    }
    else
    {
        pdfb->SetAccess(ppc);
        pedf->AddRef();         // reuse this object
        ppc->Release();         // reuse percontext
    }
#else
    pdf->vAddRef();
#endif

    *ppv = (void *)pedf;
#ifdef MULTIHEAP
    if (pvBaseOld != pvBaseNew)
    {
        pcSharedMemory.SetThreadAllocatorState(NULL);
        g_smAllocator.Uninit();           // delete the extra mapping
    }
    g_smAllocator.SetState(NULL, NULL, 0, NULL, NULL);
#endif

    mtx.Release();

    //We're returning the custom marshalled version, so we don't need
    //the std marshalled one anymore.
    if (pstgStd != NULL)
        pstgStd->Release();

    olDebugOut((DEB_ITRACE, "Out CExposedDocFile::Unmarshal => %p\n", *ppv));
    return S_OK;
 EH_exp:
    pedf->Release();
    goto EH_Err;
 EH_ppc:
    ppc->Release();
 EH_mtx:
    mtx.Release();
    goto EH_Err;
 EH_pgc:
    CoReleaseMarshalData(pstm); // release the ILockBytes
    CoReleaseMarshalData(pstm); // release the ILockBytes
    if (P_INDEPENDENT(pdf->GetDFlags()))
        CoReleaseMarshalData(pstm); // release the ILockBytes
#ifdef ASYNC
    ReleaseConnection(pstm, mshlflags);
#endif

 EH_pdfb:
 EH_pdf:
 EH_mem:
EH_Err:
#ifdef MULTIHEAP
    pcSharedMemory.SetThreadAllocatorState(NULL);
    g_smAllocator.Uninit();   // delete the file mapping in error case
    g_smAllocator.SetState(NULL, NULL, 0, NULL, NULL);
#endif
 EH_std:
    if (pstgStd != NULL)
    {
        //We can return the standard marshalled version and still succeed.
        *ppv = pstgStd;
        return S_OK;
    }
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::GetUnmarshalClass, public
//
//  Synopsis:   Returns the class ID
//
//  Arguments:  [riid] - IID of object
//              [pv] - Unreferenced
//              [dwDestContext] - Unreferenced
//              [pvDestContext] - Unreferenced
//              [mshlflags] - Unreferenced
//              [pcid] - CLSID return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcid]
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::GetUnmarshalClass(REFIID riid,
                                                void *pv,
                                                DWORD dwDestContext,
                                                LPVOID pvDestContext,
                                                DWORD mshlflags,
                                                LPCLSID pcid)
{
    SCODE sc;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif

    olLog(("%p::In  CExposedDocFile::GetUnmarshalClass("
           "riid, %p, %lu, %p, %lu, %p)\n",
           this, pv, dwDestContext, pvDestContext, mshlflags, pcid));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::GetUnmarshalClass:%p("
                "riid, %p, %lu, %p, %lu, %p)\n", this,
                pv, dwDestContext, pvDestContext, mshlflags, pcid));

    UNREFERENCED_PARM(pv);
    UNREFERENCED_PARM(mshlflags);

    olChk(ValidateOutBuffer(pcid, sizeof(CLSID)));
    memset(pcid, 0, sizeof(CLSID));
    olChk(ValidateIid(riid));
    olChk(Validate());
    olChk(_pdf->CheckReverted());

    if ((dwDestContext != MSHCTX_LOCAL) && (dwDestContext != MSHCTX_INPROC))
    {
        IMarshal *pmsh;

        if (SUCCEEDED(sc = CoGetStandardMarshal(riid, (IUnknown *)pv,
                                                dwDestContext, pvDestContext,
                                                mshlflags, &pmsh)))
        {
            sc = GetScode(pmsh->GetUnmarshalClass(riid, pv, dwDestContext,
                                                  pvDestContext, mshlflags,
                                                  pcid));
            pmsh->Release();
        }
    }
    else if (pvDestContext != NULL)
    {
        sc = STG_E_INVALIDPARAMETER;
    }
    else
    {
        olChk(VerifyIid(riid, IID_IStorage));
        *pcid = CLSID_DfMarshal;
    }

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::GetUnmarshalClass\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::GetUnmarshalClass().  ret = %lx\n",
        this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::GetMarshalSizeMax, public
//
//  Synopsis:   Returns the size needed for the marshal buffer
//
//  Arguments:  [riid] - IID of object being marshaled
//              [pv] - Unreferenced
//              [dwDestContext] - Unreferenced
//              [pvDestContext] - Unreferenced
//              [mshlflags] - Unreferenced
//              [pcbSize] - Size return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [pcbSize]
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::GetMarshalSizeMax(REFIID riid,
                                                void *pv,
                                                DWORD dwDestContext,
                                                LPVOID pvDestContext,
                                                DWORD mshlflags,
                                                LPDWORD pcbSize)
{
    SCODE sc;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif

    UNREFERENCED_PARM(pv);
    olLog(("%p::In  CExposedDocFile::GetMarshalSizeMax("
           "riid, %p, %lu, %p, %lu, %p)\n",
           this, pv, dwDestContext, pvDestContext, mshlflags, pcbSize));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::GetMarshalSizeMax:%p("
                "riid, %p, %lu, %p, %lu, %p)\n", this,
                pv, dwDestContext, pvDestContext, mshlflags, pcbSize));

    olChk(Validate());
    olChk(_pdf->CheckReverted());

    if ((dwDestContext != MSHCTX_LOCAL) && (dwDestContext != MSHCTX_INPROC))
    {
        IMarshal *pmsh;

        if (SUCCEEDED(sc = CoGetStandardMarshal(riid, (IUnknown *)pv,
                                                dwDestContext, pvDestContext,
                                                mshlflags, &pmsh)))
        {
            sc = GetScode(pmsh->GetMarshalSizeMax(riid, pv, dwDestContext,
                                                  pvDestContext, mshlflags,
                                                  pcbSize));
            pmsh->Release();
        }
    }
    else if (pvDestContext != NULL)
    {
        sc = STG_E_INVALIDPARAMETER;
    }
    else
    {
        sc = GetStdMarshalSize(riid,
                               IID_IStorage,
                               dwDestContext,
                               pvDestContext,
                               mshlflags, pcbSize,
                               sizeof(CPubDocFile *)+sizeof(CDFBasis *)+
                               sizeof(DFLAGS),
#ifdef ASYNC
                               &_cpoint,
                               TRUE,
#endif
                               _ppc, P_INDEPENDENT(_pdf->GetDFlags()));
        DWORD cbSize = 0;
        IMarshal *pmsh;

        if (SUCCEEDED(sc = CoGetStandardMarshal(riid, (IUnknown *)pv,
                                                dwDestContext, pvDestContext,
                                                mshlflags, &pmsh)))
        {
            sc = GetScode(pmsh->GetMarshalSizeMax(riid, pv, dwDestContext,
                                                  pvDestContext, mshlflags,
                                                  &cbSize));
            pmsh->Release();
            *pcbSize += cbSize;
        }
    }

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::GetMarshalSizeMax\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::GetMarshalSizeMax()."
           "*pcbSize == %lu, ret == %lx\n", this, *pcbSize, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::MarshalInterface, public
//
//  Synopsis:   Marshals a given object
//
//  Arguments:  [pstStm] - Stream to write marshal data into
//              [riid] - Interface to marshal
//              [pv] - Unreferenced
//              [dwDestContext] - Unreferenced
//              [pvDestContext] - Unreferenced
//              [mshlflags] - Unreferenced
//
//  Returns:    Appropriate status code
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::MarshalInterface(IStream *pstStm,
                                               REFIID riid,
                                               void *pv,
                                               DWORD dwDestContext,
                                               LPVOID pvDestContext,
                                               DWORD mshlflags)
{
    SCODE sc;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif

    olLog(("%p::In  CExposedDocFile::MarshalInterface("
           "%p, riid, %p, %lu, %p, %lu).  Context == %lX\n",
           this, pstStm, pv, dwDestContext,
           pvDestContext, mshlflags,(ULONG)GetCurrentContextId()));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::MarshalInterface:%p("
                "%p, riid, %p, %lu, %p, %lu)\n", this, pstStm, pv,
                dwDestContext, pvDestContext, mshlflags));

    UNREFERENCED_PARM(pv);

    olChk(Validate());
    olChk(_pdf->CheckReverted());

    if ((dwDestContext != MSHCTX_LOCAL) && (dwDestContext != MSHCTX_INPROC))
    {
        IMarshal *pmsh;

        if (SUCCEEDED(sc = CoGetStandardMarshal(riid, (IUnknown *)pv,
                                                dwDestContext, pvDestContext,
                                                mshlflags, &pmsh)))
        {
            sc = GetScode(pmsh->MarshalInterface(pstStm, riid, pv,
                                                 dwDestContext, pvDestContext,
                                                 mshlflags));
            pmsh->Release();
        }
    }
    else if (pvDestContext != NULL)
    {
        sc = STG_E_INVALIDPARAMETER;
    }
    else
    {
        olChk(StartMarshal(pstStm, riid, IID_IStorage, mshlflags));

        //Always standard marshal, in case we get an error during
        //unmarshalling of the custom stuff.
        {
            IMarshal *pmsh;
            if (SUCCEEDED(sc = CoGetStandardMarshal(riid, (IUnknown *)pv,
                                               dwDestContext, pvDestContext,
                                               mshlflags, &pmsh)))
            {
                sc = GetScode(pmsh->MarshalInterface(pstStm, riid, pv,
                                                dwDestContext, pvDestContext,
                                                mshlflags));
                pmsh->Release();
            }
            olChk(sc);
        }
        
#ifdef MULTIHEAP
        olChk(MarshalSharedMemory(pstStm, _ppc));
#endif
#ifdef POINTER_IDENTITY
        olChk(MarshalPointer(pstStm, (CExposedDocFile*) GetNextMarshal()));
#endif
        olChk(MarshalPointer(pstStm, BP_TO_P(CPubDocFile *, _pdf)));
        olChk(MarshalPointer(pstStm, BP_TO_P(CDFBasis *, _pdfb)));
        olChk(MarshalPointer(pstStm, (void *) LongToPtr(_pdf->GetDFlags()) ));
#ifdef ASYNC
        olChk(MarshalContext(pstStm,
                             _ppc,
                             dwDestContext,
                             pvDestContext,
                             mshlflags,
                             TRUE,
                             P_INDEPENDENT(_pdf->GetDFlags())));

        sc = MarshalConnection(pstStm,
                                &_cpoint,
                                dwDestContext,
                                pvDestContext,
                                mshlflags);
#else
        olChk(MarshalContext(pstStm,
                             _ppc,
                             dwDestContext,
                             pvDestContext,
                             mshlflags,
                             P_INDEPENDENT(_pdf->GetDFlags())));
#endif

    }

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::MarshalInterface\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::MarshalInterface().  ret == %lx\n",
           this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::UnmarshalInterface, public
//
//  Synopsis:   Non-functional
//
//  Arguments:  [pstStm] -
//              [riid] -
//              [ppvObj] -
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvObj]
//
//  History:    04-May-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::UnmarshalInterface(IStream *pstStm,
                                                 REFIID riid,
                                                 void **ppvObj)
{
    olLog(("%p::INVALID CALL TO CExposedDocFile::UnmarshalInterface()\n",
           this));
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::StaticReleaseMarshalData, public static
//
//  Synopsis:   Releases any references held in marshal data
//
//  Arguments:  [pstStm] - Marshal data stream
//
//  Returns:    Appropriate status code
//
//  History:    02-Feb-94       DrewB   Created
//
//  Notes:      Assumes standard marshal header has already been read
//
//---------------------------------------------------------------


SCODE CExposedDocFile::StaticReleaseMarshalData(IStream *pstStm,
                                                DWORD mshlflags)
{
    SCODE sc;
    CPubDocFile *pdf;
    CDFBasis *pdfb;
    ULONG_PTR df;
#ifdef POINTER_IDENTITY
    CExposedDocFile *pedf;
#endif

    olDebugOut((DEB_ITRACE, "In  CExposedDocFile::StaticReleaseMarshalData:("
                "%p, %lX)\n", pstStm, mshlflags));

    //First unmarshal the standard marshalled version

    olChk(CoReleaseMarshalData(pstStm));
    // The final release of the exposed object may have shut down the
    // shared memory heap, so do not access shared memory after this point

    //Then do the rest of it.
#ifdef MULTIHEAP
    olChk(SkipSharedMemory(pstStm, mshlflags));
#endif
#ifdef POINTER_IDENTITY
    olChk(UnmarshalPointer(pstStm, (void **) &pedf));
#endif
    olChk(UnmarshalPointer(pstStm, (void **)&pdf));
    olChk(UnmarshalPointer(pstStm, (void **)&pdfb));
    olChk(UnmarshalPointer(pstStm, (void **)&df));
#ifdef ASYNC
    olChk(ReleaseContext(pstStm, TRUE,
                         P_INDEPENDENT(df) != 0,
                         mshlflags));
    olChk(ReleaseConnection(pstStm, mshlflags));
#else
    olChk(ReleaseContext(pstStm, P_INDEPENDENT(df), mshlflags));
#endif

#ifdef MULTIHEAP
    g_smAllocator.SetState(NULL, NULL, 0, NULL, NULL);
#endif

    olDebugOut((DEB_ITRACE,
                "Out CExposedDocFile::StaticReleaseMarshalData\n"));
EH_Err:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::ReleaseMarshalData, public
//
//  Synopsis:   Non-functional
//
//  Arguments:  [pstStm] -
//
//  Returns:    Appropriate status code
//
//  History:    18-Sep-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::ReleaseMarshalData(IStream *pstStm)
{
    SCODE sc;
    DWORD mshlflags;
    IID iid;
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif

    olLog(("%p::In  CExposedDocFile::ReleaseMarshalData(%p)\n", this, pstStm));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::ReleaseMarshalData:%p(%p)\n",
                this, pstStm));

    olChk(Validate());
    olChk(_pdf->CheckReverted());
    olChk(SkipStdMarshal(pstStm, &iid, &mshlflags));
    olAssert(IsEqualIID(iid, IID_IStorage));
    sc = StaticReleaseMarshalData(pstStm, mshlflags);

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::ReleaseMarshalData\n"));
EH_Err:
    olLog(("%p::Out CExposedDocFile::ReleaseMarshalData().  ret == %lx\n",
           this, sc));
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::DisconnectObject, public
//
//  Synopsis:   Non-functional
//
//  Arguments:  [dwRevserved] -
//
//  Returns:    Appropriate status code
//
//  History:    18-Sep-92       DrewB   Created
//
//---------------------------------------------------------------


STDMETHODIMP CExposedDocFile::DisconnectObject(DWORD dwReserved)
{
    olLog(("%p::INVALID CALL TO CExposedDocFile::DisconnectObject()\n",
           this));
    return ResultFromScode(STG_E_INVALIDFUNCTION);
}

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::SwitchToFile, public
//
//  Synopsis:   Switches the underlying file to another file
//
//  Arguments:  [ptcsFile] - New file name
//
//  Returns:    Appropriate status code
//
//  History:    08-Jan-93       DrewB   Created
//
//----------------------------------------------------------------------------

STDMETHODIMP CExposedDocFile::SwitchToFile(OLECHAR *ptcsFile)
{

    ULONG ulOpenLock;
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;

    olLog(("%p::In  CExposedDocFile::SwitchToFile(" OLEFMT ")\n",
           this, ptcsFile));
    olDebugOut((DEB_TRACE, "In  CExposedDocFile::SwitchToFile:"
                "%p(" OLEFMT ")\n",
                this, ptcsFile));

    olChk(ValidateNameW(ptcsFile, _MAX_PATH));
    olChk(Validate());

    olChk(TakeSafeSem());
    olChk(_pdf->CheckReverted());
#ifdef COORD
    olAssert(_pdf->IsRoot() || _pdf->IsCoord());
#else
    olAssert(_pdf->IsRoot());
#endif

    SafeReadAccess();

    ulOpenLock = _ppc->GetOpenLock();
#ifdef COORD
    sc = _pdf->GetRoot()->SwitchToFile(ptcsFile,
                                       _ppc->GetOriginal(),
                                       &ulOpenLock);
#else
    sc = ((CRootPubDocFile *)(CPubDocFile*)_pdf)->SwitchToFile(ptcsFile,
                                                 _ppc->GetOriginal(),
                                                 &ulOpenLock);
#endif

    _ppc->SetOpenLock(ulOpenLock);

    olDebugOut((DEB_TRACE, "Out CExposedDocFile::SwitchToFile\n"));
 EH_Err:
    olLog(("%p::Out CExposedDocFile::SwitchToFile().  ret == %lx\n",
           this, sc));
    return ResultFromScode(sc);
}

#if WIN32 >= 300
             // IAccessControl methods
STDMETHODIMP CExposedDocFile::GrantAccessRights(ULONG cCount,
                                ACCESS_REQUEST pAccessRequestList[])
{
    olAssert((_pIAC != NULL));
    return _pIAC->GrantAccessRights(cCount, pAccessRequestList);
}

STDMETHODIMP CExposedDocFile::SetAccessRights(ULONG cCount,
                              ACCESS_REQUEST pAccessRequestList[])
{
    olAssert((_pIAC != NULL));
    return _pIAC->SetAccessRights(cCount, pAccessRequestList);
}

STDMETHODIMP CExposedDocFile::ReplaceAllAccessRights(ULONG cCount,
                                     ACCESS_REQUEST pAccessRequestList[])
{
    olAssert((_pIAC != NULL));
    return _pIAC->ReplaceAllAccessRights(cCount, pAccessRequestList);
}

STDMETHODIMP CExposedDocFile::DenyAccessRights(ULONG cCount,
                              ACCESS_REQUEST pAccessRequestList[])
{
    olAssert((_pIAC != NULL));
    return _pIAC->DenyAccessRights(cCount, pAccessRequestList);
}

STDMETHODIMP CExposedDocFile::RevokeExplicitAccessRights(ULONG cCount,
                                         TRUSTEE pTrustee[])
{
    olAssert((_pIAC != NULL));
    return _pIAC->RevokeExplicitAccessRights(cCount, pTrustee);
}

STDMETHODIMP CExposedDocFile::IsAccessPermitted(TRUSTEE *pTrustee,
                                 DWORD grfAccessPermissions)
{
    olAssert((_pIAC != NULL));
    return _pIAC->IsAccessPermitted(pTrustee, grfAccessPermissions);
}

STDMETHODIMP CExposedDocFile::GetEffectiveAccessRights(TRUSTEE *pTrustee,
                                       DWORD *pgrfAccessPermissions )
{
    olAssert((_pIAC != NULL));
    return _pIAC->GetEffectiveAccessRights(pTrustee, pgrfAccessPermissions);
}

STDMETHODIMP CExposedDocFile::GetExplicitAccessRights(ULONG *pcCount,
                                      PEXPLICIT_ACCESS *pExplicitAccessList)
{
    olAssert((_pIAC != NULL));
    return _pIAC->GetExplicitAccessRights(pcCount, pExplicitAccessList);
}

STDMETHODIMP CExposedDocFile::CommitAccessRights(DWORD grfCommitFlags)
{
    olAssert((_pIAC != NULL));
    return _pIAC->CommitAccessRights(grfCommitFlags);
}

STDMETHODIMP CExposedDocFile::RevertAccessRights()
{
    olAssert((_pIAC != NULL));
    return _pIAC->RevertAccessRights();
}



#endif // if WIN32 >= 300


#ifdef COORD
//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::CommitPhase1, public
//
//  Synopsis:   Do phase 1 of an exposed two phase commit
//
//  Arguments:  [grfCommitFlags] -- Commit flags
//
//  Returns:    Appropriate status code
//
//  History:    08-Aug-95       PhilipLa        Created
//
//----------------------------------------------------------------------------

SCODE CExposedDocFile::CommitPhase1(DWORD grfCommitFlags)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;

    olChk(VerifyCommitFlags(grfCommitFlags));
    olChk(Validate());

    olChk(TakeSafeSem());
    SafeWriteAccess();
#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    sc = _pdf->CommitPhase1(grfCommitFlags,
                            &_ulLock,
                            &_sigMSF,
                            &_cbSizeBase,
                            &_cbSizeOrig);
EH_Err:
    return sc;
}


//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::CommitPhase2, public
//
//  Synopsis:   Do phase 2 of an exposed two phase commit
//
//  Arguments:  [grfCommitFlags] -- Commit flags
//              [fCommit] -- TRUE if transaction is to commit, FALSE if abort
//
//  Returns:    Appropriate status code
//
//  History:    08-Aug-95       PhilipLa        Created
//
//----------------------------------------------------------------------------

SCODE CExposedDocFile::CommitPhase2(DWORD grfCommitFlags,
                                    BOOL fCommit)
{
    SCODE sc;
    SAFE_SEM;
    SAFE_ACCESS;

    olChk(Validate());

    olChk(TakeSafeSem());
    SafeWriteAccess();
#ifdef DIRECTWRITERLOCK
    olChk(ValidateWriteAccess());
#endif

    sc = _pdf->CommitPhase2(grfCommitFlags,
                            fCommit,
                            _ulLock,
                            _sigMSF,
                            _cbSizeBase,
                            _cbSizeOrig);

    _ulLock = _cbSizeBase = _cbSizeOrig = 0;
    _sigMSF = 0;
EH_Err:
    return sc;
}
#endif //COORD


#ifdef NEWPROPS

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::Lock, IBlockingLock
//
//  Synopsis:   Acquires the semaphore associated with the docfile.
//
//  Notes:      This member is called by CPropertyStorage.
//
//----------------------------------------------------------------------------

STDMETHODIMP
CExposedDocFile::Lock(DWORD dwTimeout)
{
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif
    TakeSem();
    SetDifferentBasisAccess(_pdfb, _ppc);
    return S_OK;
}

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::Unlock, public IBlockingLock
//
//  Synopsis:   Releases the semaphore associated with the docfile.
//
//  Notes:      This member is called by CPropertyStorage.
//
//----------------------------------------------------------------------------

STDMETHODIMP
CExposedDocFile::Unlock(VOID)
{
#ifdef MULTIHEAP
    CSafeMultiHeap smh(_ppc);
#endif
    ClearBasisAccess(_pdfb);
    ReleaseSem(S_OK);
    return( S_OK );
}
#endif

#ifdef DIRECTWRITERLOCK
//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::WaitForWriteAccess, public IDirectWriterLock
//
//  Synopsis:   tries to obtain exclusive write access in direct mode
//
//  Notes:      Tree mutex must be taken when accessing recursion count
//
//----------------------------------------------------------------------------

STDMETHODIMP CExposedDocFile::WaitForWriteAccess (DWORD dwTimeout)
{
    SAFE_SEM;
    SAFE_ACCESS;
    HRESULT hr = TakeSafeSem();

    if (SUCCEEDED(hr) && *_ppc->GetRecursionCount() == 0)
    {
        SafeReadAccess();
        hr = _pdfb->WaitForWriteAccess (dwTimeout, _ppc->GetGlobal());
    }
    if (SUCCEEDED(hr)) ++(*_ppc->GetRecursionCount());
    return hr;
}


//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::ReleaseWriteAccess, public IDirectWriterLock
//
//  Synopsis:   releases exclusive write access from WaitForWriteAccess
//
//  Notes:      Tree mutex must be taken when accessing recursion count
//
//----------------------------------------------------------------------------

STDMETHODIMP CExposedDocFile::ReleaseWriteAccess ()
{
    SAFE_SEM;
    SAFE_ACCESS;
    HRESULT hr = TakeSafeSem();

    if (SUCCEEDED(hr) && *_ppc->GetRecursionCount() == 1)
    {
        SafeReadAccess();
        hr = _pdf->Commit(STGC_DEFAULT);  // Flush
        if (SUCCEEDED(hr)) hr = _pdfb->ReleaseWriteAccess();
    }
    if (SUCCEEDED(hr)) --(*_ppc->GetRecursionCount());
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CExposedDocFile::HaveWriteAccess, public IDirectWriterLock
//
//  Synopsis:   returns S_OK if write lock is active, S_FALSE if not
//
//  Notes:
//
//----------------------------------------------------------------------------

STDMETHODIMP CExposedDocFile::HaveWriteAccess ()
{
    SAFE_SEM;
    HRESULT hr = TakeSafeSem();

    olAssert(_pdfb->DirectWriterMode());
    if (SUCCEEDED(hr))
    {
        hr = (_pdfb->HaveWriteAccess()) ? S_OK : S_FALSE;
    }
    return hr;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedDocFile::ValidateWriteAccess, public
//
//  Synopsis:   returns whether writer currently has write access
//
//  Notes:      tree mutex must be taken
//
//  History:    30-Apr-96    HenryLee     Created
//
//---------------------------------------------------------------
HRESULT CExposedDocFile::ValidateWriteAccess()
{
    if (_pdf->GetTransactedDepth() >= 1)
        return S_OK;

    return (!_pdfb->DirectWriterMode() || (*_ppc->GetRecursionCount()) > 0) ?
            S_OK : STG_E_ACCESSDENIED;
};

#endif // DIRECTWRITERLOCK