//+=======================================================================
//
//  File:       CMoniker.cxx
//
//  Purpose:    Define the CMoniker class.
//
//              This class provides for all handling of monikers in
//              the CreateFileMonikerEx DRT.  Not only does it maintain
//              a file moniker, it also maintains the represented link
//              source file, and a bind context.
//
//+=======================================================================

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

#define _DCOM_			// Allow DCOM extensions (e.g., CoInitializeEx).

#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <wchar.h>
#include <wtypes.h>
#include <oaidl.h>
#include <dsys.h>
#include <olecairo.h>
#include "CFMEx.hxx"
#include "CMoniker.hxx"


//+-------------------------------------------------------------------------
//  
//  Function:   CMoniker::CMoniker
//
//  Synopsis:   Simply initialize all member variables.
//
//  Inputs:     None.
//
//  Outputs:    N/A
//
//  Effects:    Members are defaulted/initialized.
//
//+-------------------------------------------------------------------------


CMoniker::CMoniker()
{
    *m_wszSystemTempPath = L'\0';
    *m_wszTemporaryStorage = L'\0';
    m_pIMoniker = NULL;
    m_pIBindCtx = NULL;
    m_pIStorage = NULL;
    *m_wszErrorMessage = L'\0';
    m_dwTrackFlags = 0L;
    m_hkeyLinkTracking = NULL;
    m_hr = 0L;
    m_bSuppressErrorMessages = FALSE;
    m_pcDirectoryOriginal = NULL;
    m_pcDirectoryFinal = NULL;

    return;

}   // CMoniker::CMoniker()


//+--------------------------------------------------------------------------
//
//  Function:   CMoniker::~CMoniker
//
//  Synopsis:   Release any COM objects.
//
//  Inputs:     N/A
//
//  Outputs:    N/A
//
//  Effects:    All COM objects are released.
//
//+--------------------------------------------------------------------------


CMoniker::~CMoniker()
{
    if( m_pIMoniker )
    {
        m_pIMoniker->Release();
        m_pIMoniker = NULL;
    }

    if( m_pIBindCtx )
    {
        m_pIBindCtx->Release();
        m_pIBindCtx = NULL;
    }

    if( m_pIStorage )
    {
        m_pIStorage->Release();
        m_pIStorage = NULL;
    }

    return;

}   // CMoniker::~CMoniker()


//+-----------------------------------------------------------------------
//
//  Function:   CMoniker::Initialize
//
//  Synopsis:   Keep pointers to the CDirectory objects passed in.
//
//  Inputs:     A CDirectory object for the original link source file location
//              A CDirectory object for the final location
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    The member CDirectory objects are set.
//
//+-----------------------------------------------------------------------


BOOL CMoniker::Initialize( const CDirectory& cDirectoryOriginal,
                           const CDirectory& cDirectoryFinal )
{
    m_hr = S_OK;

    m_pcDirectoryOriginal = &cDirectoryOriginal;
    m_pcDirectoryFinal = &cDirectoryFinal;

    return( TRUE ); // Success

}   // CMoniker::Initialize()


//+-----------------------------------------------------------------------------
//
//  Function:   CMoniker::CreateFileMonikerEx
//
//  Synopsis:   Create a tracking file moniker.  But before doing so, initialize
//              the Bind Context, and create a link source file.
//
//  Inputs:     Track Flags (from the TRACK_FLAGS defines)
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    The member bind context is initialized, and a link
//              source file is created.
//
//+-----------------------------------------------------------------------------


BOOL CMoniker::CreateFileMonikerEx( DWORD dwTrackFlags )
{

    //  ---------------
    //  Local Variables
    //  ---------------

    // Assume failure
    BOOL bSuccess = FALSE;

    //  -----
    //  Begin
    //  -----

    // Initialize the error code.

    m_hr = S_OK;

    // Free any existing IMoniker.

    if( m_pIMoniker )
    {
        m_pIMoniker->Release();
        m_pIMoniker = NULL;
    }

    // Initialize the bind context.

    if( !InitializeBindContext() )
        EXIT( L"Could not initialize the bind context" );

    // Create a root storage for use as a link source.

    if( !CreateTemporaryStorage() )
        EXIT( L"Could not create temporary Storage" );

    // Create a tracking File Moniker on that root storage.

    m_hr = ::CreateFileMonikerEx( dwTrackFlags, m_wszTemporaryStorage, &m_pIMoniker );
    EXIT_ON_FAILED( L"Failed CreateFileMonikerEx" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CMoniker::CreateFileMonikerEx" );
    return( bSuccess );

}   // CMoniker::CreateFileMonikerEx()


//+---------------------------------------------------------------------
//
//  Function:   CMoniker::SaveDeleteLoad
//
//  Synopsis:   This function exercises a moniker's IPersistStream interface.
//              It creates saves the member moniker's persistent state to
//              a stream, deletes the moniker, and then re-creates it
//              using CreateFileMoniker (no Ex, so it's not a tracking
//              file moniker).  It then re-loads the original moniker's
//              persistent state.
//
//  Inputs:     None.
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    The member moniker is deleted, re-created, and re-loaded.
//
//+---------------------------------------------------------------------


BOOL CMoniker::SaveDeleteLoad()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    // Assume failure
    BOOL bSuccess = FALSE;
    HRESULT hr = E_FAIL;

    IStream*        pStream = NULL;
    IPersistStream* pIPersistStream = NULL;
    LARGE_INTEGER   li;
    ULARGE_INTEGER  uli;

    //  -----
    //  Begin
    //  -----

    // Initialize the error code.

    m_hr = S_OK;

    // Verify that we have a member moniker.

    if( !m_pIMoniker )
        EXIT( L"Attempt to run SaveDeleteLoad test without an existing file moniker" );


    //  ------------------------
    //  Save the moniker's state
    //  ------------------------

    // Get the moniker's IPersistStream interface

    m_hr = m_pIMoniker->QueryInterface( IID_IPersistStream, (void **) &pIPersistStream );
    EXIT_ON_FAILED( L"Failed 1st IMoniker::QueryInterface(IPersistStream)" );

    // Create a stream

    hr = CreateStreamOnHGlobal( NULL,   // Auto alloc
                                TRUE,   // Delete on release
                                &pStream );
    EXIT_ON_FAILED( L"Failed CreateStreamOnHGlobal()" );

    // Save the moniker's state to this stream.

    hr = pIPersistStream->Save( pStream, TRUE /* Clear dirty*/ );
    EXIT_ON_FAILED( L"Failed IPersistStream::Save()" );

    //  ------------------
    //  Delete the moniker
    //  ------------------

    // Release all interfaces for the moniker.

    m_pIMoniker->Release();
    pIPersistStream->Release();

    m_pIMoniker = NULL;
    pIPersistStream = NULL;

    //  --------------------
    //  Create a new moniker
    //  --------------------
    
    // Create a new moniker, using the non-Ex version of the function.

    m_hr = ::CreateFileMoniker( m_wszTemporaryStorage, &m_pIMoniker );
    EXIT_ON_FAILED( L"Failed CreateFileMoniker()" );

    //  --------------------
    //  Load the new moniker
    //  --------------------

    // Get the IPersisStream interface

    m_hr = m_pIMoniker->QueryInterface( IID_IPersistStream, (void **) &pIPersistStream );
    EXIT_ON_FAILED( L"Failed 2nd IMoniker::QueryInterface(IPersistStream)" );

    // Re-seek the stream to the beginning.

    li.LowPart = li.HighPart = 0L;
    hr = pStream->Seek( li, STREAM_SEEK_SET, &uli );
    EXIT_ON_FAILED( L"Failed IStream::Seek()" );
    if( uli.LowPart || uli.HighPart ) EXIT( L"Incorrect IStream::Seek()" );

    // Re-load the moniker from the stream.

    m_hr = pIPersistStream->Load( pStream );
    EXIT_ON_FAILED( L"Failed IPersistStream::Load()" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    // Clean up the stream and the IPersistStream interface.

    if( pStream )
       pStream->Release;

    if( pIPersistStream )
       pIPersistStream->Release();


    DisplayErrors( bSuccess, L"CMoniker::SaveDeleteLoad()" );
    return( bSuccess );

}   // CMoniker::SaveDeleteLoad()


//+------------------------------------------------------------------
//
//  Function:   CMoniker::ComposeWith
//
//  Synopsis:   Compose a tracking moniker with a non-tracking moniker
//              on the right.  (The resulting moniker should be tracking,
//              but this is not relevant to this function; that is, whether
//              or not the composed moniker is tracking, this function will
//              succeed.)
//
//  Inputs:     None.
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    The member moniker is deleted, then recreated.
//
//+------------------------------------------------------------------

BOOL CMoniker::ComposeWith()
{

    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;

    IMoniker* pmkrFirst = NULL;
    IMoniker* pmkrSecond = NULL;
    HRESULT hr = E_FAIL;

    WCHAR  wszDirectoryName[ MAX_PATH + sizeof( L'\0' ) ];
    WCHAR* wszFileName = NULL;

    //  -----
    //  Begin
    //  -----

    // Initiailize the error code.

    m_hr = S_OK;

    // If we have a moniker already, delete it.

    if( m_pIMoniker )
    {
        m_pIMoniker->Release();
        m_pIMoniker = NULL;
    }

    // Verify we already have a link source file created.

    if( !wcslen( m_wszTemporaryStorage ) )
        EXIT( L"Attempt to use ComposeWith without first creating a link source.\n" );

    //  -----------------------------------------------
    //  Create a tracking and non-tracking file moniker
    //  -----------------------------------------------

    // Parse the storage's filename into a path and a file ...
    // for example, "C:\Temp\file.tmp" would become
    // "C:\Temp" and "file.tmp".
    //
    // First, make a copy of the storage's complete path name,
    // then replace the last '\\' with a '\0', thus creating two
    // strings.

    wcscpy( wszDirectoryName, m_wszTemporaryStorage );
    wszFileName = wcsrchr( wszDirectoryName, L'\\' );
    *wszFileName = L'\0';
    wszFileName++;

    // Create a tracking file moniker using the directory name.

    m_hr = ::CreateFileMonikerEx( 0L, wszDirectoryName, &pmkrFirst );
    EXIT_ON_FAILED( L"Failed 1st CreateFileMoniker()" );

    // Create a non-tracking file moniker using the file name.

    m_hr = ::CreateFileMoniker( wszFileName, &pmkrSecond );
    EXIT_ON_FAILED( L"Failed 2nd CreateFileMoniker()" );

    //  -------
    //  Compose
    //  -------

    // Compose the directory name moniker (on the left) with the file name moniker
    // (on the right).  Put the result in the member moniker.

    m_hr = pmkrFirst->ComposeWith( pmkrSecond, TRUE, &m_pIMoniker );
    EXIT_ON_FAILED( L"Failed IMoniker::ComposeWith" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    // Clean up the intermediary monikers.

    if( pmkrFirst )
        pmkrFirst->Release();

    if( pmkrSecond )
        pmkrSecond->Release();

    DisplayErrors( bSuccess, L"CMoniker::ComposeWith()" );
    return( bSuccess );

}   // CMoniker::ComposeWith()


//+--------------------------------------------------------------------
//
//  Function:   CMoniker::CreateTemporaryStorage
//
//  Synopsis:   This function creates a Structured Storage
//              in the directory identified by m_cDirectoryOriginal.
//              The name of the file is randomly generated by the system,
//              but begins with "MKR" and has the extension ".tmp".
//
//  Inputs:     None.
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    Stores the Structure Storage's name in
//              m_wszTemporaryStorageName, and releases m_pIStorage if
//              it is currently set.
//
//+--------------------------------------------------------------------


BOOL CMoniker::CreateTemporaryStorage()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL    bSuccess = FALSE;

    DWORD   dwError = 0L;
    UINT    nError = 0;


    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Delete any existing storage.

    if( wcslen( m_wszTemporaryStorage ))
    {
        if( !DeleteTemporaryStorage() )
            EXIT( L"Could not delete the existing temporary storage" );
    }

    // Generate a temporary filename.

    nError = GetTempFileName(  m_pcDirectoryOriginal->GetDirectoryName(),
                               L"MKR",  // Prefix string.
                               0,       // Generate random number,
                               m_wszTemporaryStorage );
    if( nError == 0 )
    {
        m_hr = (HRESULT) GetLastError();
        EXIT( L"Failed GetTempFileName()" );
    }

    // Create a root Storage.

    m_hr = StgCreateDocfile( m_wszTemporaryStorage,
                             STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT,
                             0L,    // Reserved
                             &m_pIStorage );
    EXIT_ON_FAILED( L"Failed StgCreateDocfile()" );


    //  Release the storage.

    m_pIStorage->Release();
    m_pIStorage = NULL;

    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:


    DisplayErrors( bSuccess, L"CMoniker::CreateTemporaryStorage()" );
    return( bSuccess );

}   // CMoniker::CreateTemporaryStorage()


//+-------------------------------------------------------------------------
//
//  Function:   CMoniker::RenameTemporaryStorage
//
//  Synopsis:   Rename the link source file (who's name is in m_wszTemporaryStorage)
//              to the m_cDirectoryFinal directory, with a new name.
//              The current name is "MKR#.tmp" (where "#" is a random number
//              generated by the system), and the new name is "RKM#.tmp" (where
//              "#" is the same random number).  (We must rename the base file
//              name, rather than its extension, because otherwise the default
//              link-tracking would fail (it would only score the renamed file
//              a 32 - by matching the file extension the score is 40.)
//
//  Inputs:     None.
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    The link source file is renamed, and it's new name is
//              put into m_wszTemporaryStorage.
//
//+-------------------------------------------------------------------------


BOOL CMoniker::RenameTemporaryStorage()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;
    int  nError = 0;

    WCHAR  wszNewName[ MAX_PATH + sizeof( L'\0' ) ];
    WCHAR* wszOldFileName;
    WCHAR  wszNewFileName[ MAX_PATH + sizeof( L'\0' ) ];

    char   szOldName[ MAX_PATH + sizeof( L'\0' ) ];
    char   szNewName[ MAX_PATH + sizeof( L'\0' ) ];


    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Verify that we already have a link source created.

    if( !wcslen( m_wszTemporaryStorage ))
        EXIT( L"No temporary storage to rename." );

    // Locate the file name within the complete path.
    // (E.g., find the "foo.txt" in "C:\TEMP\foot.txt".)

    wszOldFileName = wcsrchr( m_wszTemporaryStorage, L'\\' );
    if( !wszOldFileName )
        EXIT( L"Could not extract old file name from temporary storage name\n" );
    wszOldFileName++; // Get past the '\\'

    // Generate the new file name (change "MKR" to "RKM").
    
    wcscpy( wszNewFileName, wszOldFileName );

    wszNewFileName[0] = L'R';
    wszNewFileName[1] = L'K';
    wszNewFileName[2] = L'M';

    // Generate the complete path spec of the new file.

    wcscpy( wszNewName, m_pcDirectoryFinal->GetDirectoryName() );
    wcscat( wszNewName, wszNewFileName );


    // Convert the new and old file names to ANSI.

    if( m_hr = UnicodeToAnsi( m_wszTemporaryStorage, szOldName, sizeof( szOldName )) )
    {
        EXIT( L"Could not convert convert Unicode to Ansi for old name" );
    }

    if( m_hr = UnicodeToAnsi( wszNewName, szNewName, sizeof( szNewName )) )
    {
        EXIT( L"Could not convert convert Unicode to Ansi for new name" );
    }


    // Rename the file.

    nError = rename( szOldName, szNewName );
    if( nError )
    {
        m_hr = (HRESULT) errno;
        EXIT( L"Failed rename()" );
    }

    // Record the new name.

    wcscpy( m_wszTemporaryStorage, wszNewName );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CMoniker::RenameTemporaryStorage()" );
    return( bSuccess );

}   // CMoniker::RenameTemporaryStorage()


//+--------------------------------------------------------------------
//
//  Function:   CMoniker::DeleteTemporaryStorage
//
//  Synopsis:   Delete the temporary storage that this object uses
//              as a link source for the moniker.  The name of the
//              storage is in m_wszTemporaryStorage.
//
//  Inputs:     None.
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    The link source file is deleted, and m_wszTemporaryStorage
//              is set to a NULL string.
//
//+--------------------------------------------------------------------


BOOL CMoniker::DeleteTemporaryStorage()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;

    int  nError = 0;
    CHAR szTemporaryStorage[ MAX_PATH + sizeof( '\0' ) ];

    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Don't do anything if we have no file (don't report an
    // error either; the caller wants the file deleted, and it's
    // already not there).

    if( wcslen( m_wszTemporaryStorage ))
    {

        // Get the file name in ANSI.

        if( m_hr = UnicodeToAnsi( m_wszTemporaryStorage, szTemporaryStorage, sizeof( szTemporaryStorage )))
            EXIT( L"Could not convert unicode path to ANSI path" );

        // Delete the file.

        nError = unlink( szTemporaryStorage );
        if( nError )
        {
            m_hr = (HRESULT) errno;
            EXIT( L"Failed unlink()" );
        }

        // Clear the file name.

        wcscpy( m_wszTemporaryStorage, L"" );
    }

    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CMoniker::DeleteTemporaryStorage()" );
    return( bSuccess );

}   // CMoniker::DeleteTemporaryStorage()




//+-----------------------------------------------------------------
//
//  Function:   CMoniker::Reduce
//
//  Synopsis:   Perform a IMoniker::Reduce on the member moniker.
//              
//  Inputs:     -   Number of ticks until the deadline for completion of
//                  the operation.
//              -   A buffer into which to put the reduced IMoniker*
//                  (may be NULL).
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    None.
//
//+-----------------------------------------------------------------


BOOL CMoniker::Reduce( DWORD dwDelay, IMoniker** ppmkReturn )
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;
    IMoniker* pmkReduced = NULL;
    BIND_OPTS2 bind_opts;
    bind_opts.cbStruct = sizeof( BIND_OPTS2 );

    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    //  ----------
    //  Initialize
    //  ----------

    // Initialize the return buffer, if extant.

    if( ppmkReturn )
        *ppmkReturn = NULL;

    // Validate our state.

    if( !m_pIMoniker )
        EXIT( L"No moniker exists to be reduced" );

    //  ----------------
    //  Set the deadline
    //  ----------------

    // Get the BIND_OPTS from the bind context.

    m_hr = m_pIBindCtx->GetBindOptions( (LPBIND_OPTS) &bind_opts );
    EXIT_ON_FAILED( L"Failed IBindCtx::GetBindOptions" );

    // Determine what the tick count of the deadline is.

    if( dwDelay == INFINITE )
    {
        bind_opts.dwTickCountDeadline = 0;
    }
    else
    {
        bind_opts.dwTickCountDeadline = GetTickCount() + dwDelay;

        // Make sure the resulting tick count is not 0 (indicating no
        // deadline).

        if( bind_opts.dwTickCountDeadline == 0 )
            bind_opts.dwTickCountDeadline++;
    }

    // Put the resulting BIND_OPTS back into the bind context.

    m_hr = m_pIBindCtx->SetBindOptions( (LPBIND_OPTS) &bind_opts );
    EXIT_ON_FAILED( L"Failed IBindCtx::SetBindOptions" );

    
    //  ------------------
    //  Reduce the Moniker
    //  ------------------

    m_hr = m_pIMoniker->Reduce( m_pIBindCtx,
                                MKRREDUCE_ALL,
                                NULL,
                                &pmkReduced );
    EXIT_ON_FAILED( L"Failed IMoniker::Reduce" );

    // Return the reduced moniker to the caller (if so requested).

    if( ppmkReturn )
    {
        // Transfer responsibility for the release to the caller.
        *ppmkReturn = pmkReduced;
        pmkReduced = NULL;
    }

    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CMoniker::Reduce()" );

    if( pmkReduced )
	    pmkReduced->Release();

    return( bSuccess );

}   // CMoniker::Reduce()



//+----------------------------------------------------------------------
//
//  Function:   CMoniker::GetDisplayName
//
//  Synopsis:   Get the moniker's display name.
//
//  Inputs:     A Unicode buffer for the display name, and (optionally)
//              a moniker from which to get the display name.  If such
//              a moniker is not provided by the caller, then the member
//              moniker is used.
//
//              The unicode buffer must be long enough for MAX_PATH characters
//              and a terminating NULL.
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    None.
//
//+----------------------------------------------------------------------


BOOL CMoniker::GetDisplayName( WCHAR * wszDisplayName, IMoniker* pmnkCaller )
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;

    WCHAR* wszReturnedDisplayName = NULL;
    IMoniker* pmnk = NULL;

    //  -----
    //  Begin
    //  -----

    m_hr = NOERROR;

    // Determine which moniker to use, the caller-specified one or
    // the member one.

    if( pmnkCaller != NULL )
        pmnk = pmnkCaller;
    else
        pmnk = m_pIMoniker;

    if( !pmnk )
        EXIT( L"Attempt to GetDisplayName on NULL moniker" );

    // Get the display name from the moniker.

    m_hr = pmnk->GetDisplayName( m_pIBindCtx,
                                 NULL,
                                 &wszReturnedDisplayName );
    EXIT_ON_FAILED( L"Failed IMoniker::GetDisplayName()" );

    if( wcslen( wszReturnedDisplayName ) > MAX_UNICODE_PATH )
        EXIT( L"IMoniker::GetDisplayName() returned a path which was too long" );

    // Copy the display name into the caller's buffer, and free it.

    wcscpy( wszDisplayName, wszReturnedDisplayName );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    if( wszReturnedDisplayName )
    {
        CoTaskMemFree( wszReturnedDisplayName );
        wszReturnedDisplayName = NULL;
    }

    DisplayErrors( bSuccess, L"CMoniker::GetDisplayName()" );
    return( bSuccess );

}   // CMoniker::GetDisplayName()


//+-------------------------------------------------------------
//
//  Function:   CMoniker::InitializeBindContext
//
//  Synopsis:   Create a new bind context, and store it
//              in a member pointer.
//
//  Inputs:     None.
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    Updates m_pIBindCtx.
//
//+-------------------------------------------------------------



BOOL CMoniker::InitializeBindContext( )
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;

    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Release the old bind context if we have one.

    if( m_pIBindCtx )
        m_pIBindCtx->Release();

    // Create the new bind context.

    m_hr = CreateBindCtx( 0L, &m_pIBindCtx );
    EXIT_ON_FAILED( L"Failed CreateBindCtx()" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CMoniker::InitializeBindContext()" );
    return( bSuccess );

}   // CMoniker::InitializeBindContext()


//+----------------------------------------------------------------
//
//  Function:   CMoniker::GetTimeOfLastChange
//
//  Synopsis:   Request the time-of-last-change from our member
//              moniker.
//
//  Inputs:     A buffer into which to put the FILETIME.
//
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    None.
//
//+----------------------------------------------------------------


BOOL CMoniker::GetTimeOfLastChange( FILETIME* pft )
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;

    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Validate our state.

    if( !m_pIMoniker )
        EXIT( L"Cannot GetTimeOfLastChange on a NULL moniker" );

    // Get the time from the moniker.

    m_hr = m_pIMoniker->GetTimeOfLastChange( m_pIBindCtx,
                                             NULL,
                                             pft );
    EXIT_ON_FAILED( L"Failed IMoniker::GetTimeOfLastChange()" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    DisplayErrors( bSuccess, L"CMoniker::GetTimeOfLastChange()" );
    return( bSuccess );

}   // CMoniker::GetTimeOfLastChange()



//+------------------------------------------------------------------------
//
//  Function:   CMoniker::BindToStorage
//
//  Synopsis:   Bind our member moniker to its Structured Storage object.
//
//  Inputs:     None.
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    None.
//
//+------------------------------------------------------------------------


BOOL CMoniker::BindToStorage()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;
    BIND_OPTS2 bind_opts;
    bind_opts.cbStruct = sizeof( BIND_OPTS2 );

    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Validate our state.

    if( !m_pIMoniker )
        EXIT( L"Cannot GetTimeOfLastChange on a NULL moniker" );

    // Release the IStorage interface if we have one.

    if( m_pIStorage )
    {
        m_pIStorage->Release();
        m_pIStorage = NULL;
    }

    // Get the bind_opts and set the flags for StgOpenStorage.

    m_hr = m_pIBindCtx->GetBindOptions( (LPBIND_OPTS) &bind_opts );
    EXIT_ON_FAILED( L"Failed IBindCtx::GetBindOptions" );

    bind_opts.grfMode = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT;

    m_hr = m_pIBindCtx->SetBindOptions( (LPBIND_OPTS) &bind_opts );
    EXIT_ON_FAILED( L"Failed IBindCtx::SetBindOptions" );


    // Bind to the storage.

    m_hr = m_pIMoniker->BindToStorage( m_pIBindCtx,
                                       NULL,
                                       IID_IStorage,
                                       (void **) &m_pIStorage );
    EXIT_ON_FAILED( L"Failed IMoniker::BindToStorage()" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    // Release the Storage if we got it.

    if( m_pIStorage )
    {
        m_pIStorage->Release();
        m_pIStorage = NULL;
    }

    DisplayErrors( bSuccess, L"CMoniker::BindToStorage()" );
    return( bSuccess );

}   // CMoniker::BindToStorage()


//+-------------------------------------------------------------------
//
//  Function:   CMoniker::BindToObject
//
//  Synopsis:   Bind to our member moniker's object.
//
//  Inputs:     None.
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    None.
//
//  Notes:      Since the member moniker represents a storage with no
//              associated server, BindToObject will fail.  We will
//              consider it a success if the failure is do to an
//              object-related problem, rather than a Storage-related
//              problem.
//
//+-------------------------------------------------------------------

BOOL CMoniker::BindToObject()
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;
    IUnknown* pUnk = NULL;

    //  -----
    //  Begin
    //  -----

    m_hr = S_OK;

    // Validate our state.

    if( !m_pIMoniker )
        EXIT( L"Cannot bind to an object with a NULL moniker" );

    // Bind to the object.

    m_hr = m_pIMoniker->BindToObject( m_pIBindCtx,
                                      NULL,
                                      IID_IUnknown,
                                      (void **) &pUnk );

    // If the bind succeeded, or failed for a valid reason,
    // then return Success to the caller.

    if( SUCCEEDED( m_hr )
        ||
        ( m_hr = MK_E_INVALIDEXTENSION ) // No handler for ".tmp" files.
      )
    {
        bSuccess = TRUE;
    }
    else
    {
        EXIT( L"Failed BindToObject" );
    }

    //  ----
    //  Exit
    //  ----

Exit:

    //  If we got an IUnknown interface on the Bind, release it.

    if( pUnk )
    {
        pUnk->Release();
        pUnk = NULL;
    }

    DisplayErrors( bSuccess, L"CMoniker::BindToObject()" );
    return( bSuccess );

}   // CMoniker::BindToObject()


//+-------------------------------------------------------------------
//
//  Function:   CMoniker::GetTemporaryStorageTime
//
//  Synopsis:   Get the time from the link source file
//              (identified by m_wszTemporaryStorage).
//
//  Inputs:     A buffer in which to put the FILETIME structure.
//
//  Outputs:    TRUE if successful, FALSE otherwise.
//
//  Effects:    None.
//
//+-------------------------------------------------------------------


BOOL CMoniker::GetTemporaryStorageTime( FILETIME * pft)
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;
    HANDLE hFile = NULL;

    //  -----
    //  Begin
    //  -----

    m_hr = NOERROR;

    //  Get a handle to the file.

    hFile = CreateFile( m_wszTemporaryStorage,  // File name
                        GENERIC_READ,           // Desired access
                        FILE_SHARE_READ,        // Share mode
                        NULL,                   // Security attributes
                        OPEN_EXISTING,          // Creation distribution
                        0L,                     // Flags & Attributes
                        NULL );                 // hTemplateFile
    if( hFile == NULL )
    {
        m_hr = (HRESULT) GetLastError();
        EXIT( L"Failed call to CreateFile()" );
    }

    // Get the time on the file.

    if( !GetFileTime(   hFile,  // File to check
                        NULL,   // Create Time
                        NULL,   // Access Time
                        pft )   // Write Time
      )
    {
        m_hr = (HRESULT) GetLastError();
        EXIT( L"Failed call to GetFileTime()" );
    }

    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    // Close the file if we opened it.

    if( hFile )
    {
        CloseHandle( hFile );
        hFile = NULL;
    }


    DisplayErrors( bSuccess, L"CMoniker::GetTemporaryStorageTime()" );
    return( bSuccess );

}   // CMoniker::GetTemporaryStorageTime()


//+------------------------------------------------------------------------
//
//  Function:   CMoniker::TouchTemporaryStorage
//
//  Synopsis:   Set the Access time on the link source file.
//
//  Inputs:     None.
//  
//  Output:     TRUE if successful, FALSE otherwise.
//
//  Effects:    The link source file (identified by m_wszTemporaryStorage)
//              has its Access time set to the current time.
//
//+------------------------------------------------------------------------


BOOL CMoniker::TouchTemporaryStorage( )
{
    //  ---------------
    //  Local Variables
    //  ---------------

    BOOL bSuccess = FALSE;
    HANDLE hFile = NULL;
    STATSTG statStorage;
    FILETIME ftNow;

    //  -----
    //  Begin
    //  -----

    m_hr = NOERROR;

    // Open the root Storage.

    m_hr = StgOpenStorage(  m_wszTemporaryStorage,
                            NULL,
                            STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT,
                            NULL,
                            0L,
                            &m_pIStorage );
    EXIT_ON_FAILED( L"Failed StgOpenStorage()" );

    // Get the current time.

    m_hr = CoFileTimeNow( &ftNow );
    EXIT_ON_FAILED( L"Failed CoFileTimeNow()" );

    // Set the access time

    m_pIStorage->SetElementTimes(   NULL,   // Set the storage itself
                                    NULL,   // Create time
                                    NULL,   // Access time
                                    &ftNow );
    EXIT_ON_FAILED( L"Failed IStorage::SetTimes()" );


    bSuccess = TRUE;

    //  ----
    //  Exit
    //  ----

Exit:

    // If we got the storage, release it.

    if( m_pIStorage )
    {
        m_pIStorage->Release();
        m_pIStorage = NULL;
    }

    DisplayErrors( bSuccess, L"CMoniker::TouchTemporaryStorage()" );
    return( bSuccess );

}   // CMoniker::TouchTemporaryStorage()



#ifdef _FUTURE_

/*
BOOL CMoniker::OpenLinkTrackingRegistryKey()
{

    BOOL bSuccess = FALSE;
    DWORD dwDisposition = 0L;
    long lResult = 0L;

    m_hr = S_OK;

    lResult = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
                            OLETRACKING_KEY,
                            0L,
                            KEY_ALL_ACCESS,
                            &m_hkeyLinkTracking
                         );

    if( lResult != ERROR_SUCCESS
        &&
        lResult != ERROR_FILE_NOT_FOUND
      )
    {
        m_hr = (HRESULT) lResult;
        EXIT( L"Failed RegOpenKeyEx()" );
    }


    bSuccess = TRUE;

Exit:

    DisplayErrors( bSuccess, L"CMoniker::OpenLinkTrackingRegistryKey()" );
    return( bSuccess );


}   // CMoniker::OpenLinkTrackingRegistryKey()


BOOL CMoniker::CreateLinkTrackingRegistryKey()
{

    BOOL bSuccess = FALSE;
    HKEY hkey = NULL;
    DWORD dwDisposition = 0L;
    long lResult = 0L;

    m_hr = S_OK;

    if( m_hkeyLinkTracking )
        CloseLinkTrackingRegistryKey();

    lResult = RegCreateKeyEx(  HKEY_LOCAL_MACHINE,
                               OLETRACKING_KEY,
                               0L,
                               NULL,
                               REG_OPTION_NON_VOLATILE,
                               KEY_ALL_ACCESS,
                               NULL,
                               &m_hkeyLinkTracking,
                               &dwDisposition
                            );

    if( lResult != ERROR_SUCCESS )
    {
        m_hr = (HRESULT) lResult;
        EXIT( L"Failed RegCreateKeyEx()" );
    }

    bSuccess = TRUE;

Exit:

    DisplayErrors( bSuccess, L"CMoniker::CreateLinkTrackingRegistryKey()" );
    return( bSuccess );

}   // CMoniker::CreateLinkTrackingRegistryKey()



BOOL CMoniker::CloseLinkTrackingRegistryKey()
{
    m_hr = S_OK;

    if( m_hkeyLinkTracking )
        RegCloseKey( m_hkeyLinkTracking );

    m_hkeyLinkTracking = NULL;

    return TRUE;

}   // CMoniker::CloseLinkTrackingRegistryKey()



BOOL CMoniker::SaveRegistryTrackFlags()
{
    BOOL bSuccess = FALSE;

    long lResult = 0L;
    DWORD dwType = 0L;
    DWORD dwcbData = sizeof( m_dwTrackFlags );
    

    m_hr = S_OK;

    if( !OpenLinkTrackingRegistryKey() )
        EXIT( L"Could not open the registry" );

    lResult = RegQueryValueEx( m_hkeyLinkTracking,
                               OLETRACKING_FILEMONIKER_VALUE,
                               NULL,
                               &dwType,
                               (LPBYTE) &m_dwTrackFlags,
                               &dwcbData );

    if( lResult != ERROR_SUCCESS )
    {
        CloseLinkTrackingRegistryKey();
        
        if( lResult != ERROR_FILE_NOT_FOUND )
        {
            m_hr = (HRESULT) lResult;
            EXIT( L"Failed RegQueryValueEx()" );
        }
    }


    bSuccess = TRUE;

Exit:

    DisplayErrors( bSuccess, L"CMoniker::SaveRegistryTrackFlags()" );
    return( bSuccess );

}   // CMoniker::SaveRegistryTrackFlags()



BOOL CMoniker::DeleteRegistryTrackFlags()
{
    BOOL bSuccess = FALSE;

    long lResult = 0L;
    DWORD dwType = 0L;
    DWORD dwcbData = sizeof( m_dwTrackFlags );
    

    m_hr = S_OK;

    if( !CreateLinkTrackingRegistryKey() )
        EXIT( L"Could not open the registry" );


    lResult = RegDeleteValue(   m_hkeyLinkTracking,
                                OLETRACKING_FILEMONIKER_VALUE );


    if( lResult != ERROR_SUCCESS
        &&
        lResult != ERROR_FILE_NOT_FOUND
      )
    {
        if( lResult != ERROR_FILE_NOT_FOUND )
        {
            m_hr = (HRESULT) lResult;
            EXIT( L"Failed RegDeleteValue()" );
        }
    }

    bSuccess = TRUE;

Exit:

    CloseLinkTrackingRegistryKey();

    DisplayErrors( bSuccess, L"CMoniker::DeleteRegistryTrackFlags()" );
    return( bSuccess );

}   // CMoniker::DeleteRegistryTrackFlags()



BOOL CMoniker::RestoreRegistryTrackFlags()
{
    BOOL bSuccess = FALSE;
    long lResult = 0L;


    m_hr = S_OK;

    // If the registry key doesn't exist, then there's no flags
    // to restore.

    if( m_hkeyLinkTracking )
    {

        lResult = RegSetValueEx( m_hkeyLinkTracking,
                                 OLETRACKING_FILEMONIKER_VALUE,
                                 0L,
                                 REG_DWORD,
                                 (LPBYTE) &m_dwTrackFlags,
                                 sizeof( m_dwTrackFlags )
                               );

        if( lResult != ERROR_SUCCESS )
        {
            m_hr = (HRESULT) lResult;
            EXIT( L"Failed RegSetValueEx()" );
        }

        CloseLinkTrackingRegistryKey();
        
    }
    

    bSuccess = TRUE;

Exit:

    DisplayErrors( bSuccess, L"CMoniker::RestoreRegistryTrackFlags()" );
    return( bSuccess );

}   // CMoniker::RestoreRegistryTrackFlags()

CMoniker::SetTrackFlagsInRegistry( DWORD dwTrackFlags )
{
    BOOL bSuccess = FALSE;
    long lResult = 0L;
    HKEY hkey = NULL;


    m_hr = S_OK;

    if( !CreateLinkTrackingRegistryKey() )
        EXIT( L"Could not create registry key" );

    lResult = RegSetValueEx( m_hkeyLinkTracking,
                             OLETRACKING_FILEMONIKER_VALUE,
                             0L,
                             REG_DWORD,
                             (LPBYTE) &dwTrackFlags,
                             sizeof( dwTrackFlags )
                           );

    if( lResult != ERROR_SUCCESS )
    {
        m_hr = (HRESULT) lResult;
        EXIT( L"Failed RegSetValueEx()" );
    }
    

    bSuccess = TRUE;

Exit:

    DisplayErrors( bSuccess, L"CMoniker::SetTrackFlagsInRegistry()" );
    return( bSuccess );

}   // CMoniker::SetTrackFlagsInRegistry()


BOOL CMoniker::CreateFileMoniker()
{

    BOOL bSuccess = FALSE;

    m_hr = S_OK;

    // Free any existing IMoniker.

    if( m_pIMoniker )
    {
        m_pIMoniker->Release();
        m_pIMoniker = NULL;
    }

    // Create a root storage.

    if( !CreateTemporaryStorage() )
        EXIT( L"Could not create a temporary storage" );

    // Create a default File Moniker on that root storage.

    m_hr = ::CreateFileMoniker( m_wszTemporaryStorage, &m_pIMoniker );
    EXIT_ON_FAILED( L"Failed CreateFileMoniker" );


    bSuccess = TRUE;


Exit:

    DisplayErrors( bSuccess, L"CMoniker::CreateFileMoniker" );
    return( bSuccess );

}   // CMoniker::CreateFileMoniker()
*/
#endif // _FUTURE_