NT4/private/ole32/com/moniker2/cfilemon.cxx
2020-09-30 17:12:29 +02:00

6710 lines
172 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1993 - 1993.
//
// File: cfilemon.cxx
//
// Contents:
//
// Classes:
//
// Functions:
//
// History: 12-27-93 ErikGav Commented
// 01-04-94 KevinRo Serious modifications
// UNC paths are used directly
// Added UNICODE extents
// 03-18-94 BruceMa #5345 Fixed Matches to parse
// offset correctly
// 03-18-94 BruceMa #5346 Fixed error return on invalid CLSID
// string
// 05-10-94 KevinRo Added Long Filename/8.3 support so
// downlevel guys can see new files
// 06-14-94 Rickhi Fix type casting
// 22-Feb-95 BruceMa Account for OEM vs. ANSI code pages
// 01-15-95 BillMo Add tracking on x86 Windows.
// 19-Sep-95 BruceMa Change ::ParseDisplayName to try the
// object first and then the class
// 10-13-95 stevebl threadsafety
// 10-20-95 MikeHill Updated to support new CreateFileMonikerEx API.
// 11-15-95 MikeHill Use BIND_OPTS2 when Resolving a ShellLink object.
// 11-22-95 MikeHill - In ResolveShellLink, always check for a null path
// returned from IShellLink::Resolve.
// - Also changed m_fPathSetInShellLink to
// m_fShellLinkInitialized.
// - In RestoreShellLink & SetPathShellLink,
// only early-exit if m_fShellLinkInitialized.
// 12-01-95 MikeHill - Validate bind_opts2.dwTrackFlags before using it.
// - For Cairo, do an unconditional ResolveShellLink
// for BindToObject/Storage
// - Don't do a Resolve in GetTimeOfLastChange.
//
//----------------------------------------------------------------------------
#include <ole2int.h>
#include "cbasemon.hxx"
#include "extents.hxx"
#include "cfilemon.hxx"
#include "ccompmon.hxx"
#include "cantimon.hxx"
#include "mnk.h"
#include <olepfn.hxx>
#include <rotdata.hxx>
#ifdef _TRACKLINK_
#include <itrkmnk.hxx>
#endif
#define LPSTGSECURITY LPSECURITY_ATTRIBUTES
#include "..\..\..\stg\h\dfentry.hxx"
DECLARE_INFOLEVEL(mnk)
//
// The following value is used to determine the average string size for use
// in optimizations, such as copying strings to the stack.
//
#define AVERAGE_STR_SIZE (MAX_PATH)
//
// Determine an upper limit on the length of a path. This is a sanity check
// so that we don't end up reading in megabyte long paths. The 16 bit code
// used to use INT_MAX, which is 32767. That is reasonable, plus old code
// will still work.
//
#define MAX_MBS_PATH (32767)
// function prototype
// Special function from ROT
HRESULT GetObjectFromLocalRot(
IMoniker *pmk,
IUnknown **ppvUnk);
//+---------------------------------------------------------------------------
//
// Function: ReadAnsiStringStream
//
// Synopsis: Reads a counted ANSI string from the stream.
//
// Effects: Old monikers store paths in ANSI characters. This routine
// reads ANSI strings.
//
//
// Arguments: [pStm] -- Stream to read from
// [pszAnsiPath] -- Reference to the path variable.
// [cbAnsiPath] -- Reference to number of bytes read
//
// Requires:
//
// Returns:
// pszAnsiPath was allocated using PrivMemAlloc. May return NULL
// if there were zero bytes written.
//
// cbAnsiPath is the total size of the buffer allocated
//
// This routine treats the string as a blob. There may be more
// than one NULL character (ItemMonikers, for example, append
// UNICODE strings to the end of existing strings.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-08-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT ReadAnsiStringStream( IStream *pStm,
LPSTR & pszAnsiPath,
USHORT &cbAnsiPath)
{
HRESULT hresult;
pszAnsiPath = NULL;
ULONG cbAnsiPathTmp;
cbAnsiPath = 0;
hresult = StRead(pStm, &cbAnsiPathTmp, sizeof(ULONG));
if (FAILED(hresult))
{
return hresult;
}
//
// If no bytes exist in the stream, thats OK.
//
if (cbAnsiPathTmp == 0)
{
return NOERROR;
}
//
// Quick sanity check against the size of the string
//
if (cbAnsiPathTmp > MAX_MBS_PATH)
{
//
// String length didn't make sense.
//
return E_UNSPEC;
}
cbAnsiPath = (USHORT) cbAnsiPathTmp;
//
// This string is read in as char's.
//
// NOTE: cb includes the null terminator. Therefore, we don't add
// extra room. Also, the read in string is complete. No additional
// work needed.
//
pszAnsiPath = (char *)PrivMemAlloc(cbAnsiPath);
if (pszAnsiPath == NULL)
{
return(E_OUTOFMEMORY);
}
hresult = StRead(pStm, pszAnsiPath, cbAnsiPath);
if (FAILED(hresult))
{
goto errRtn;
}
return NOERROR;
errRtn:
if (pszAnsiPath != NULL)
{
PrivMemFree( pszAnsiPath);
pszAnsiPath = NULL;
}
cbAnsiPath = 0;
return hresult;
}
//+---------------------------------------------------------------------------
//
// Function: WriteAnsiStringStream
//
// Synopsis: Writes a counted ANSI string to the stream.
//
// Effects: Old monikers store paths in ANSI characters. This routine
// writes ANSI strings.
//
// Arguments: [pStm] -- Stream to serialize to
// [pszAnsiPath] -- AnsiPath to serialize
// [cbAnsiPath] -- Count of bytes in ANSI path
//
// Requires:
//
// cbAnsiPath is the length of the cbAnsiPath buffer, INCLUDING the
// terminating NULL.
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-08-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT WriteAnsiStringStream( IStream *pStm, LPSTR pszAnsiPath ,ULONG cbAnsiPath)
{
HRESULT hr;
ULONG cb = 0;
// The >= is because there may be an appended unicode string
Assert( (pszAnsiPath == NULL) || (cbAnsiPath >= strlen(pszAnsiPath)+1) );
if (pszAnsiPath != NULL)
{
cb = cbAnsiPath;
//
// We don't allow the write of arbitrary length strings, since
// we won't be able to read them back in.
//
if (cb > MAX_MBS_PATH)
{
Assert(!"Attempt to write cbAnsiPath > MAX_MBS_PATH" );
return(E_UNSPEC);
}
//
// Optimization for the write
// if possible, do a single write instead of two by using a temp
// buffer.
if (cb <= AVERAGE_STR_SIZE-4)
{
char szBuf[AVERAGE_STR_SIZE];
*((ULONG FAR*) szBuf) = cb;
//
// cb is the string length including the NULL. A memcpy is
// used instead of a strcpy
//
memcpy(szBuf+sizeof(ULONG), pszAnsiPath, cb);
hr = pStm->Write((VOID FAR *)szBuf, cb+sizeof(ULONG), NULL);
return hr;
}
}
if (hr = pStm->Write((VOID FAR *)&cb, sizeof(ULONG), NULL))
{
return hr;
}
if (pszAnsiPath == NULL)
{
hr = NOERROR;
}
else
{
hr = pStm->Write((VOID FAR *)pszAnsiPath, cb, NULL);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: CopyPathFromUnicodeExtent
//
// Synopsis: Given a path to a UNICODE moniker extent, return the path and
// its length.
//
// Effects:
//
// Arguments: [pExtent] --
// [ppPath] --
// [cbPath] --
//
// Requires:
//
// Returns: ppPath is a copy of the string (NULL terminated)
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-08-94 kevinro Created
//
// Notes:
//
// m_szPath should be freed using PrivMemFree();
//
//----------------------------------------------------------------------------
HRESULT
CopyPathFromUnicodeExtent(MONIKEREXTENT UNALIGNED *pExtent,
LPWSTR & pwcsPath,
USHORT & ccPath)
{
//
// The path isn't NULL terminated in the serialized format. Add enough
// to have NULL termination.
//
pwcsPath =(WCHAR *)PrivMemAlloc(pExtent->cbExtentBytes + sizeof(WCHAR));
if (pwcsPath == NULL)
{
return(E_OUTOFMEMORY);
}
memcpy(pwcsPath,pExtent->achExtentBytes,pExtent->cbExtentBytes);
//
// The length divided by the size of the character yields the count
// of characters.
//
ccPath = ((USHORT)(pExtent->cbExtentBytes)) / sizeof(WCHAR);
//
// NULL terminate the string.
//
pwcsPath[ccPath] = 0;
return(NOERROR);
}
//+---------------------------------------------------------------------------
//
// Function: CopyPathToUnicodeExtent
//
// Synopsis: Given a UNICODE path and a length, return a MONIKEREXTENT
//
// Effects:
//
// Arguments: [pwcsPath] -- UNICODE string to put in extent
// [ccPath] -- Count of unicode characters
// [pExtent] -- Pointer reference to recieve buffer
//
// Requires:
//
// Returns:
// pExtent allocated using PrivMemAlloc
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-09-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT CopyPathToUnicodeExtent(LPWSTR pwcsPath,ULONG ccPath,LPMONIKEREXTENT &pExtent)
{
pExtent = (LPMONIKEREXTENT)PrivMemAlloc(MONIKEREXTENT_HEADERSIZE +
(ccPath * sizeof(WCHAR)));
if (pExtent == NULL)
{
return(E_OUTOFMEMORY);
}
pExtent->cbExtentBytes = ccPath * sizeof(WCHAR);
pExtent->usKeyValue = mnk_UNICODE;
memcpy(pExtent->achExtentBytes,pwcsPath,ccPath*sizeof(WCHAR));
return(NOERROR);
}
INTERNAL_(DWORD) GetMonikerType ( LPMONIKER pmk )
{
GEN_VDATEIFACE (pmk, 0);
DWORD dw;
CBaseMoniker FAR* pbasemk;
if (NOERROR == pmk->QueryInterface(IID_IInternalMoniker,(LPVOID FAR*)&pbasemk))
{
pbasemk->IsSystemMoniker(&dw);
((IMoniker *) pbasemk)->Release();
return dw;
}
return 0;
}
INTERNAL_(BOOL) IsReduced ( LPMONIKER pmk )
{
DWORD dw = GetMonikerType(pmk);
if (dw != 0)
{
CCompositeMoniker *pCMk;
if ((pCMk = IsCompositeMoniker(pmk)) != NULL)
{
return pCMk->m_fReduced;
}
else
{
return TRUE;
}
}
return FALSE;
}
INTERNAL_(CFileMoniker *) IsFileMoniker ( LPMONIKER pmk )
{
CFileMoniker *pCFM;
if ((pmk->QueryInterface(CLSID_FileMoniker, (void **)&pCFM)) == S_OK)
{
// we release the AddRef done by QI, but still return the ptr.
pCFM->Release();
return pCFM;
}
// dont rely on user implementations to set pCFM NULL on failed QI
return NULL;
}
/*
* Implementation of CFileMoniker
*
*
*
*
*/
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::CFileMoniker
//
// Synopsis: Constructor for CFileMoniker
//
// Effects:
//
// Arguments: [void] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-09-94 kevinro Modified
//
// Notes:
//
//----------------------------------------------------------------------------
CFileMoniker::CFileMoniker( void ) CONSTR_DEBUG
{
#ifdef _TRACKLINK_
_tfm.SetParent(this);
#endif
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::CFileMoniker(%x)\n",this));
m_szPath = NULL;
m_ccPath = 0;
m_pszAnsiPath = NULL;
m_cbAnsiPath = 0;
m_cAnti = 0;
m_ole1 = undetermined;
m_clsid = CLSID_NULL;
m_fClassVerified = FALSE;
m_fUnicodeExtent = FALSE;
m_fHashValueValid = FALSE;
m_dwHashValue = 0x12345678;
m_endServer = DEF_ENDSERVER;
#ifdef _TRACKLINK_
m_pShellLink = NULL;
m_fTrackingEnabled = FALSE;
m_fSaveShellLink = FALSE;
m_fReduceEnabled = FALSE;
m_fDirty = FALSE;
m_fIsTracking = FALSE; // Is this a tracking moniker?
m_fShellLinkInitialized = FALSE; // Has IShellLink->SetPath been called?
#ifdef _CAIRO_
m_pShellLinkTracker = NULL;
#endif // _CAIRO_
#endif // _TRACKLINK_
//
// CoQueryReleaseObject needs to have the address of the this objects
// query interface routine.
//
if (adwQueryInterfaceTable[QI_TABLE_CFileMoniker] == 0)
{
adwQueryInterfaceTable[QI_TABLE_CFileMoniker] =
**(DWORD **)((IMoniker *)this);
}
wValidateMoniker();
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::ValidateMoniker
//
// Synopsis: As a debugging routine, this will validate the contents
// as VALID
//
// Effects:
//
// Arguments: (none)
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-12-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
#if DBG == 1
void
CFileMoniker::ValidateMoniker()
{
CLock lck(m_mxs); // protect all internal state
wValidateMoniker();
}
void
CFileMoniker::wValidateMoniker()
{
//
// A valid moniker should have character counts set correctly
// m_ccPath holds the number of characters in m_szPath
//
if (m_szPath != NULL)
{
Assert(m_ccPath == lstrlenW(m_szPath));
Assert((m_endServer == DEF_ENDSERVER) || (m_endServer <= m_ccPath));
}
else
{
Assert(m_ccPath == 0);
Assert(m_endServer == DEF_ENDSERVER);
}
//
// If the ANSI version of the path already exists, then validate that
// its buffer length is the same as its strlen
//
if (m_pszAnsiPath != NULL)
{
Assert(m_cbAnsiPath == strlen(m_pszAnsiPath) + 1);
}
else
{
Assert(m_cbAnsiPath == 0);
}
//
// There is a very very remote chance that this might fail when it
// shouldn't. If it happens, congratulations, you win!
//
if (!m_fHashValueValid)
{
Assert(m_dwHashValue == 0x12345678);
}
//
// If there is an extent, then we would be very surprised to see it
// have a zero size.
//
if (m_fUnicodeExtent)
{
Assert(m_ExtentList.GetSize() >= sizeof(ULONG));
}
}
#endif
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::~CFileMoniker
//
// Synopsis:
//
// Effects:
//
// Arguments: [void] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-09-94 kevinro Modified
//
// Notes:
//
//----------------------------------------------------------------------------
CFileMoniker::~CFileMoniker( void )
{
// no locking needed here, since we are going away, and nobody should
// have any references to us.
wValidateMoniker();
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::~CFileMoniker(%x) m_szPath(%ws)\n",
this,
m_szPath?m_szPath:L"<No Path>"));
if( m_szPath != NULL)
{
PrivMemFree(m_szPath);
}
if (m_pszAnsiPath != NULL)
{
PrivMemFree(m_pszAnsiPath);
}
#ifdef _TRACKLINK_
if (m_pShellLink != NULL)
{
m_pShellLink->Release();
}
#ifdef _CAIRO_
if (m_pShellLinkTracker != NULL)
{
m_pShellLinkTracker->Release();
}
#endif // _CAIRO_
#endif // _TRACKLINK_
}
void UpdateClsid (LPCLSID pclsid)
{
CLSID clsidNew = CLSID_NULL;
// If a class has been upgraded, we want to use
// the new class as the server for the link.
// The old class's server may no longer exist on
// the machine. See Bug 4147.
if (NOERROR == OleGetAutoConvert (*pclsid, &clsidNew))
{
*pclsid = clsidNew;
}
else if (NOERROR == CoGetTreatAsClass (*pclsid, &clsidNew))
{
*pclsid = clsidNew;
}
}
/*
When IsOle1Class determines that the moniker should now be an OLE2 moniker
and sets m_ole1 = ole2, it does NOT set m_clsid to CLSID_NULL.
This is intentional. This ensures that when GetClassFileEx is called, it
will be called with this CLSID. This allows BindToObject, after calling
GetClassFileEx, to map the 1.0 CLSID, via UpdateClsid(), to the correct
2.0 CLSID. If m_clsid was NULLed, GetClassFileEx would have no
way to determine the 1.0 CLSID (unless pattern matching worked).
Note that this means that the moniker may have m_ole1==ole2 and
m_clsid!=CLSID_NULL. This may seem a little strange but is intentional.
The moniker is persistently saved this way, which is also intentional.
*/
INTERNAL_(BOOL) CFileMoniker::IsOle1Class ( LPCLSID pclsid )
{
wValidateMoniker();
{
if (m_fClassVerified)
{
if (m_ole1 == ole1)
{
*pclsid = m_clsid;
return TRUE;
}
if (m_ole1 == ole2)
{
return FALSE;
}
}
//
// BUGBUG: (KevinRo)If GetClassFileEx fails, then we have not really
// verified the class. Is this a problem?
//
m_fClassVerified = TRUE;
HRESULT hr = GetClassFileEx (m_szPath, pclsid, m_clsid);
if (NOERROR== hr)
{
UpdateClsid (pclsid);
if (CoIsOle1Class(*pclsid))
{
m_clsid = *pclsid;
m_ole1 = ole1;
return TRUE;
}
else
{
m_ole1 = ole2;
// Do not set m_clsid to CLSID_NULL. See note above.
}
}
return m_ole1==ole1;
}
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GetShellLink, private
//
// Synopsis: Ensure that m_pShellLink is valid, or return error.
//
// Arguments: [void]
//
// Algorithm: if m_pShellLink is already valid return S_OK, else
// sort of CoCreateInstance a shell link using the routine
// OleGetShellLink in com\util\dynload.cxx.
//
// Notes:
//
//----------------------------------------------------------------------------
#ifdef _TRACKLINK_
extern VOID * OleGetShellLink();
INTERNAL CFileMoniker::GetShellLink()
{
if (m_pShellLink == NULL)
m_pShellLink = (IShellLink*) OleGetShellLink();
return(m_pShellLink != NULL ? S_OK : E_FAIL);
}
#endif
//+-------------------------------------------------------------------
//
// Member: CFileMoniker::EnableTracking
//
// Synopsis: Creates/destroys the information neccessary to
// track objects on BindToObject calls.
//
// Arguments: [pmkToLeft] -- moniker to left.
//
// [ulFlags] -- flags to control behaviour of tracking
// extensions.
//
// Combination of:
// OT_READTRACKINGINFO -- get id from source
// OT_ENABLESAVE -- enable tracker to be saved in
// extents.
// OT_DISABLESAVE -- disable tracker to be saved.
// OT_DISABLETRACKING -- destroy any tracking info
// and prevent tracking and save of
// tracking info.
//
// OT_DISABLESAVE takes priority of OT_ENABLESAVE
// OT_READTRACKINGINFO takes priority over
// OT_DISABLETRACKING
//
// OT_ENABLEREDUCE -- enable new reduce functionality
// OT_DISABLEREDUCE -- disable new reduce functionality
//
// OT_MAKETRACKING -- make the moniker inherently tracking;
// then tracking need not be enabled, and cannot be disabled.
//
// Returns: HResult
// Success is SUCCEEDED(hr)
//
// Modifies:
//
//--------------------------------------------------------------------
#ifdef _TRACKLINK_
STDMETHODIMP CFileMoniker::EnableTracking(IMoniker *pmkToLeft, ULONG ulFlags)
{
//
// - if the shellink does not exist, and shellink creation has not
// been disabled by the OLELINK (using private i/f ITrackingMoniker)
// then create one and save in extent list. (The EnabledTracking(FALSE)
// call prevents the file moniker from creating the shellink on save.)
// - if the shellink exists, update the ShellLink in the extent list
//
//
// BUGBUG: BillMo, Optimization. If the path is not extant (e.g. "New1")
// then we shouldn't even bother to create the link.
//
CLock lck(m_mxs); // protect all internal state
//
// create an in memory shell link object if needed.
//
HRESULT hr = S_OK;
if (ulFlags & OT_ENABLESAVE)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking() -- enable save\n",
this));
m_fSaveShellLink = TRUE;
}
if (ulFlags & OT_DISABLESAVE)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking() -- disable save\n",
this));
m_fSaveShellLink = FALSE;
}
if (ulFlags & OT_ENABLEREDUCE)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking() -- enable reduce\n",
this));
m_fReduceEnabled = TRUE;
}
if (ulFlags & OT_DISABLEREDUCE)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking() -- disable reduce\n",
this));
m_fReduceEnabled = FALSE;
}
if (ulFlags & OT_READTRACKINGINFO)
{
// Trigger the ShellLink object to get file attributes.
hr = SetPathShellLink();
if( FAILED( hr ))
{
if( m_pShellLink )
{
m_pShellLink->Release();
m_pShellLink = NULL;
}
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking(%ls) -- m_pShellLink->SetPath failed %08X.\n",
this,
m_szPath,
hr));
} // ShellLink->SetPath ... if (FAILED(hr))
else
{
m_fTrackingEnabled = TRUE;
hr = S_OK;
}
} // if (ulFlags & OT_READTRACKINGINFO)
else
if (ulFlags & OT_DISABLETRACKING)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking() -- disabletracking\n",
this));
// If this is a tracking moniker, the Shell Link cannot
// be deleted.
if( !m_fIsTracking )
{
if (m_pShellLink != NULL)
m_pShellLink->Release();
m_pShellLink = NULL;
m_ExtentList.DeleteExtent(mnk_ShellLink);
}
m_fSaveShellLink = FALSE;
m_fTrackingEnabled = FALSE;
} // else if (ulFlags & OT_DISABLETRACKING)
#ifdef _CAIRO_
if( ulFlags & OT_MAKETRACKING )
{
// Initialize the shell link for tracking. As such, it will
// always perform enhanced tracking, regardless of the
// m_fTrackingEnabled or m_fReduceEnabled flags.
// Get the ShellLinkTracker interface.
hr = GetShellLinkTracker();
if( FAILED(hr) )
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking(%ls) -- Could not get ShellLinkTracker (%08X).\n",
this,
m_szPath,
hr));
}
else
{
Assert( m_pShellLink != NULL );
Assert( m_pShellLinkTracker != NULL );
// Initialize the Shell Link object with respect to Tracking.
// Note that the Track Flags (necessary for this initialization)
// are embedded in the ulFlags from our caller.
hr = m_pShellLinkTracker->Initialize( OT_2_TRACK_FLAGS(ulFlags) );
if( FAILED(hr) )
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking(%ls) -- Could not initialize ShellLinkTracker %08X.\n",
this,
m_szPath,
hr));
}
else
{
// We've successfully made this a tracking moniker.
m_fIsTracking = TRUE;
// Set the path in the ShellLink object.
hr = SetPathShellLink();
if( FAILED( hr ))
{
// Even though we couldn't set the path, this is
// still a Tracking moniker. We might be able to
// find and set the path later.
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::EnableTracking(%ls) -- SetPath in ShellLink %08X.\n",
this,
m_szPath,
hr));
}
} // hr = m_pShellLinkTracker->Initialize ... if( FAILED(hr) )
} // hr = GetShellLinkTracker() ... if( FAILED(hr) )
} // if( ulFlags & OT_MAKETRACKING )
#endif // _CAIRO_
return(hr);
}
#endif
/*
* Storage of paths in file monikers:
*
* A separate unsigned integer holds the count of .. at the
* beginning of the path, so the canononical form of a file
* moniker contains this count and the "path" described above,
* which will not contain "..\" or ".\".
*
* It is considered an error for a path to contain ..\ anywhere
* but at the beginning. I assume that these will be taken out by
* ParseUserName.
*/
inline BOOL IsSeparator( WCHAR ch )
{
return (ch == L'\\' || ch == L'/' || ch == L':');
}
#ifdef MAC_REVIEW
Needs to be mac'ifyed
#endif
//+---------------------------------------------------------------------------
//
// Function: EatDotDDots
//
// Synopsis: Remove directory prefixes
//
// Effects:
// Removes and counts the number of 'dot dots' on a path. It also
// removes the case where the leading characters are '.\', which
// is the 'current' directory.
//
// Arguments: [pch] --
// [cDoubleDots] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 3-02-94 kevinro Commented
// 3-21-95 kevinro Fixed case where path is ..foo
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL EatDotDots ( LPCWSTR *ppch, USHORT FAR *pcDoubleDots )
{
// passes over ..'s (or ..\'s at the beginning of paths, and returns
// an integer counting the ..
LPWSTR pch = (LPWSTR) *ppch;
if (pch == NULL)
{
//
// NULL paths are alright
//
return(TRUE);
}
while (pch[0] == L'.')
{
//
// If the next character is a dot, the consume both, plus any
// seperator
//
if (pch[1] == L'.')
{
//
// If the next character is a seperator, then remove it also
// This handles the '..\' case.
//
if (IsSeparator(pch[2]))
{
pch += 3;
(*pcDoubleDots)++;
}
//
// If the next char is a NULL, then eat it and count a dotdot.
// This handles the '..' case where we want the parent directory
//
else if(pch[2] == 0)
{
pch += 2;
(*pcDoubleDots)++;
}
//
// Otherwise, we just found a '..foobar', which is a valid name.
// We can stop processing the string all together and be done.
//
else
{
break;
}
}
else if (IsSeparator(pch[1]))
{
//
// Found a .\ construct, eat the dot and the seperator
//
pch += 2;
}
else
{
//
// There is a dot at the start of the name. This is valid,
// since many file systems allow names to start with dots
//
break;
}
}
*ppch = pch;
return TRUE;
}
int CountSegments ( LPWSTR pch )
{
// counts the number of pieces in a path, after the first colon, if
// there is one
int n = 0;
LPWSTR pch1;
pch1 = pch;
while (*pch1 != L'\0' && *pch1 != L':') IncLpch(pch1);
if (*pch1 == ':') pch = ++pch1;
while (*pch != '\0')
{
while (*pch && IsSeparator(*pch)) pch++;
if (*pch) n++;
while (*pch && (!IsSeparator(*pch))) IncLpch(pch);
}
return n;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::Initialize
//
// Synopsis: Initializes data members.
//
// Effects: This one stores the path, its length, and the AntiMoniker
// count.
//
// Arguments: [cAnti] --
// [pszAnsiPath] -- Ansi version of path. May be NULL
// [cbAnsiPath] -- Number of bytes in pszAnsiPath buffer
// [szPathName] -- Path. Takes control of memory
// [ccPathName] -- Number of characters in Wide Path
// [usEndServer] -- Offset to end of server section
//
// Requires:
// szPathName must be allocated by PrivMemAlloc();
// This routine doesn't call EatDotDots. Therefore, the path should
// not include any leading DotDots.
//
// Returns:
// TRUE success
// FALSE failure
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-08-94 kevinro Modified
//
// Notes:
//
// There is at least one case where Initialize is called with a pre
// allocated string. This routine is called rather than the other.
// Removes an extra memory allocation, and the extra string scan
//
//----------------------------------------------------------------------------
INTERNAL_(BOOL)
CFileMoniker::Initialize ( USHORT cAnti,
LPSTR pszAnsiPath,
USHORT cbAnsiPath,
LPWSTR szPathName,
USHORT ccPathName,
USHORT usEndServer )
{
wValidateMoniker(); // Be sure we started with something
// we expected
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::Initialize(%x) szPathName(%ws)cAnti(%u) ccPathName(0x%x)\n",
this,
szPathName?szPathName:L"<NULL>",
cAnti,
ccPathName));
mnkDebugOut((DEB_ITRACE,
"\tpszAnsiPath(%s) cbAnsiPath(0x%x) usEndServer(0x%x)\n",
pszAnsiPath?pszAnsiPath:"<NULL>",
cbAnsiPath,
usEndServer));
Assert( (szPathName == NULL) || ccPathName == lstrlenW(szPathName) );
Assert( (pszAnsiPath == NULL) || cbAnsiPath == strlen(pszAnsiPath) + 1);
Assert( (usEndServer <= ccPathName) || (usEndServer == DEF_ENDSERVER) );
//
// It is possible to get Initialized twice.
// Be careful not to leak
//
if (m_szPath != NULL)
{
PrivMemFree(m_szPath); // OleLoadFromStream causes two inits
}
if (m_pszAnsiPath != NULL)
{
PrivMemFree(m_pszAnsiPath);
}
m_cAnti = cAnti;
m_pszAnsiPath = pszAnsiPath;
m_cbAnsiPath = cbAnsiPath;
m_szPath = szPathName;
m_ccPath = (USHORT)ccPathName;
m_endServer = usEndServer;
//
// m_ole1 and m_clsid where loaded in 'Load'. Really should get moved
// into here.
//
m_fClassVerified = FALSE;
// m_fUnicodeExtent gets set in DetermineUnicodePath() routine, so
// leave it alone here.
//
// We just loaded new strings. Hash value is no longer valid.
//
m_fHashValueValid = FALSE;
m_dwHashValue = 0x12345678;
//
// Notice that the extents are not initialized.
//
// The two cases are:
// 1) This is called as result of CreateFileMoniker, in which case
// no extents are created. The default constructor suffices.
//
// 2) This is called as result of ::Load(), in which case the extents
// have already been loaded.
//
wValidateMoniker();
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::Initialize
//
// Synopsis: This version of Initialize is called by CreateFileMoniker
//
// Effects:
//
// Arguments: [cAnti] -- Anti moniker count
// [szPathName] -- Unicode path name
// [usEndServer] -- End of server section of UNC path
//
// Requires:
//
// Returns:
// TRUE success
// FALSE failure
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-08-94 kevinro Modified
//
// Notes:
//
// Preprocesses the path, makes a copy of it, then calls the other
// version of Initialize.
//
//----------------------------------------------------------------------------
INTERNAL_(BOOL)
CFileMoniker::Initialize ( USHORT cAnti,
LPCWSTR szPathName,
USHORT usEndServer )
{
WCHAR const *pchSrc = szPathName;
WCHAR *pwcsPath = NULL;
USHORT ccPath;
//
// Adjust for leading '..'s
//
if (EatDotDots(&pchSrc, &cAnti) == FALSE)
{
return FALSE;
}
if (FAILED(DupWCHARString(pchSrc,pwcsPath,ccPath)))
{
return(FALSE);
}
//
// Be sure we are creating a valid Win32 path. ccPath is the count of
// characters. It needs to fit into a MAX_PATH buffer
//
if (ccPath >= MAX_PATH)
{
goto errRet;
}
if (Initialize(cAnti, NULL, 0, pwcsPath, ccPath, usEndServer) == FALSE)
{
goto errRet;
}
return(TRUE);
errRet:
if (pwcsPath != NULL)
{
PrivMemFree(pwcsPath);
}
return(FALSE);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::Create
//
// Synopsis: Create function for file moniker
//
// Effects:
//
// Arguments: [szPathName] -- Path to create with
// [cbPathName] -- Count of characters in path
// [memLoc] -- Memory context
// [usEndServer] -- Offset to end of server name in path
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-11-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
CFileMoniker FAR *
CFileMoniker::Create ( LPCWSTR szPathName,
USHORT cAnti ,
USHORT usEndServer)
{
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::Create szPath(%ws)\n",
szPathName?szPathName:L"<NULL PATH>"));
CFileMoniker FAR * pCFM = new CFileMoniker();
if (pCFM != NULL)
{
pCFM->AddRef();
pCFM->m_mxs.Request(); // protect all internal state
if (pCFM->Initialize( cAnti,
szPathName,
usEndServer))
{
pCFM->m_mxs.Release();
return pCFM;
}
pCFM->m_mxs.Release(); // we have to do this before we blow it away...
delete pCFM;
}
return NULL;
}
//+---------------------------------------------------------------------------
//
// Function: FindExt
//
// Synopsis:
//
// Effects:
// returns a pointer into szPath which points to the period (.) of the
// extension; returns NULL if no such point exists.
//
// Arguments: [szPath] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
LPCWSTR FindExt ( LPCWSTR szPath )
{
LPCWSTR sz = szPath;
if (!sz)
{
return NULL;
}
sz += lstrlenW(szPath); // sz now points to the null at the end
Assert(*sz == '\0');
DecLpch(szPath, sz);
while (*sz != '.' && *sz != '\\' && *sz != '/' && sz > szPath )
{
DecLpch(szPath, sz);
}
if (*sz != '.') return NULL;
return sz;
}
STDMETHODIMP CFileMoniker::QueryInterface (THIS_ REFIID riid,
LPVOID FAR* ppvObj)
{
wValidateMoniker();
VDATEIID (riid);
VDATEPTROUT(ppvObj, LPVOID);
#ifdef _DEBUG
if (riid == IID_IDebug)
{
*ppvObj = &(m_Debug);
return NOERROR;
}
#endif
#ifdef _TRACKLINK_
if (IsEqualIID(riid, IID_ITrackingMoniker))
{
AddRef();
*ppvObj = (ITrackingMoniker *) &_tfm;
return(S_OK);
}
#endif
if (IsEqualIID(riid, CLSID_FileMoniker))
{
// called by IsFileMoniker.
AddRef();
*ppvObj = this;
return S_OK;
}
else if (IsEqualIID(riid, IID_IROTData))
{
AddRef();
*ppvObj = (IROTData *) this;
return S_OK;
}
return CBaseMoniker::QueryInterface(riid, ppvObj);
}
STDMETHODIMP_(ULONG) CFileMoniker::Release (void)
{
wValidateMoniker();
Assert(m_refs != 0);
ULONG ul = m_refs;
if (InterlockedDecrement((long *)&m_refs) == 0)
{
delete this;
return 0;
}
return ul - 1;
}
STDMETHODIMP CFileMoniker::GetClassID (LPCLSID lpClassId)
{
VDATEPTROUT (lpClassId, CLSID);
CLock lck(m_mxs); // protect all internal state during operation
*lpClassId = CLSID_FileMoniker;
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::Load
//
// Synopsis: Loads a moniker from a stream
//
// Effects:
//
// Arguments: [pStm] -- Stream to load from
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-07-94 kevinro Modified
//
// Notes:
//
// We have some unfortunate legacy code to deal with here. Previous monikers
// saved their paths in ANSI instead of UNICODE. This was a very unfortunate
// decision, since we now are forced to pull some tricks to support UNICODE
// paths.
//
// Specifically, there isn't always a translation between UNICODE and ANSI
// characters. This means we may need to save a seperate copy of the UNCODE
// string, if the mapping to ASCII fails.
//
// The basic idea is the following:
//
// The in memory representation is always UNICODE. The serialized form
// will always attempt to be ANSI. If, while seralizing, the UNICODE path
// to ANSI path conversion fails, then we will create an extent to save the
// UNICODE version of the path. We will use whatever the ANSI path conversion
// ended up with to store in the ANSI part of the stream, though it will not
// be a good value. We will replace the non-converted characters with the
// systems 'default' mapping character, as defined by WideCharToMultiByte()
//
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::Load (LPSTREAM pStm)
{
CLock lck(m_mxs); // protect all internal state during load operation
wValidateMoniker();
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::Load(%x)\n",
this));
VDATEIFACE (pStm);
HRESULT hresult;
LPSTR szAnsiPath = NULL;
USHORT cAnti;
USHORT usEndServer;
WCHAR *pwcsWidePath = NULL;
USHORT ccWidePath = 0; // Number of characters in UNICODE path
ULONG cbExtents = 0;
USHORT cbAnsiPath = 0; // Number of bytes in path including NULL
#ifdef _CAIRO_
//
// If we're about to load from a stream, then our existing
// state is now invalid. There's no need to explicitely
// re-initialize our persistent state, except for the
// Shell Link object. An existing ShellLink should be
// deleted.
//
if( m_pShellLink )
m_pShellLink->Release();
m_pShellLink = NULL;
#endif // _CAIRO_
//
// The cAnti field was written out as a UINT in the original 16-bit code.
// This has been changed to a USHORT, to preserve its 16 bit size.
//
hresult = StRead(pStm, &cAnti, sizeof(USHORT));
if (hresult != NOERROR)
{
goto errRet;
}
//
// The path string is stored in ANSI format.
//
hresult = ReadAnsiStringStream( pStm, szAnsiPath , cbAnsiPath );
if (hresult != NOERROR)
{
goto errRet;
}
//
// The first version of the moniker code only had a MAC alias field.
// The second version used a cookie in m_cbMacAlias field to determine
// if the moniker is a newer version.
//
hresult = StRead(pStm, &cbExtents, sizeof(DWORD));
if (hresult != NOERROR)
{
goto errRet;
}
usEndServer = LOWORD(cbExtents);
if (usEndServer== 0xBEEF) usEndServer = DEF_ENDSERVER;
if (HIWORD(cbExtents) == 0xDEAD)
{
MonikerReadStruct ioStruct;
hresult = StRead(pStm, &ioStruct, sizeof(ioStruct));
if (hresult != NOERROR)
{
goto errRet;
}
m_clsid = ioStruct.m_clsid;
m_ole1 = (enum CFileMoniker::olever) ioStruct.m_ole1;
cbExtents = ioStruct.m_cbExtents;
}
//
// If cbExtents is != 0, then there are extents to be read. Call
// the member function of CExtentList to load them from stream.
//
// Having to pass cbExtents from this routine is ugly. But, we have
// to since it is read in as part of the cookie check above.
//
if (cbExtents != 0)
{
hresult = m_ExtentList.Load(pStm,cbExtents);
#ifdef _TRACKLINK_
if (hresult == S_OK)
{
#ifdef _CAIRO_
MONIKEREXTENT *pmeTracking;
// Is this a tracking moniker?
if( pmeTracking = m_ExtentList.FindExtent( mnk_TrackingInformation ))
{
m_fIsTracking = TRUE;
// Initialize the shell link object (in m_pShellLink)
// by creating it, and restoring its
// persistent data.
if( FAILED( hresult = RestoreShellLink() ))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Load could not restore shell link (%08x).\n",
this,
hresult));
}
else
{
// Have we ever successfully IShellLink->SetPath()?
m_fShellLinkInitialized = pmeTracking->achExtentBytes[0];
}
} // if( pmeTracking = m_ExtentList.FindExtent( mnk_TrackingInformation ))
else
{
// This is not a Tracking Moniker, but it may still have
// tracking enabled.
#endif // _CAIRO_
m_fTrackingEnabled =
NULL != m_ExtentList.FindExtent(mnk_ShellLink);
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Load did%s find mnk_ShellLink extent, m_fTrackingEnabled=%d.\n",
this,
m_fTrackingEnabled ? "" : " not",
m_fTrackingEnabled));
#ifdef _CAIRO_
} // if( ... FindExtent( mnk_TrackingInformation )) ... else
#endif
} // hresult = m_ExtentList.Load(pStm,cbExtents) ... if (hresult == S_OK)
#endif // _TRACKLINK_
} // if (cbExtents != 0)
//
// DetermineUnicodePath will handle the mbs to UNICODE conversions, and
// will also check the Extents to determine if there is a
// stored UNICODE path.
//
hresult = DetermineUnicodePath(szAnsiPath,pwcsWidePath,ccWidePath);
if (FAILED(hresult))
{
goto errRet;
}
//
// Initialize will take control of all path memory
//
if (Initialize( cAnti,
szAnsiPath,
cbAnsiPath,
pwcsWidePath,
ccWidePath,
usEndServer) == FALSE)
{
hresult = ResultFromScode(E_OUTOFMEMORY);
goto errRet;
}
errRet:
if (FAILED(hresult))
{
mnkDebugOut((DEB_ITRACE,
"::Load(%x) failed hr(%x)\n",
this,
hresult));
}
else
{
mnkDebugOut((DEB_ITRACE,
"::Load(%x) cAnti(%x) m_szPath(%ws) m_pszAnsiPath(%s)\n",
this,
cAnti,
m_szPath,
m_pszAnsiPath));
}
wValidateMoniker();
return hresult;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::Save
//
// Synopsis: Save this moniker to stream.
//
// Effects:
//
// Arguments: [pStm] -- Stream to save to
// [fClearDirty] -- Dirty flag
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-07-94 kevinro Modified
//
// Notes:
//
// It is unfortunate, but we may need to save two sets of paths in the
// moniker. The shipped version of monikers saved paths as ASCII strings.
//
// See the notes found in ::Load for more details
//
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::Save (LPSTREAM pStm, BOOL fClearDirty)
{
CLock lck(m_mxs); // protect all internal state during save operation
wValidateMoniker();
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::Save(%x)m_szPath(%ws)\n",
this,
m_szPath?m_szPath:L"<Null Path>"));
M_PROLOG(this);
VDATEIFACE (pStm);
HRESULT hresult;
UNREFERENCED(fClearDirty);
ULONG cbWritten;
//
// We currently have a UNICODE string. Need to write out an
// Ansi version.
//
hresult = ValidateAnsiPath();
if (hresult != NOERROR) goto errRet;
hresult = pStm->Write(&m_cAnti, sizeof(USHORT), &cbWritten);
if (hresult != NOERROR) goto errRet;
//
// Write the ANSI version of the path.
//
hresult = WriteAnsiStringStream( pStm, m_pszAnsiPath, m_cbAnsiPath );
if (hresult != NOERROR) goto errRet;
//
// Here we write out everything in a single blob
//
MonikerWriteStruct ioStruct;
ioStruct.m_endServer = m_endServer;
ioStruct.m_w = 0xDEAD;
ioStruct.m_clsid = m_clsid;
ioStruct.m_ole1 = m_ole1;
hresult = pStm->Write(&ioStruct, sizeof(ioStruct), &cbWritten);
if (hresult != NOERROR) goto errRet;
Assert(cbWritten == sizeof(ioStruct));
#ifdef _TRACKLINK_
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Save m_fSaveShellLink = %s, m_pShellLink=%08X.\n",
this,
m_fSaveShellLink ? "TRUE" : "FALSE",
m_pShellLink));
// If we have a ShellLink object, and either this is a tracking moniker
// or we've been asked to save the ShellLink, then save it in a
// Moniker Extent.
if ( ( m_fSaveShellLink
||
m_fIsTracking
)
&&
m_pShellLink != NULL
)
{
//
// Here we are saving the shell link to a MONIKEREXTENT.
// The basic idea here is to save the shell link to an in memory
// stream (using CreateStreamOnHGlobal). The format of the stream
// is the same as a MONIKEREXTENT (i.e. has a MONIKEREXTENT at the
// front of the stream.)
//
IPersistStream *pps = NULL;
IStream * pstm = NULL;
BOOL fOk;
HRESULT hr;
Verify(S_OK == m_pShellLink->QueryInterface(IID_IPersistStream, (void **) & pps));
hr = CreateStreamOnHGlobal(NULL, // auto alloc
TRUE, // delete on release
&pstm);
if (hr != S_OK)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Save CreateStreamOnHGlobal failed %08X",
this,
hr));
goto ExitShellLink;
}
//
// We write out the MONIKEREXTENT header to the stream ...
//
MONIKEREXTENT me;
MONIKEREXTENT *pExtent;
me.cbExtentBytes = 0;
me.usKeyValue = mnk_ShellLink;
hr = pstm->Write(&me, MONIKEREXTENT_HEADERSIZE, NULL);
if (hr != S_OK)
goto ExitShellLink;
// ... and then save the shell link
hr = pps->Save(pstm, FALSE);
if (hr != S_OK)
goto ExitShellLink;
// We then seek back and write the cbExtentList value.
LARGE_INTEGER li0;
ULARGE_INTEGER uli;
memset(&li0, 0, sizeof(li0));
Verify(S_OK == pstm->Seek(li0, STREAM_SEEK_END, &uli));
me.cbExtentBytes = uli.LowPart - MONIKEREXTENT_HEADERSIZE;
Verify(S_OK == pstm->Seek(li0, STREAM_SEEK_SET, &uli));
Assert(uli.LowPart == 0 && uli.HighPart == 0);
Verify(S_OK == pstm->Write(&me.cbExtentBytes, sizeof(me.cbExtentBytes), NULL));
// Finally, we get access to the memory of the stream and
// cast it to a MONIKEREXTENT to pass to PutExtent.
HGLOBAL hGlobal;
Verify(S_OK == GetHGlobalFromStream(pstm, &hGlobal));
pExtent = (MONIKEREXTENT *) GlobalLock(hGlobal);
Assert(pExtent != NULL);
// this overwrites the existing mnk_ShellLink extent if any.
hr = m_ExtentList.PutExtent(pExtent);
fOk = GlobalUnlock(hGlobal);
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Save serialized shell link to extent=%08X\n",
this,
hr));
// If this is a Tracking Moniker, then we additionally write
// out the fShellLinkInitialized flag. Not only does this make the
// flag persistent, but the existence of this Extent indicates
// that this is a Tracking Moniker (put another way, it makes the
// fIsTracking member persistent).
#ifdef _CAIRO_
if( m_fIsTracking )
{
// We needn't allocate any memory for the flag; it
// fits into the default MONIKEREXTENT (since it is
// a BYTE).
Assert( sizeof(m_fShellLinkInitialized) == sizeof(BYTE) );
MONIKEREXTENT meTracking;
meTracking.usKeyValue = mnk_TrackingInformation;
meTracking.cbExtentBytes = sizeof( meTracking )
- MONIKEREXTENT_HEADERSIZE;
meTracking.achExtentBytes[0] = m_fShellLinkInitialized;
// Put this extent.
hr = m_ExtentList.PutExtent( &meTracking );
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Save serialized tracking info to extent=%08X\n",
this,
hr));
} // if( m_fIsTracking )
#endif // _CAIRO_
ExitShellLink:
if (pstm != NULL)
pstm->Release(); // releases the hGlobal.
if (pps != NULL)
pps->Release();
} // if ( ( m_fSaveShellLink ...
#endif // _TRACKLINK_
//
// A UNICODE version may exist in the ExtentList. Write that out.
//
hresult = m_ExtentList.Save(pStm);
errRet:
#ifdef _TRACKLINK_
if (SUCCEEDED(hresult) && fClearDirty)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Save clearing dirty flag\n",
this));
m_fDirty = FALSE;
}
#endif
wValidateMoniker();
return hresult;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::IsDirty
//
// Synopsis: Return the dirty flag
//
// Notes:
//
//----------------------------------------------------------------------------
#ifdef _TRACKLINK_
STDMETHODIMP CFileMoniker::IsDirty (VOID)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::IsDirty returning%s dirty\n",
this,
m_fDirty ? "" : " not"));
return(m_fDirty ? S_OK : S_FALSE);
}
#endif
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GetSizeMax
//
// Synopsis: Return the current max size for a serialized moniker
//
// Effects:
//
// Arguments: [pcbSize] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-09-94 kevinro Modified
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::GetSizeMax (ULARGE_INTEGER FAR* pcbSize)
{
CLock lck(m_mxs); // protect all internal state during this call
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (pcbSize, ULONG);
wValidateMoniker();
//
// Total the string lengths. If the Ansi string doesn't exist yet, then
// assume the maximum length will be 2 bytes times the number of
// characters. 2 bytes is the maximum length of a DBCS character.
//
ULONG ulStringLengths = (m_cbAnsiPath?m_cbAnsiPath:m_ccPath*2);
//
// Now add in the size of the UNICODE string, if we haven't seen
// a UNICODE extent yet.
//
if (!m_fUnicodeExtent )
{
ulStringLengths += (m_ccPath * sizeof(WCHAR));
}
//
// The original code had added 10 bytes to the size, apparently just
// for kicks. I have left it here, since it doesn't actually hurt
//
ULONG cbSize;
cbSize = ulStringLengths +
sizeof(CLSID) + // The monikers class ID
sizeof(CLSID) + // OLE 1 classID
sizeof(ULONG) +
sizeof(USHORT) +
sizeof(DWORD) +
m_ExtentList.GetSize()
+ 10;
ULISet32(*pcbSize,cbSize);
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::GetSizeMax(%x)m_szPath(%ws) Size(0x%x)\n",
this,
m_szPath?m_szPath:L"<Null Path>",
cbSize));
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Function: GetClassFileEx
//
// Synopsis: returns the classid associated with a file
//
// Arguments: [lpszFileName] -- name of the file
// [pcid] -- where to return the clsid
// [clsidOle1] -- ole1 clsid to use (or CLSID_NULL)
//
// Returns: S_OK if clisd determined
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes: On Cairo, STGFMT_FILE will try to read the clsid from a file,
// then do pattern matching, and then do extension matching.
// For all other storage formats, pattern matching is skipped.
// If at any point along the way we find a clsid, exit
//
//----------------------------------------------------------------------------
STDAPI GetClassFileEx( LPCWSTR lpszFileName, CLSID FAR *pcid,
REFCLSID clsidOle1)
{
VDATEPTRIN (lpszFileName, WCHAR);
VDATEPTROUT (pcid, CLSID);
LPCWSTR szExt = NULL;
HRESULT hresult;
HANDLE hFile = INVALID_HANDLE_VALUE;
//
// Don't crash when provided a bogus file path.
//
if (lpszFileName == NULL)
{
hresult = MK_E_CANTOPENFILE;
goto errRet;
}
#ifdef _CAIRO_
hresult = StgGetClassFile (NULL, lpszFileName, pcid, &hFile);
if (hresult == NOERROR && !IsEqualIID(*pcid, CLSID_NULL))
{
// we have a class id from the file
goto errRet;
}
// In certain cases, StgGetClassFile (NtCreateFile) will fail
// but CreateFile will successfully open a docfile or OFS storage.
// In the docfile case, DfGetClass returns lock violation
// but Daytona ignores it, and checks the pattern & extensions
// In the OFS case, GetNtHandle returns share violation
// but CreateFile will work, skip the pattern & check extensions
// This is intended to emulate this odd behavior
if (hresult != STG_E_LOCKVIOLATION &&
hresult != STG_E_SHAREVIOLATION &&
!SUCCEEDED(hresult)) // emulate CreateFile error
{
hresult = MK_E_CANTOPENFILE;
goto errRet;
}
#endif // _CAIRO_
#ifndef _CAIRO_
// open the file once, then pass the file handle to the various
// subsystems (storage, pattern matching) to do the work.
hFile = CreateFile(lpszFileName, // file name
GENERIC_READ, // read only access
FILE_SHARE_READ | FILE_SHARE_WRITE, // allow any other access
NULL, // no sec descriptor
OPEN_EXISTING, // file if file doesn't exist
NULL, // no flags or attributes
NULL); // no template
if (INVALID_HANDLE_VALUE == hFile)
{
hresult = MK_E_CANTOPENFILE;
goto errRet;
}
// First, check with storage to see if this a docfile. if it is,
// storage will return us the clsid.
hresult = DfGetClass(hFile, pcid);
if (hresult == NOERROR && !IsEqualIID(*pcid, CLSID_NULL))
{
goto errRet;
}
#endif // _CAIRO_
// If this is an OLE1 file moniker, then use the CLSID given
// to the moniker at creation time instead of using the
// file extension. Bug 3948.
if (!IsEqualCLSID(clsidOle1,CLSID_NULL))
{
*pcid = clsidOle1;
hresult = NOERROR;
goto errRet;
}
#ifdef _CAIRO_
if (hFile != INVALID_HANDLE_VALUE)
{
#endif
// Attempt to find the class by matching byte patterns in
// the file with patterns stored in the registry.
hresult = wCoGetClassPattern(hFile, pcid);
if (hresult != REGDB_E_CLASSNOTREG)
{
// either a match was found, or the file does not exist.
goto errRet;
}
#ifdef _CAIRO_
} // end if (hFile != INVALID_HANDLE_VALUE)
#endif
// The file is not a storage, there was no pattern matching, and
// the file exists. Look up the class for this extension.
// Find the extension by scanning backward from the end for ".\/!"
// There is an extension only if we find "."
hresult = NOERROR;
szExt = FindExt(lpszFileName);
if (!szExt)
{
// no file extension
hresult = ResultFromScode(MK_E_INVALIDEXTENSION);
goto errRet;
}
if (wCoGetClassExt(szExt, pcid) != 0)
{
hresult = ResultFromScode(MK_E_INVALIDEXTENSION);
}
errRet:
if (INVALID_HANDLE_VALUE != hFile)
{
CloseHandle(hFile);
}
if (hresult != NOERROR)
{
*pcid = CLSID_NULL;
}
return hresult;
}
//+---------------------------------------------------------------------------
//
// Function: GetClassFile
//
// Synopsis: returns the classid associated with a file
//
// Arguments: [lpszFileName] -- name of the file
// [pcid] -- where to return the clsid
//
// Returns: S_OK if clisd determined
//
// Algorithm: just calls GetClassFileEx
//
// History: 1-16-94 kevinro Created
//
//----------------------------------------------------------------------------
STDAPI GetClassFile( LPCWSTR lpszFileName, CLSID FAR *pcid )
{
OLETRACEIN((API_GetClassFile, PARAMFMT("lpszFileName= %ws, pcid= %p"),
lpszFileName, pcid));
HRESULT hr;
hr = GetClassFileEx (lpszFileName, pcid, CLSID_NULL);
OLETRACEOUT((API_GetClassFile, hr));
return hr;
}
#ifdef _TRACKLINK_
STDMETHODIMP CFileMoniker::Reduce (LPBC pbc,
DWORD dwReduceHowFar,
LPMONIKER FAR* ppmkToLeft,
LPMONIKER FAR * ppmkReduced)
{
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::Reduce(%x)\n",this));
M_PROLOG(this);
CLock lck(m_mxs); // protect all internal state
VDATEPTROUT(ppmkReduced,LPMONIKER);
VDATEIFACE(pbc);
if (ppmkToLeft)
{
VDATEPTROUT(ppmkToLeft,LPMONIKER);
if (*ppmkToLeft) VDATEIFACE(*ppmkToLeft);
}
HRESULT hr=E_FAIL;
IMoniker *pmkNew=NULL;
BOOL fReduceToSelf = TRUE;
*ppmkReduced = NULL;
//
// search for the file
//
if ( m_fIsTracking
||
( m_fTrackingEnabled && m_fReduceEnabled )
)
{
// Resolve the ShellLink object.
if( SUCCEEDED( ResolveShellLink( pbc )))
{
Assert(m_szPath != NULL);
// Use the path that we now know is up-to-date, to create
// a default (i.e., non-tracking) File Moniker.
hr = CreateFileMoniker(m_szPath, ppmkReduced); // expensive
if (hr == S_OK)
fReduceToSelf = FALSE;
} // if( SUCCEEDED( ResolveShellLink( pbc )))
} // if ( m_fIsTracking || ...
if (fReduceToSelf)
{
*ppmkReduced = this;
AddRef();
hr = MK_S_REDUCED_TO_SELF;
}
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::Reduce exit with hr=%08X.\n",
this,
hr));
return(hr);
}
#endif
//+---------------------------------------------------------------------------
//
// Function: FileBindToObject
//
// Synopsis: Given a filename, and some other information, bind to the
// file and load it.
//
// Effects: This routine is used to load an object when the caller to
// CFileMoniker::BindToObject had provided its own bind context.
//If pmkToLeft != 0, then we have a moniker on the left of the
//file moniker. The file moniker will use pmkToLeft to bind to
//either the IClassFactory or the IClassActivator interface.
//
//If protSystem != prot, then the bind context has supplied a
//non-standard ROT. CoGetInstanceFromFile and CoGetPersistentInstance
//only search the standard ROT. Therefore we cannot call
//CoGetInstanceFromFile or CoGetPersistentInstance in this case.
//
// Arguments: [pmkThis] -- Moniker being bound
// [pwzPath] -- Path to bind
// [clsid] -- CLSID for path
// [pbc] -- The bind context - Not ours! Make no assumptions
// [pmkToLeft] -- Moniker to left
// [riidResult] -- IID being requested
// [ppvResult] -- punk for result
//
//----------------------------------------------------------------------------
INTERNAL FileBindToObject
(LPMONIKER pmkThis,
LPWSTR pwzPath,
REFCLSID clsid,
LPBC pbc,
BIND_OPTS2 *pBindOptions,
LPMONIKER pmkToLeft,
REFIID riidResult,
LPVOID FAR* ppvResult)
{
HRESULT hr;
LPPERSISTFILE pPF = NULL;
*ppvResult = NULL;
//Get an IPersistFile interface pointer.
if(0 == pmkToLeft)
{
if ( pBindOptions->pServerInfo )
{
MULTI_QI MultiQi;
MultiQi.pIID = &IID_IPersistFile;
MultiQi.pItf = 0;
hr = CoCreateInstanceEx(clsid, NULL,
pBindOptions->dwClassContext,
pBindOptions->pServerInfo,
1, &MultiQi );
pPF = (IPersistFile *) MultiQi.pItf;
}
else
{
hr = CoCreateInstance(clsid, NULL,
pBindOptions->dwClassContext,
IID_IPersistFile,
(void **) &pPF);
}
}
else
{
IClassActivator *pActivator;
IClassFactory *pFactory = 0;
//Bind to IClassActivator interface.
hr = pmkToLeft->BindToObject(pbc,
0,
IID_IClassActivator,
(void **) &pActivator);
if(SUCCEEDED(hr))
{
hr = pActivator->GetClassObject(clsid,
pBindOptions->dwClassContext,
pBindOptions->locale,
IID_IClassFactory,
(void **) &pFactory);
pActivator->Release();
}
else
{
//Bind to the IClassFactory interface.
hr = pmkToLeft->BindToObject(pbc,
0,
IID_IClassFactory,
(void **) &pFactory);
}
if(SUCCEEDED(hr) && pFactory != 0)
{
//Create an instance and get the IPersistFile interface.
hr = pFactory->CreateInstance(0,
IID_IPersistFile,
(void **) &pPF);
pFactory->Release();
}
}
//Load the instance from the file.
if(SUCCEEDED(hr))
{
hr = pPF->Load(pwzPath, pBindOptions->grfMode);
if (SUCCEEDED(hr))
{
hr = pPF->QueryInterface(riidResult, ppvResult);
}
pPF->Release();
}
else if(E_NOINTERFACE == hr)
{
hr = MK_E_INTERMEDIATEINTERFACENOTSUPPORTED;
}
return hr;
}
/*
BindToObject takes into account AutoConvert and TreatAs keys when
determining which class to use to bind. It does not blindly use the
CLSID returned by GetClassFileEx. This is to allow a new OLE2
server to service links (files) created with its previous OLE1 or OLE2
version.
This can produce some strange behavior in the follwoing (rare) case.
Suppose you have both an OLE1 version (App1) and an OLE2 version
(App2) of a server app on your machine, and the AutoConvert key is
present. Paste link from App1 to an OLE2 container. The link will
not be connected because BindToObject will try to bind
using 2.0 behavior (because the class has been upgraded) rather than 1.0
behavior (DDE). Ideally, we would call DdeIsRunning before doing 2.0
binding. If you shut down App1, then you will be able to bind to
App2 correctly.
*/
STDMETHODIMP CFileMoniker::BindToObject ( LPBC pbc,
LPMONIKER pmkToLeft, REFIID riidResult, LPVOID FAR* ppvResult)
{
mnkDebugOut((DEB_ITRACE,"CFileMoniker(%x)::BindToObject\n",this));
m_mxs.Request();
BOOL bGotLock = TRUE;
wValidateMoniker();
A5_PROLOG(this);
VDATEPTROUT (ppvResult, LPVOID);
*ppvResult = NULL;
VDATEIFACE (pbc);
if (pmkToLeft)
{
VDATEIFACE (pmkToLeft);
}
VDATEIID (riidResult);
HRESULT hr;
CLSID clsid;
LPRUNNINGOBJECTTABLE prot = NULL;
LPRUNNINGOBJECTTABLE protSystem = NULL;
LPUNKNOWN pUnk = NULL;
BIND_OPTS2 bindopts;
BOOL fOle1Loaded;
#ifdef _CAIRO_
// Resolve the link source for tracking monikers. This will cause its
// file attributes to be updated, including its name & location if the
// file has been moved.
if( m_fIsTracking )
{
hr = ResolveShellLink(pbc))
if(FAILED(hr))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::BindToObject Resolve failed %08X.\n",
this,
hr));
goto exitRtn;
}
}
#endif // _CAIRO_
//Get the bind options from the bind context.
bindopts.cbStruct = sizeof(bindopts);
hr = pbc->GetBindOptions(&bindopts);
if(FAILED(hr))
{
//Try the smaller BIND_OPTS size.
bindopts.cbStruct = sizeof(BIND_OPTS);
hr = pbc->GetBindOptions(&bindopts);
if(FAILED(hr))
{
goto exitRtn;
}
}
if(bindopts.cbStruct < sizeof(BIND_OPTS2))
{
//Initialize the new BIND_OPTS2 fields
bindopts.dwTrackFlags = 0;
bindopts.locale = GetThreadLocale();
bindopts.pServerInfo = 0;
#ifdef WX86OLE
bindopts.dwClassContext = gcwx86.IsWx86Enabled() ?
CLSCTX_SERVER | CLSCTX_INPROC_SERVERX86 :
CLSCTX_SERVER;
#else
bindopts.dwClassContext = CLSCTX_SERVER;
#endif //WX86OLE
}
hr = GetRunningObjectTable(0,&protSystem);
if(SUCCEEDED(hr))
{
// Get the Bind Contexts version of the ROT
hr = pbc->GetRunningObjectTable( &prot );
if(SUCCEEDED(hr))
{
// first snapshot some member data and unlock
CLSID TempClsid = m_clsid;
TCHAR TempPath[MAX_PATH+1];
olever NewOleVer = m_ole1;
BOOL fUpdated = FALSE;
lstrcpyW( TempPath, m_szPath );
bGotLock = FALSE;
m_mxs.Release();
if((prot == protSystem) && (0 == pmkToLeft))
{
//This is the normal case.
//Bind to the object.
#ifdef DCOM
MULTI_QI QI_Block;
QI_Block.pItf = NULL;
QI_Block.pIID = &riidResult;
CLSID * pClsid = &TempClsid;
if ( IsEqualGUID( GUID_NULL, m_clsid ) )
pClsid = NULL;
hr = CoGetInstanceFromFile(bindopts.pServerInfo,
pClsid,
NULL,
bindopts.dwClassContext,
bindopts.grfMode,
TempPath,
1,
&QI_Block);
*ppvResult = (LPVOID) QI_Block.pItf;
#else // !DCOM
hr = CoGetPersistentInstance(riidResult,
bindopts.dwClassContext,
bindopts.grfMode,
TempPath,
NULL,
TempClsid,
&fOle1Loaded,
ppvResult);
#endif // !DCOM
}
else // prot != protSystem or pmkToLeft exists
{
mnkDebugOut((DEB_ITRACE,"::BindToObject using non-standard ROT\n"));
//Search the ROT for the object.
hr = prot->GetObject(this, &pUnk);
if (SUCCEEDED(hr))
{
// Found in the ROT. Try and get the interface and return
mnkDebugOut((DEB_ITRACE,"::BindToObject Found object in ROT\n"));
hr = pUnk->QueryInterface(riidResult, ppvResult);
pUnk->Release();
}
else
{
//Object was not found in the ROT. Get the class ID,
//then load the object from the file.
mnkDebugOut((DEB_ITRACE,"::BindToObject doing old style bind\n"));
hr = GetClassFileEx (TempPath, &clsid,TempClsid);
if (hr == NOERROR)
{
UpdateClsid (&clsid); // See note above
if (CoIsOle1Class (clsid))
{
mnkDebugOut((DEB_ITRACE,
"::BindToObject found OLE1.0 class\n"));
COleTls Tls;
if( Tls->dwFlags & OLETLS_DISABLE_OLE1DDE )
{
// If this app doesn't want or can tolerate having a DDE
// window then currently it can't use OLE1 classes because
// they are implemented using DDE windows.
//
hr = CO_E_OLE1DDE_DISABLED;
}
else // DDE not disabled
{
hr = DdeBindToObject (TempPath,
clsid,
FALSE,
riidResult,
ppvResult);
{
NewOleVer = ole1;
TempClsid = clsid;
m_fClassVerified = TRUE;
fUpdated = TRUE;
}
}
}
else // Not OLE 1 class
{
mnkDebugOut((DEB_ITRACE,
"::BindToObject found OLE2.0 class\n"));
hr = FileBindToObject (this,
TempPath,
clsid,
pbc,
&bindopts,
pmkToLeft,
riidResult,
ppvResult);
{
NewOleVer = ole2;
TempClsid = clsid;
m_fClassVerified = TRUE;
fUpdated = TRUE;
}
}
}
else
{
mnkDebugOut((DEB_ITRACE,
"::BindToObject failed GetClassFileEx %x\n",
hr));
}
}
}
prot->Release();
if (fUpdated)
{
// note that the lock is never held at this point...
CLock lck(m_mxs);
m_ole1 = NewOleVer;
m_clsid = TempClsid;
}
}
else
{
mnkDebugOut((DEB_ITRACE,
"::BindToObject failed pbc->GetRunningObjectTable() %x\n",
hr));
}
protSystem->Release();
}
else
{
mnkDebugOut((DEB_ITRACE,
"::BindToObject failed GetRunningObjectTable() %x\n",
hr));
}
exitRtn:
mnkDebugOut((DEB_ITRACE,
"CFileMoniker(%x)::BindToObject returns %x\n",
this,
hr));
// make sure we exit with the lock clear in case of errors.
if ( bGotLock )
m_mxs.Release();
return hr;
}
BOOL Peel( LPWSTR lpszPath, USHORT endServer, LPWSTR FAR * lplpszNewPath, ULONG n )
// peels off the last n components of the path, leaving a delimiter at the
// end. Returns the address of the new path via *lplpszNewPath. Returns
// false if an error occurred -- e.g., n too large, trying to peel off a
// volume name, etc.
{
WCHAR FAR* lpch;
ULONG i = 0;
ULONG j;
WCHAR FAR* lpszRemainingPath; // ptr to beginning of path name minus the share name
if (*lpszPath == '\0') return FALSE;
//
// Find the end of the string and determine the string length.
//
for (lpch=lpszPath; *lpch; lpch++);
DecLpch (lpszPath, lpch); // lpch now points to the last real character
// if n == 0, we dup the string, possibly adding a delimiter at the end.
if (n == 0)
{
i = lstrlenW(lpszPath);
if (!IsSeparator(*lpch))
{
j = 1;
}
else
{
j = 0;
}
*lplpszNewPath = (WCHAR *) PrivMemAlloc((i + j + 1) * sizeof(WCHAR));
if (*lplpszNewPath == NULL)
{
return FALSE;
}
memcpy(*lplpszNewPath, lpszPath, i * sizeof(WCHAR));
if (j == 1)
{
*(*lplpszNewPath + i) = '\\';
}
*(*lplpszNewPath + i + j) = '\0';
return TRUE;
}
if (DEF_ENDSERVER == endServer)
endServer = 0;
lpszRemainingPath = lpszPath + endServer; // if endServer > 0 the remaining path will be in the form of \dir\file
#ifdef _DEBUG
if (endServer)
{
Assert(lpszRemainingPath[0] == '\\');
}
#endif // _DEBUG
if (lpch < lpszRemainingPath)
{
AssertSz(0,"endServer Value is larger than Path");
return FALSE;
}
for (i = 0; i < n; i++)
{
if (IsSeparator(*lpch))
{
DecLpch(lpszPath, lpch);
}
if ((lpch < lpszRemainingPath) || (*lpch == ':') || (IsSeparator(*lpch)))
{
return FALSE;
}
// n is too large, or we hit two delimiters in a row, or a volume name.
while( !IsSeparator(*lpch) && (lpch > lpszRemainingPath) )
{
DecLpch(lpszPath, lpch);
}
}
// lpch points to the last delimiter we will leave or lpch == lpszPath
// REVIEW: make sure we haven't eaten into the volume name
if (lpch == lpszPath)
{
*lplpszNewPath = (WCHAR *) PrivMemAlloc(1 * sizeof(WCHAR));
if (*lplpszNewPath == NULL)
{
return FALSE;
}
**lplpszNewPath = '\0';
}
else
{
*lplpszNewPath = (WCHAR *) PrivMemAlloc(
(lpch - lpszPath + 2) * sizeof(WCHAR));
if (*lplpszNewPath == NULL) return FALSE;
memcpy(*lplpszNewPath,lpszPath,(lpch - lpszPath + 1) * sizeof(WCHAR));
*(*lplpszNewPath + (lpch - lpszPath) + 1) = '\0';
}
return TRUE;
}
STDMETHODIMP CFileMoniker::BindToStorage (LPBC pbc, LPMONIKER
pmkToLeft, REFIID riid, LPVOID FAR* ppvObj)
{
wValidateMoniker();
M_PROLOG(this);
CLock lck(m_mxs); // protect all internal state
VDATEPTROUT (ppvObj, LPVOID);
*ppvObj = NULL;
VDATEIFACE (pbc);
if (pmkToLeft)
{
VDATEIFACE (pmkToLeft);
}
VDATEIID (riid);
*ppvObj = NULL;
HRESULT hresult = NOERROR;
BIND_OPTS bindopts;
bindopts.cbStruct = sizeof(BIND_OPTS);
hresult = pbc->GetBindOptions(&bindopts);
if FAILED(hresult)
goto errRet;
// Resolve the link source for tracking monikers. This will cause
// its file attributes to be updated, including its name & location
// if the file has been moved.
#ifdef _CAIRO_
if( m_fIsTracking )
{
hresult = ResolveShellLink( pbc );
if( FAILED(hresult))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::BindToStorage Resolve failed %08X.\n",
this,
hresult));
goto errRet;
}
}
#endif // _CAIRO_
// Bind to the storage.
if (IsEqualIID(riid, IID_IStorage))
{
hresult = StgOpenStorage( m_szPath, NULL, bindopts.grfMode, NULL, 0, (LPSTORAGE FAR*)ppvObj );
}
else if (IsEqualIID(riid, IID_IStream))
{
hresult = ResultFromScode(E_UNSPEC); // unimplemented until CreateStreamOnFile is implemented
}
else if (IsEqualIID(riid, IID_ILockBytes))
{
hresult = ResultFromScode(E_UNSPEC); // unimplemented until CreateILockBytesOnFile is implemented
}
else
{
// CFileMoniker:BindToStorage called for unsupported interface
hresult = ResultFromScode(E_NOINTERFACE);
}
// REVIEW: CFileMoniker:BindToStorage being called for unsupported interface
errRet:
return hresult;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::ComposeWith
//
// Synopsis: Compose another moniker to the end of this
//
// Effects: Given another moniker, create a composite between this
// moniker and the other. If the other is also a CFileMoniker,
// then collapse the two monikers into a single one by doing a
// concatenate on the two paths.
//
// Arguments: [pmkRight] --
// [fOnlyIfNotGeneric] --
// [ppmkComposite] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 3-03-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::ComposeWith ( LPMONIKER pmkRight,
BOOL fOnlyIfNotGeneric, LPMONIKER FAR* ppmkComposite)
{
CLock lck(m_mxs); // protect all internal state
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (ppmkComposite, LPMONIKER);
*ppmkComposite = NULL;
VDATEIFACE (pmkRight);
HRESULT hresult = NOERROR;
CFileMoniker FAR* pcfmRight;
LPWSTR lpszLeft = NULL;
LPWSTR lpszRight;
LPWSTR lpszComposite;
CFileMoniker FAR* pcfmComposite;
int n1;
int n2;
*ppmkComposite = NULL;
//
// If we are being composed with an Anti-Moniker, then return
// the resulting composition. The EatOne routine will take care
// of returning the correct composite of Anti monikers (or NULL)
//
CAntiMoniker *pCAM = IsAntiMoniker(pmkRight);
if(pCAM)
{
pCAM->EatOne(ppmkComposite);
return(NOERROR);
}
//
// If the moniker is a CFileMoniker, then collapse the two monikers
// into one by doing a concate of the two strings.
//
if ((pcfmRight = IsFileMoniker(pmkRight)) != NULL)
{
lpszRight = pcfmRight->m_szPath;
// lpszRight may be NULL
if (NULL == lpszRight)
lpszRight = L"";
if (( *lpszRight == 0) &&
pcfmRight->m_cAnti == 0)
{
// Second moniker is "". Simply return the first.
*ppmkComposite = this;
AddRef();
return NOERROR;
}
//
// If the path on the right is absolute, then there is a
// syntax error. The path is invalid, since you can't
// concat d:\foo and d:\bar to get d:\foo\d:\bar and
// expect it to work.
//
if (IsAbsolutePath(lpszRight))
{
return(MK_E_SYNTAX);
}
//
// If the right moniker has m_cAnti != 0, then peel back
// the path
//
if (Peel(m_szPath,m_endServer, &lpszLeft, pcfmRight->m_cAnti))
{
// REVIEW: check that there is no volume name at the start
// skip over separator
while (IsSeparator(*lpszRight)) lpszRight++;
n1 = lstrlenW(lpszLeft);
n2 = lstrlenW(lpszRight);
lpszComposite = (WCHAR *) PrivMemAlloc((n1 + n2 + 1)*sizeof(WCHAR));
if (!lpszComposite)
{
hresult = E_OUTOFMEMORY;
}
else
{
memcpy(lpszComposite, lpszLeft, n1 * sizeof(WCHAR));
memcpy(lpszComposite + n1, lpszRight, n2 * sizeof(WCHAR));
lpszComposite[n1 + n2] = '\0';
pcfmComposite = CFileMoniker::Create(lpszComposite,
m_cAnti,m_endServer);
if (pcfmComposite == NULL)
{
hresult = E_OUTOFMEMORY;
}
else
{
// Is tracking moniker?
#ifdef _CAIRO_
if( m_fIsTracking )
{
// Since this is a tracking moniker, we should get our TrackFlags
// and use them to make the composite a tracking moniker.
DWORD dwTrackFlags;
hresult = GetTrackFlags( &dwTrackFlags );
if( SUCCEEDED( hresult ))
{
// Make the composite a tracking moniker. Note that the
// Track Flags get piggy-backed onto the
// EnableTracking routine's OT flags.
hresult = pcfmComposite->EnableTracking(
NULL,
( TRACK_2_OT_FLAGS( dwTrackFlags )
|
OT_MAKETRACKING
)
);
if( SUCCEEDED( hresult ))
{
*ppmkComposite = pcfmComposite;
pcfmComposite = NULL;
}
else
{
pcfmComposite->Release();
pcfmComposite = NULL;
}
}
} // if( m_fIsTracking )
else
#endif // _CAIRO_
{
*ppmkComposite = pcfmComposite;
pcfmComposite = NULL;
}
} // if (pcfmComposite == NULL) ... else
PrivMemFree(lpszComposite);
} // if (!lpszComposite) ... else
if ( lpszLeft != NULL)
{
PrivMemFree(lpszLeft);
}
} // if (Peel(m_szPath, &lpszLeft, pcfmRight->m_cAnti))
else
{
// Peel failed, which means the caller attempted an
// invalid composition of file paths. There is apparently
// a syntax error in the names.
//
hresult = MK_E_SYNTAX;
} // if (Peel(m_szPath, &lpszLeft, pcfmRight->m_cAnti)) ... else
} // if ((pcfmRight = IsFileMoniker(pmkRight)) != NULL)
else
{
if (!fOnlyIfNotGeneric)
{
hresult = CreateGenericComposite( this, pmkRight, ppmkComposite );
}
else
{
hresult = MK_E_NEEDGENERIC;
*ppmkComposite = NULL;
}
} // if ((pcfmRight = IsFileMoniker(pmkRight)) != NULL) ... else
return hresult;
}
STDMETHODIMP CFileMoniker::Enum (THIS_ BOOL fForward, LPENUMMONIKER FAR* ppenumMoniker)
{
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (ppenumMoniker, LPENUMMONIKER);
*ppenumMoniker = NULL;
// REVIEW: this says files monikers are not enumerable.
return NOERROR;
}
STDMETHODIMP CFileMoniker::IsEqual (THIS_ LPMONIKER pmkOtherMoniker)
{
mnkDebugOut((DEB_ITRACE,
"CFileMoniker::IsEqual(%x) m_szPath(%ws)\n",
this,
WIDECHECK(m_szPath)));
CLock lck(m_mxs); // protect all internal state
wValidateMoniker();
M_PROLOG(this);
VDATEIFACE (pmkOtherMoniker);
CFileMoniker FAR* pCFM = IsFileMoniker(pmkOtherMoniker);
if (!pCFM)
{
return ResultFromScode(S_FALSE);
}
if (pCFM->m_cAnti != m_cAnti)
{
return ResultFromScode(S_FALSE);
}
mnkDebugOut((DEB_ITRACE,
"::IsEqual(%x) m_szPath(%ws) pOther(%ws)\n",
this,
WIDECHECK(m_szPath),
WIDECHECK(pCFM->m_szPath)));
// for the paths, we just do a case-insensitive compare.
if (lstrcmpiW(pCFM->m_szPath, m_szPath) == 0)
{
return NOERROR;
}
return ResultFromScode(S_FALSE);
}
//+---------------------------------------------------------------------------
//
// Function: CalcFileMonikerHash
//
// Synopsis: Given a LPWSTR, calculate the hash value for the string.
//
// Effects:
//
// Arguments: [lp] -- String to compute has value for
//
// Requires:
// Uses a MAX_PATH+1 sized buffer. Never pass a longer string,
// or we will crash violently. The +1 accounts for the NULL
// Returns:
// DWORD hash value for string.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-15-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
DWORD CalcFileMonikerHash(LPWSTR lp)
{
DWORD dwTemp = 0;
WCHAR ch;
//
// toupper turns out to be expensive, since it takes a
// critical section each and every time. It turns out to be
// much cheaper to make a local copy of the string, then upper the
// whole thing.
//
WCHAR pszTempPath[MAX_PATH+1];
if (lp == NULL)
{
return(0);
}
lstrcpyW(pszTempPath,lp);
CharUpperW(pszTempPath);
lp = pszTempPath;
while (*lp)
{
dwTemp *= 3;
ch = *lp;
dwTemp ^= ch;
lp++;
}
return dwTemp;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::Hash
//
// Synopsis:
//
// Effects:
//
// Arguments: [pdwHash] -- Output pointer for hash value
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-09-94 kevinro Modified
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::Hash (THIS_ LPDWORD pdwHash)
{
CLock lck(m_mxs); // protect m_fHashValueValid and m_dwHashValue
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (pdwHash, DWORD);
//
// Calculating the hash value is expensive. Cache it.
//
if (!m_fHashValueValid)
{
m_dwHashValue = m_cAnti + CalcFileMonikerHash(m_szPath);
m_fHashValueValid = TRUE;
}
*pdwHash = m_dwHashValue;
wValidateMoniker();
return(NOERROR);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::IsRunning
//
// Synopsis: Determine if the object pointed to by the moniker is listed
// as currently running.
//
// Effects:
//
// Arguments: [pbc] --
// [pmkToLeft] --
// [pmkNewlyRunning] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 3-03-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::IsRunning (THIS_ LPBC pbc,
LPMONIKER pmkToLeft,
LPMONIKER pmkNewlyRunning)
{
M_PROLOG(this);
CLock lck(m_mxs); // protect all internal state
VDATEIFACE (pbc);
LPRUNNINGOBJECTTABLE pROT;
HRESULT hresult;
//
// According to the spec, CFileMoniker ignores the
// moniker to the left.
//
if (pmkToLeft)
{
VDATEIFACE (pmkToLeft);
}
if (pmkNewlyRunning)
{
VDATEIFACE (pmkNewlyRunning);
}
CLSID clsid;
if (IsOle1Class(&clsid))
{
return DdeIsRunning (clsid, m_szPath, pbc, pmkToLeft, pmkNewlyRunning);
}
if (pmkNewlyRunning != NULL)
{
return pmkNewlyRunning->IsEqual (this);
}
hresult = pbc->GetRunningObjectTable (&pROT);
if (hresult == NOERROR)
{
hresult = pROT->IsRunning (this);
pROT->Release ();
}
return hresult;
}
STDMETHODIMP CFileMoniker::GetTimeOfLastChange (THIS_ LPBC pbc, LPMONIKER pmkToLeft,
FILETIME FAR* pfiletime)
{
M_PROLOG(this);
CLock lck(m_mxs); // protect all internal state
VDATEIFACE (pbc);
if (pmkToLeft) VDATEIFACE (pmkToLeft);
VDATEPTROUT (pfiletime, FILETIME);
HRESULT hresult;
LPMONIKER pmkTemp = NULL;
LPRUNNINGOBJECTTABLE prot = NULL;
LPWSTR lpszName = NULL;
WCHAR localBuffer[MAX_PATH+1];
if (pmkToLeft == NULL)
{
pmkTemp = this;
AddRef();
}
else
{
hresult = CreateGenericComposite(pmkToLeft, this, &pmkTemp );
if (hresult != NOERROR)
{
goto errRet;
}
}
hresult = pbc->GetRunningObjectTable(&prot);
if (hresult != NOERROR)
{
goto errRet;
}
// Attempt to get the time-of-last-change from the ROT. Note that
// if there is a File Moniker in 'pmkTemp', the ROT will Reduce it.
// Thus, if it is a *Tracking* File Moniker, it will be updated to reflect
// any changes to the file (such as location, timestamp, etc.)
hresult = prot->GetTimeOfLastChange(pmkTemp, pfiletime);
if (hresult != MK_E_UNAVAILABLE)
{
goto errRet;
}
//
// BUGBUG: Why aren't we just
// looking in the file moniker to the left. Is it possible to have
// another MKSYS_FILEMONIKER implementation?
//
if (IsFileMoniker(pmkTemp))
{
hresult = pmkTemp->GetDisplayName(pbc, NULL, &lpszName);
if (hresult != NOERROR)
{
goto errRet;
}
if (lstrlenW(lpszName) < _MAX_PATH)
{
lstrcpyW(localBuffer, lpszName );
}
else
{
hresult = E_UNSPEC;
goto errRet;
}
// Attempt to get the file's attributes. If the file exists,
// give the modify time to the caller.
#ifdef _CHICAGO_
HANDLE hdl;
WIN32_FIND_DATA fid;
if ((hdl = FindFirstFile(lpszName, &fid)) != INVALID_HANDLE_VALUE)
{
memcpy(pfiletime, &fid.ftLastWriteTime, sizeof(FILETIME));
FindClose(hdl);
hresult = S_OK;
}
#else // _CHICAGO_
WIN32_FILE_ATTRIBUTE_DATA fad;
if( GetFileAttributesEx( lpszName, GetFileExInfoStandard, &fad ))
{
memcpy(pfiletime, &fad.ftLastWriteTime, sizeof(FILETIME));
hresult = S_OK;
}
#endif // _CHICAGO_
else
{
hresult = ResultFromScode(MK_E_NOOBJECT);
}
}
else
{
hresult = ResultFromScode(E_UNSPEC);
}
errRet:
if (prot != NULL)
{
prot->Release();
}
if (pmkTemp != NULL)
{
pmkTemp->Release();
}
if (lpszName != NULL)
{
CoTaskMemFree(lpszName);
}
return hresult;
}
STDMETHODIMP CFileMoniker::Inverse (THIS_ LPMONIKER FAR* ppmk)
{
CLock lck(m_mxs); // protect all internal state
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (ppmk, LPMONIKER);
return CreateAntiMoniker(ppmk);
}
//+---------------------------------------------------------------------------
//
// Function: CompareNCharacters
//
// Synopsis: Compare N characters, ignoring case and sort order
//
// Effects: We are interested only in whether the strings are the same.
// Unlike wcscmp, which determines the sort order of the strings.
// This routine should save us some cycles
//
// Arguments: [pwcThis] --
// [pwcOther] --
// [n] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 2-14-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL CompareNCharacters( LPWSTR pwcThis, LPWSTR pwcOther, ULONG n)
{
while(n--)
{
if (CharUpperW((LPWSTR)*pwcThis) != CharUpperW((LPWSTR)*pwcOther))
{
return(FALSE);
}
pwcThis++;
pwcOther++;
}
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: CopyNCharacters
//
// Synopsis: Copy N characters from lpSrc to lpDest
//
// Effects:
//
// Arguments: [lpDest] -- Reference to lpDest
// [lpSrc] -- Pointer to source characters
// [n] --
//
// Requires:
//
// Returns:
//
// Returns with lpDest pointing to the end of the string. The string will
// be NULL terminated
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 2-14-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
inline
void CopyNCharacters( LPWSTR &lpDest, LPWSTR lpSrc, ULONG n)
{
memcpy(lpDest,lpSrc,sizeof(WCHAR)*n);
lpDest += n;
*lpDest = 0;
}
//+---------------------------------------------------------------------------
//
// Function: DetermineLongestString
//
// Synopsis: Used by CommonPrefixWith to handle case where one string may
// be longer than the other.
// Effects:
//
// Arguments: [pwcBase] --
// [pwcPrefix] --
// [pwcLonger] --
//
// Requires:
//
// Returns: TRUE if all of pwcBase is a prefix of what pwcLonger is the
// end of, or if tail of pwcBase is a separator.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 2-14-94 kevinro Created
// 03-27-94 darryla Added special case where pwcPrefix is
// pointing at terminator and previous char
// is a separator.
//
// Notes:
//
// See CommonPrefixWith. This code isn't a general purpose routine, and is
// fairly intimate with CommonPrefixWith.
//
//
//----------------------------------------------------------------------------
BOOL DetermineLongestString( LPWSTR pwcBase,
LPWSTR &pwcPrefix,
LPWSTR pwcLonger)
{
//
// pwcPrefix is the end of the string that so far matches pwcLonger
// as a prefix.
//
// If the next character in pwcLonger is a seperator, then pwcPrefix
// is a complete prefix. Otherwise, we need to back pwcPrefix to the
// next prevous seperator character
//
if (IsSeparator(*pwcLonger))
{
//
// pwcPrefix is a true prefix
//
return TRUE;
}
// One more special case. If pwcPrefix is pointing at a terminator and
// the previous char is a separator, then this, too, is a valid prefix.
// It is easier to catch this here than to try to walk back to the
// separator and then determine if it was at the end.
if (*pwcPrefix == '\0' && IsSeparator(*(pwcPrefix - 1)))
{
//
// pwcPrefix is a true prefix ending with a separator
//
return TRUE;
}
//
// We now have a situtation where pwcPrefix holds a string that is
// might not be a prefix of pwcLonger. We need to start backing up
// until we find a seperator character.
//
LPWSTR pStart = pwcPrefix;
while (pwcPrefix > pwcBase)
{
if (IsSeparator(*pwcPrefix))
{
break;
}
pwcPrefix--;
}
//
// NULL terminate the output string.
//
*pwcPrefix = 0;
//
// If pStart == pwcPrefix, then we didn't actually back up anything, or
// we just removed a trailing backslash. If so, return TRUE, since the
// pwcPrefix is a prefix of pwcLonger
//
if (pStart == pwcPrefix)
{
return(TRUE);
}
return(FALSE);
}
//+---------------------------------------------------------------------------
//
// Function: IsEmptyString
//
// Synopsis: Determine if a string is 'Empty', which means either NULL
// or zero length
//
// Effects:
//
// Arguments: [lpStr] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 2-25-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
inline
BOOL IsEmptyString(LPWSTR lpStr)
{
if ((lpStr == NULL) || (*lpStr == 0))
{
return(TRUE);
}
return(FALSE);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::CommonPrefixWith
//
// Synopsis: Given two file monikers, determine the common prefix for
// the two monikers.
//
// Effects: Computes a path that is the common prefix between the two
// paths. It does this by string comparision, taking into
// account the m_cAnti member, which counts the number of
// preceeding dot dots constructs for each moniker.
//
// Arguments: [pmkOther] --
// [ppmkPrefix] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 2-10-94 kevinro Created
//
// Notes:
//
// Welcome to some rather hairy code. Actually, it isn't all that bad,
// there are just quite a few boundary cases that you will have to
// contend with. I am sure if I thought about it long enough, there is
// a better way to implement this routine. However, it really isn't
// worth the effort, given the frequency at which this API is called.
//
// I have approached this in a very straightforward way. There is
// room for optimization, but it currently isn't high enough on
// the priority list.
//
// File monikers need to treat the end server with care. We actually
// consider the \\server\share as a single component. Therefore, if
// the two monikers are \\savik\win40\foo and \\savik\cairo\foo,
// then \\savik is NOT a common prefix.
//
// Same holds true with the <drive>: case, where we need to treat
// the drive as a unit
//
// To determine if two monikers have a common prefix, we look
// down both paths watching for the first non-matching
// character. When we find it, we need determine the correct
// action to take.
//
// \\foo\bar and foo\bar shouldn't match
// c:\foo\bar and c:\foo should return c:\foo
// c:\foo\bar and c:\foobar should return c:\ .
//
// Be careful to handle the server case.
//
// \\savik\win40 and
// \\savik\win40\src\foo\bar should return \\savik\win40
// while \\savik\cairo should return MK_E_NOPREFIX
//
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::CommonPrefixWith (LPMONIKER pmkOther, LPMONIKER FAR*
ppmkPrefix)
{
CLock lck(m_mxs); // protect all internal state
return wCommonPrefixWith( pmkOther, ppmkPrefix );
}
STDMETHODIMP CFileMoniker::wCommonPrefixWith (LPMONIKER pmkOther, LPMONIKER FAR*
ppmkPrefix)
{
wValidateMoniker();
VDATEPTROUT (ppmkPrefix, LPMONIKER);
*ppmkPrefix = NULL;
VDATEIFACE (pmkOther);
CFileMoniker FAR* pcfmOther = NULL;
CFileMoniker FAR* pcfmPrefix = NULL;
HRESULT hresult = NOERROR;
USHORT cAnti;
//
// The following buffer will contain the matching prefix. We should
// be safe in MAX_PATH, since neither path can be longer than that.
// This was verified when the moniker was created, so no explicit
// checking is done in this routine
//
WCHAR awcMatchingPrefix[MAX_PATH + 1];
WCHAR *pwcPrefix = awcMatchingPrefix;
//
// Each subsection of the path will be parsed into the following
// buffer. This allows us to match each section of the path
// independently
//
WCHAR awcComponent[MAX_PATH + 1];
WCHAR *pwcComponent = awcComponent;
*pwcPrefix = 0; // Null terminate the empty string
*pwcComponent = 0; // Null terminate the empty string
//
// A couple temporaries to walk the paths.
//
LPWSTR pwcThis = NULL;
LPWSTR pwcOther = NULL;
HRESULT hrPrefixType = S_OK;
//
// If the other moniker isn't 'one of us', then get the generic system
// provided routine to handle the rest of this call.
//
if ((pcfmOther = IsFileMoniker(pmkOther)) == NULL)
{
return MonikerCommonPrefixWith(this, pmkOther, ppmkPrefix);
}
//
// If the m_cAnti fields are different, then match the minimum number of
// dotdots.
//
//
if (pcfmOther->m_cAnti != m_cAnti)
{
// differing numbers of ..\ at the beginning
cAnti = (m_cAnti > pcfmOther->m_cAnti ? pcfmOther->m_cAnti :m_cAnti );
if (cAnti == 0)
{
hresult = ResultFromScode(MK_E_NOPREFIX);
}
// pcfmPrefix is NULL
else
{
pcfmPrefix = CFileMoniker::Create(L"",
cAnti);
if (pcfmPrefix == NULL)
{
hresult = E_OUTOFMEMORY;
goto exitRoutine;
}
// we must check to see if the final result is that same as
// this or pmkOther
hresult = NOERROR;
if (cAnti == m_cAnti)
{
if ((m_szPath==NULL)||(*m_szPath == '\0'))
{
hresult = MK_S_ME;
}
}
else
{
if ((pcfmOther->m_szPath == NULL ) ||
(*(pcfmOther->m_szPath) == '\0') )
{
hresult = MK_S_HIM;
}
}
}
goto exitRoutine;
}
//
// The number of leading dot-dots match. Therefore, we need to
// compare the paths also. If no path exists, then the common prefix
// is going to be the 'dot-dots'
//
cAnti = m_cAnti;
pwcThis = m_szPath;
pwcOther = pcfmOther->m_szPath;
//
// If either pointer is empty, then only the dotdots make for a prefix
//
if (IsEmptyString(pwcThis) || IsEmptyString(pwcOther))
{
//
// At least one of the strings was empty, therefore the common
// prefix is only the dotdots. Determine if its US, ME, or HIM
//
if (IsEmptyString(pwcThis) && IsEmptyString(pwcOther))
{
hrPrefixType = MK_S_US;
}
else if (IsEmptyString(pwcThis))
{
hrPrefixType = MK_S_ME;
}
else
{
hrPrefixType = MK_S_HIM;
}
goto onlyDotDots;
}
//
// The strings may be prefaced by either a UNC name, or a 'drive:'
// We treat both of these as a unit, and will only match prefixes
// on paths that match UNC servers, or match drives.
//
// If it is a UNC name, then m_endServer will be set to point at
// the end of the UNC name.
//
// First part of the match is to determine if the end servers are even
// close. If the offsets are different, the answer is no.
//
//
// The assertion at this point is that neither string is 'empty'
//
Assert( !IsEmptyString(pwcThis));
Assert( !IsEmptyString(pwcOther));
if (m_endServer != pcfmOther->m_endServer)
{
//
// End servers are different, match only the dotdots. Neither
// string is a complete
//
hrPrefixType = S_OK;
goto onlyDotDots;
}
//
// If the end servers are the default value, then look to see if
// this is an absolute path. Otherwise, copy over the server section
//
if (m_endServer == DEF_ENDSERVER)
{
BOOL fThisAbsolute = IsAbsoluteNonUNCPath(pwcThis);
BOOL fOtherAbsolute = IsAbsoluteNonUNCPath(pwcOther);
//
// If both paths are absolute, check for matching characters.
// If only one is absolute, then match the dot dots.
//
if (fThisAbsolute && fOtherAbsolute)
{
//
// Both absolute paths (ie 'c:' at the front)
// If not the same, only dotdots
//
if (CharUpperW((LPWSTR)*pwcThis) != CharUpperW((LPWSTR)*pwcOther))
{
//
// The paths don't match
//
hrPrefixType = S_OK;
goto onlyDotDots;
}
//
// The <drive>: matched. Copy it over
//
CopyNCharacters(pwcPrefix,pwcThis,2);
pwcThis += 2;
pwcOther += 2;
}
else if (fThisAbsolute || fOtherAbsolute)
{
//
// One path is absolute, the other isn't.
// Match only the dots
//
hrPrefixType = S_OK;
goto onlyDotDots;
}
//
// The fall through case does more path processing
//
}
else
{
//
// m_endServer is a non default value. Check to see if the
// first N characters match. If they don't, then only match
// the dotdots. If they do, copy them to the prefix buffer
//
if (!CompareNCharacters(pwcThis,pwcOther,m_endServer))
{
//
// The servers didn't match.
//
hrPrefixType = S_OK;
goto onlyDotDots;
}
//
// The UNC paths matched, copy them over
//
CopyNCharacters(pwcPrefix,pwcThis,m_endServer);
pwcThis += m_endServer;
pwcOther += m_endServer;
}
//
// Handle the root directory case. If BOTH monikers start
// with a backslash, then copy this to the prefix section.
// This allows for having '\foo' and '\bar' have the common
// prefix of '\'. The code below this section will remove
// any trailing backslashes.
//
// This also takes care of the case where you have a
// drive: or \\server\share, followed by a root dir.
// In either of these cases, we should return
// drive:\ or \\server\share\ respectively
//
if ((*pwcThis == '\\') && (*pwcOther == '\\'))
{
*pwcPrefix = '\\';
pwcThis++;
pwcOther++;
pwcPrefix++;
*pwcPrefix = 0;
}
//
// At this point, we have either matched the drive/server section,
// or have an empty string. Time to start copying over the rest
// of the data.
//
//
// Walk down the strings, looking for the first non-matching
// character
//
while (1)
{
if ((*pwcThis == 0) || (*pwcOther == 0))
{
//
// We have hit the end of one or both strings.
// awcComponent holds all of the matching
// characters so far. Break out of the loop
//
break;
}
if (CharUpperW((LPWSTR)*pwcThis) != CharUpperW((LPWSTR)*pwcOther))
{
//
// This is the first non-matching character.
// We should break out here.
//
break;
}
//
// At this point, the characters match, and are part
// of the common prefix. Copy it to the string, and move on
//
*pwcComponent = *pwcThis;
pwcThis++;
pwcOther++;
pwcComponent++;
//
// NULL terminate the current version of the component string
//
*pwcComponent = '\0';
}
//
// If both strings are at the end, then we have a
// complete match.
//
if ((*pwcThis == 0) && (*pwcOther == 0))
{
//
// Ah, this feels good. The strings ended up being
// the same length, with all matching characters.
//
// Therefore, we can just return one of us as the
// result.
//
pcfmPrefix = this;
AddRef();
hresult = MK_S_US;
goto exitRoutine;
}
//
// If one of the strings is longer than the other...
//
if ((*pwcThis == 0) || (*pwcOther == 0))
{
//
// Test to see if the next character in the longer string is a
// seperator character. If it isn't, then back up the string to
// the character before the previous seperator character.
//
// If TRUE then the shorter of the strings ends up being the
// entire prefix.
//
//
if( DetermineLongestString( awcComponent,
pwcComponent,
(*pwcThis == 0)?pwcOther:pwcThis) == TRUE)
{
if (*pwcThis == 0)
{
//
// This is the entire prefix
//
pcfmPrefix = this;
hresult = MK_S_ME;
}
else
{
//
// The other guy is the entire prefix
//
pcfmPrefix = pcfmOther;
hresult = MK_S_HIM;
}
pcfmPrefix->AddRef();
goto exitRoutine;
}
}
else
{
//
// Right now, pwcThis and pwcOther point at non-matching characters.
// Given the above tests, we know that neither character is
// == 0.
//
// Backup the string to the previous seperator. To do this, we
// will use DetermineLongestString, and pass it the string that
// doesn't have a seperator
//
DetermineLongestString( awcComponent,
pwcComponent,
IsSeparator(*pwcThis)?pwcOther:pwcThis);
}
//
// At this point, awcsComponent holds the second part of the string,
// while awcsPrefix holds the server or UNC prefix. Either of these
// may be NULL. Append awcComponent to the end of awcPrefix.
//
CopyNCharacters( pwcPrefix, awcComponent, pwcComponent - awcComponent);
//
// Check to see if anything matched.
//
if (pwcPrefix == awcMatchingPrefix)
{
//
// The only matching part is the dotdot count.
// This is easy, since we can just create a new
// moniker consisting only of dotdots.
//
// However, if there are no preceeding dotdots,
// then there was absolutely no prefix, which means
// we return MK_E_NOPREFIX
//
if (cAnti == 0)
{
hresult = MK_E_NOPREFIX;
goto exitRoutine;
}
//
// Nothing special about the moniker, so just return S_OK
//
hrPrefixType = S_OK;
goto onlyDotDots;
}
//
// Create a new file moniker using the awcMatchingPrefix
//
pcfmPrefix = CFileMoniker::Create(awcMatchingPrefix,0,cAnti);
if (pcfmPrefix == NULL)
{
hresult = E_OUTOFMEMORY;
goto exitRoutine;
}
hresult = S_OK;
exitRoutine:
*ppmkPrefix = pcfmPrefix; // null, or a file moniker
return hresult;
onlyDotDots:
//
// We have determined that only the dotdot's match, so create a
// new moniker with the appropriate number of them.
//
// If there are no dotdots, then return NULL
//
if (cAnti == 0)
{
hresult = MK_E_NOPREFIX;
goto exitRoutine;
}
pcfmPrefix = CFileMoniker::Create(L"",0,cAnti);
if (pcfmPrefix == NULL)
{
hresult = E_OUTOFMEMORY;
}
else
{
hresult = hrPrefixType;
}
goto exitRoutine;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::RelativePathTo
//
// Synopsis: Compute a relative path to the other moniker
//
// Effects:
//
// Arguments: [pmkOther] --
// [ppmkRelPath] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 2-24-94 kevinro Created
//
// Notes:
//
// BUGBUG: (KevinRo)
// This routine was really bad, and didn't generate correct results (aside
// from the fact that it faulted). I replaced it with a slightly less
// effiecient, but correct implementation.
//
// This can be improved on, but I currently have time restraints, so I am
// not spending the needed amount of time. What really needs to happen is
// the code that determines the common path prefix string from
// CommonPrefixWith() should be broken out so this routine can share it.
//
// Thats more work that I can do right now, so we will just call CPW,
// and use its result to compute the relative path. This results in an
// extra moniker creation (allocate and construct only), but will work
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::RelativePathTo (THIS_ LPMONIKER pmkOther,
LPMONIKER FAR*
ppmkRelPath)
{
CLock lck(m_mxs); // protect all internal state
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (ppmkRelPath, LPMONIKER);
*ppmkRelPath = NULL;
VDATEIFACE (pmkOther);
HRESULT hr;
CFileMoniker FAR* pcfmPrefix;
LPWSTR lpszSuffix;
LPWSTR lpszOther;
CFileMoniker FAR* pcfmRelPath = NULL;
CFileMoniker FAR* pcfmOther = IsFileMoniker(pmkOther);
if (!pcfmOther)
{
return MonikerRelativePathTo(this, pmkOther, ppmkRelPath, TRUE);
}
//
// Determine the common prefix between the two monikers. This generates
// a moniker which has a path that is the prefix between the two
// monikers
//
hr = CommonPrefixWith(pmkOther,(IMoniker **)&pcfmPrefix);
//
// If there was no common prefix, then the relative path is 'him'
//
if (hr == MK_E_NOPREFIX)
{
*ppmkRelPath = pmkOther;
pmkOther->AddRef();
return MK_S_HIM;
}
if (FAILED(hr))
{
*ppmkRelPath = NULL;
return(hr);
}
//
// At this point, the common prefix to the two monikers is in pcfmPrefix
// Since pcfmPrefix is a file moniker, we know that m_ccPath is the
// number of characters that matched in both moniker paths. To
// compute the relative part, we use the path from pmkOther, minus the
// first pcfmPrefix->m_ccPath characters.
//
// We don't want to start with a seperator. Therefore, skip over the
// first set of seperator characters. (Most likely, there aren't any).
//
lpszOther = pcfmOther->m_szPath + pcfmPrefix->m_ccPath;
lpszSuffix = m_szPath + pcfmPrefix->m_ccPath;
while ((*lpszSuffix != 0) && IsSeparator(*lpszSuffix))
{
lpszSuffix++;
}
//
// Create new file moniker that holds the prefix.
//
pcfmRelPath = CFileMoniker::Create(lpszOther,
CountSegments(lpszSuffix));
//
// At this point, we are all done with the prefix
//
pcfmPrefix->Release();
if (pcfmRelPath == NULL)
{
*ppmkRelPath = NULL;
return ResultFromScode(S_OOM);
}
*ppmkRelPath = pcfmRelPath;
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GetDisplayNameLength
//
// Synopsis: Returns the length of the display name if GenerateDisplayName
// was called
//
// Effects:
//
// Returns: Length of display name in bytes
//
// Algorithm:
//
// History: 3-16-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
ULONG
CFileMoniker::GetDisplayNameLength()
{
CLock lck(m_mxs); // protect all internal state
// Number of characters in path plus number of anti components plus NULL
// All times the size of WCHAR
//
// Anti components look like '..\' in the string. 3 characters
ULONG ulLength = (m_ccPath + (3 * m_cAnti) + 1) * sizeof(WCHAR);
return(ulLength);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GenerateDisplayName, private
//
// Synopsis: Generates a display name for this moniker.
//
// Effects:
//
// Arguments: [pwcDisplayName] -- A buffer that is at least as long as
// GetDisplayNameLength
//
// Returns: void
//
// Algorithm:
//
// History: 3-16-95 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
void
CFileMoniker::GenerateDisplayName(LPWSTR pwcDisplayName)
{
Assert(pwcDisplayName != NULL);
//
// The display name may need 'dotdots' at the front
//
for (USHORT i = 0; i < m_cAnti; i++)
{
memcpy(pwcDisplayName, L"..\\", 3 * sizeof(WCHAR));
pwcDisplayName += 3;
}
//
// don't duplicate '\' since the anti monikers may
// have already appended one. Copy rest of string
// over, including the NULL
//
if (m_cAnti > 0 && *m_szPath == '\\')
{
memcpy(pwcDisplayName, m_szPath + 1, m_ccPath * sizeof(WCHAR));
}
else
{
memcpy(pwcDisplayName, m_szPath, (m_ccPath + 1) * sizeof(WCHAR));
}
}
STDMETHODIMP CFileMoniker::GetDisplayName ( LPBC pbc, LPMONIKER
pmkToLeft, LPWSTR FAR * lplpszDisplayName )
{
HRESULT hr = E_FAIL;
CLock lck(m_mxs); // protect all internal state
wValidateMoniker();
M_PROLOG(this);
VDATEPTROUT (lplpszDisplayName, LPWSTR);
*lplpszDisplayName = NULL;
VDATEIFACE (pbc);
if (pmkToLeft)
{
VDATEIFACE (pmkToLeft);
}
int n;
LPWSTR pch;
LPWSTR pchSrc;
DWORD cchSrc;
DWORD ulLen;
#ifdef _CAIRO_
// First, if this is a tracking moniker, resolve it.
if( m_fIsTracking )
{
//
// Attempt to Resolve the moniker very quickly. That is, if it has
// moved within the local set of indexed (e.g. OFS) volumes, we will update the
// moniker and be able to give the correct display name to the user.
// If any stage of this process fails, the error is ignored,
// and we return the currently known display name to the caller.
//
QuickShellLinkResolve( pbc );
} // if( m_fIsTracking )
#endif // _CAIRO_
ulLen = GetDisplayNameLength();
//
// cchSrc is the number of characters including the NULL. This will
// always be half the number of bytes.
//
cchSrc = ulLen >> 1;
(*lplpszDisplayName) = (WCHAR *) CoTaskMemAlloc(ulLen);
pch = *lplpszDisplayName;
if (!pch)
{
hr = E_OUTOFMEMORY;
goto Exit;
}
//
// Call a common routine to generate the initial display name
//
GenerateDisplayName(pch);
// If we're in WOW, return short path names so that 16-bit apps
// don't see names they're not equipped to handle. This also
// affects 32-bit inproc DLLs in WOW; they'll need to be written
// to handle it
if (IsWOWProcess())
{
DWORD cchShort, cchDone;
LPOLESTR posCur;
posCur = *lplpszDisplayName;
// GetShortPathName only works on files that exist. Monikers
// don't have to refer to files that exist, so if GetShortPathName
// fails we just return whatever the moniker has as a path
// Special case zero-length paths since the length returns from
// GetShortPathName become ambiguous when zero characters are processed
cchShort = lstrlenW(posCur);
if (cchShort > 0)
{
cchShort = GetShortPathName(posCur, NULL, 0);
}
if (cchShort != 0)
{
LPOLESTR posShort;
// GetShortPathName can convert in place so if our source
// string is long enough, don't allocate a new string
if (cchShort <= cchSrc)
{
posShort = posCur;
cchShort = cchSrc;
}
else
{
posShort = (LPOLESTR)CoTaskMemAlloc(cchShort*sizeof(WCHAR));
if (posShort == NULL)
{
CoTaskMemFree(posCur);
*lplpszDisplayName = NULL;
hr = E_OUTOFMEMORY;
goto Exit;
}
}
cchDone = GetShortPathName(posCur, posShort, cchShort);
// For both success and failure cases we're done with posCur,
// so get rid of it (unless we've reused it for the short name)
if (posShort != posCur)
{
CoTaskMemFree(posCur);
}
if (cchDone == 0 || cchDone > cchShort)
{
CoTaskMemFree(posShort);
*lplpszDisplayName = NULL;
hr = E_OUTOFMEMORY;
goto Exit;
}
*lplpszDisplayName = posShort;
}
}
hr = NOERROR;
Exit:
return( hr );
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::ParseDisplayName
//
// Synopsis: Bind to object, and ask it to parse the display name given.
//
// Effects:
//
// Arguments: [pbc] -- Bind context
// [pmkToLeft] -- Moniker to the left
// [lpszDisplayName] -- Display name to be parsed
// [pchEaten] -- Outputs the number of characters parsed
// [ppmkOut] -- Output moniker
//
// Requires:
// File-monikers never have monikers to their left
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 2-02-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::ParseDisplayName ( LPBC pbc,
LPMONIKER pmkToLeft,
LPWSTR lpszDisplayName,
ULONG FAR* pchEaten,
LPMONIKER FAR* ppmkOut)
{
HRESULT hresult;
IParseDisplayName * pPDN = NULL;
CLSID cid;
VDATEPTROUT (ppmkOut, LPMONIKER);
*ppmkOut = NULL;
VDATEIFACE (pbc);
if (pmkToLeft)
{
VDATEIFACE (pmkToLeft);
}
VDATEPTRIN (lpszDisplayName, WCHAR);
VDATEPTROUT (pchEaten, ULONG);
//
// Since this is the most frequent case, try binding to the object
// itself first
//
hresult = BindToObject( pbc,
pmkToLeft,
IID_IParseDisplayName,
(VOID FAR * FAR *)&pPDN );
// we deferred doing this lock until after the BindToObject, in case the
// BindToObject is very slow. It manages locking internally to itself.
CLock lck(m_mxs); // protect all internal state
// If binding to the object failed, then try binding to the class object
// asking for the IParseDisplayName interface
if (FAILED(hresult))
{
hresult = GetClassFile(m_szPath, &cid);
if (SUCCEEDED(hresult))
{
hresult = CoGetClassObject(cid,
#ifdef WX86OLE
gcwx86.IsWx86Enabled() ?
CLSCTX_INPROC |
CLSCTX_INPROC_SERVERX86 |
CLSCTX_INPROC_HANDLERX86 :
CLSCTX_INPROC,
#else
CLSCTX_INPROC,
#endif
NULL,
IID_IParseDisplayName,
(LPVOID FAR*)&pPDN);
}
if (FAILED(hresult))
{
goto errRet;
}
}
//
// Now that we have bound this object, we register it with the bind
// context. It will be released with the bind context release.
//
hresult = pbc->RegisterObjectBound(pPDN);
if (FAILED(hresult))
{
goto errRet;
}
//
// As the class code to parse the rest of the display name for us.
//
hresult = pPDN->ParseDisplayName(pbc,
lpszDisplayName,
pchEaten,
ppmkOut);
errRet:
if (pPDN) pPDN->Release();
return hresult;
}
STDMETHODIMP CFileMoniker::IsSystemMoniker (THIS_ LPDWORD pdwType)
{
M_PROLOG(this);
VDATEPTROUT (pdwType, DWORD);
*pdwType = MKSYS_FILEMONIKER;
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::ValidateAnsiPath
//
// Synopsis: This function validates the ANSI version of the path. Intended
// to be used to get the serialized Ansi version of the path.
//
// This function also detects when a Long File Name exists, and
// must be dealt with.
//
// Effects:
//
// This routine will set the Ansi path suitable for serializing
// into the stream. This path may just use the stored ANSI path,
// or may be a path that was created from the UNICODE version.
//
// If the ANSI version of the path doesn't exist, then the UNICODE
// version of the path is converted to ANSI. There are several possible
// conversions.
//
// First, if the path uses a format that is > 8.3, then the path to
// be serialized needs to be the alternate name. This allows the
// downlevel systems to access the file using the short name. This step
//
// If the UNICODE path is all ANSI characters already (no DBCSLeadBytes),
// then the path is converted by doing a simple truncation algorithm.
//
// If the UNICODE path contains large characters, or DBCSLeadBytes,
// then the routine will create a UNICODE extent, then try to convert
// the UNICODE string into a ANSI path. If some of the characters
// won't convert, then those characters are represented by an ANSI
// character constant defined in the registry.
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
// m_pszAnsiPath
// m_cbAnsiPath
//
// Derivation:
//
// Algorithm:
//
// History: 1-09-94 kevinro Created
// 05-24-94 AlexT Use GetShortPathNameW
//
// Notes:
//
// The path created may not actually be useful. It is quite possible
// for there to be a path that will not convert correctly from UNICODE
// to Ansi. In these cases, this routine will create a UNICODE extent.
//
//----------------------------------------------------------------------------
HRESULT CFileMoniker::ValidateAnsiPath(void)
{
wValidateMoniker();
HRESULT hr = NOERROR;
{
CLock lck(m_mxs); // protect m_pszAnsiPath, and m_cbAnsiPath
// also AddExtent (needed since mutext was removed from CExtentList).
mnkDebugOut((DEB_ITRACE,
"GetAnsiPath(%x) m_szPath(%ws)\n",
this,
m_szPath?m_szPath:L"<NULL>"));
BOOL fFastConvert = FALSE;
//
// If there is no path, return NULL
//
if (m_szPath == NULL)
{
goto NoError;
}
//
// If there is already an ANSI path, return, we are OK.
//
if (m_pszAnsiPath != NULL)
{
goto NoError;
}
// We can't call GetShortPathNameW with a NULL string. m_szPath can
// be "" as the result of CoCreateInstance of a file moniker or as
// the result of RelativePathTo being called on an identical file moniker
if ('\0' != *m_szPath)
{
OLECHAR szShortPath[MAX_PATH];
DWORD dwBytesCopied;
dwBytesCopied = GetShortPathName(m_szPath, szShortPath, MAX_PATH);
if (dwBytesCopied > 0 && dwBytesCopied <= MAX_PATH)
{
hr = MnkUnicodeToMulti(szShortPath,
lstrlenW(szShortPath),
m_pszAnsiPath,
m_cbAnsiPath,
fFastConvert);
if (FAILED(hr))
{
mnkDebugOut((DEB_ITRACE,
"MnkUnicodeToMulti failed (%x) on %ws\n",
WIDECHECK(szShortPath)));
goto ErrRet;
}
}
#if DBG==1
if (0 == dwBytesCopied)
{
mnkDebugOut((DEB_ITRACE,
"GetShortPathName failed (%x) on %ws\n",
GetLastError(),
WIDECHECK(szShortPath)));
// let code below handle the path
}
else if (dwBytesCopied > MAX_PATH)
{
mnkDebugOut((DEB_ITRACE,
"GetShortPathName buffer not large enough (%ld, %ld)\n",
MAX_PATH, dwBytesCopied,
WIDECHECK(szShortPath)));
// let code below handle the path
}
#endif // DBG==1
}
//
// If there is no m_pszAnsiPath yet, then just convert
// the UNICODE path to the ANSI path
//
if (m_pszAnsiPath == NULL)
{
//
// There was no alternate file name
//
hr = MnkUnicodeToMulti( m_szPath,
m_ccPath,
m_pszAnsiPath,
m_cbAnsiPath,
fFastConvert);
if (FAILED(hr))
{
goto ErrRet;
}
}
else
{
//
// We have an alternate name. By setting
// fFastConvert to be FALSE, we force the
// following code to add a UNICODE extent
// if one doesn't exist.
//
fFastConvert = FALSE;
}
//
// If an extent doesn't already exist, and it wasn't a fast
// conversion, create a UNICODE extent.
//
if ( !m_fUnicodeExtent && !fFastConvert)
{
LPMONIKEREXTENT pExtent = NULL;
hr = CopyPathToUnicodeExtent(m_szPath,m_ccPath,pExtent);
if (FAILED(hr))
{
goto ErrRet;
}
hr = m_ExtentList.AddExtent(pExtent);
PrivMemFree(pExtent);
if (FAILED(hr))
{
goto ErrRet;
}
}
NoError:
mnkDebugOut((DEB_ITRACE,
"GetAnsiPath(%x) m_pszAnsiPath(%s) m_cbAnsiPath(0x%x)\n",
this,
m_pszAnsiPath?m_pszAnsiPath:"<NULL>",
m_cbAnsiPath));
return(NOERROR);
ErrRet:
mnkDebugOut((DEB_IERROR,
"GetAnsiPath(%x) Returning error hr(%x)\n",
this,
hr));
if (m_pszAnsiPath != NULL)
{
PrivMemFree(m_pszAnsiPath);
m_pszAnsiPath = NULL;
m_cbAnsiPath = 0;
}
}
wValidateMoniker();
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: MnkUnicodeToMulti
//
// Synopsis: Convert a Unicode path to an Ansi path.
//
// Effects:
//
// Arguments: [pwcsWidePath] -- Unicode path
// [ccWidePath] -- Wide character count
// [pszAnsiPath] -- Reference
// [cbAnsiPath] -- ref number of bytes in ANSI path incl NULL
// [fFastConvert] -- Returns TRUE if fast conversion
//
// Requires:
//
// Returns:
//
// pszAnsiPath was allocated using PrivMemAlloc
//
// fFastConvert means that the ANSI and UNICODE paths were converted
// by WCHAR->CHAR truncation.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT
MnkUnicodeToMulti(LPWSTR pwcsWidePath,
USHORT ccWidePath,
LPSTR & pszAnsiPath,
USHORT & cbAnsiPath,
BOOL & fFastConvert)
{
HRESULT hr = NOERROR;
ULONG cb;
BOOL fUsedDefaultChar = FALSE;
WCHAR *lp = pwcsWidePath;
fFastConvert = TRUE;
if (pwcsWidePath == NULL)
{
cbAnsiPath = 0;
pszAnsiPath = 0;
return(NOERROR);
}
//
// Lets hope for the best. If we can run the length of the
// unicode string, and all the characters are 1 byte long, and
// there are no conflicts with DBCSLeadBytes, then we
// can cheat and just do a truncation copy
//
while ( (*lp != 0) && (*lp == (*lp & 0xff)) && !IsDBCSLeadByte(*lp & 0xff))
{
lp++;
}
if (*lp == 0)
{
//
// We are at the end of the string, and we are safe to do our
// simple copy. We will assume the ANSI version of the path is
// going to have the same number of characters as the wide path
//
pszAnsiPath = (char *)PrivMemAlloc(ccWidePath + 1);
if (pszAnsiPath == NULL)
{
hr = E_OUTOFMEMORY;
goto ErrRet;
}
USHORT i;
//
// By doing i <= m_ccPath, we pick up the NULL
//
for (i = 0 ; i <= ccWidePath ; i++ )
{
pszAnsiPath[i] = pwcsWidePath[i] & 0xff;
}
//
// We just converted to a single byte path. The cb is the
// count of WideChar + 1 for the NULL
//
cbAnsiPath = ccWidePath + 1;
goto NoError;
}
//
// At this point, all of the easy out options have expired. We
// must convert the path the hard way.
//
fFastConvert = FALSE;
mnkDebugOut((DEB_ITRACE,
"MnkUnicodeToMulti(%ws) doing path conversions\n",
pwcsWidePath?pwcsWidePath:L"<NULL>"));
//
// We haven't a clue how large this path may be in bytes, other
// than some really large number. So, we need to call and find
// out the correct size to allocate for the path.
//
cb = WideCharToMultiByte(AreFileApisANSI() ? CP_ACP : CP_OEMCP,
WC_COMPOSITECHECK | WC_DEFAULTCHAR,
pwcsWidePath,
ccWidePath + 1, // Convert the NULL
NULL,
0,
NULL,
&fUsedDefaultChar);
if (cb == 0)
{
//
// Hmmm... Can't convert anything. Sounds like the downlevel
// guys are flat out of luck. This really isn't a hard error, its
// just an unfortunate fact of life. This is going to be a very
// rare situation, but one we need to handle gracefully
//
pszAnsiPath = NULL;
cbAnsiPath = 0;
}
else
{
//
// cb holds the number of bytes required for the output path
//
pszAnsiPath = (char *)PrivMemAlloc(cb + 1);
if (pszAnsiPath == NULL)
{
hr = E_OUTOFMEMORY;
goto ErrRet;
}
cbAnsiPath = (USHORT)cb;
cb = WideCharToMultiByte(AreFileApisANSI() ? CP_ACP : CP_OEMCP,
WC_COMPOSITECHECK | WC_DEFAULTCHAR,
pwcsWidePath,
ccWidePath + 1, // Convert the NULL
pszAnsiPath,
cbAnsiPath,
NULL,
&fUsedDefaultChar);
//
// Again, if there was an error, its just unfortunate
//
if (cb == 0)
{
PrivMemFree(pszAnsiPath);
pszAnsiPath = NULL;
cbAnsiPath = 0;
}
}
NoError:
return(NOERROR);
ErrRet:
if (pszAnsiPath != NULL)
{
PrivMemFree(pszAnsiPath);
pszAnsiPath = NULL;
cbAnsiPath = 0;
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: MnkMultiToUnicode
//
// Synopsis: Converts a MultiByte string to a Unicode string
//
// Effects:
//
// Arguments: [pszAnsiPath] -- Path to convert
// [pWidePath] -- Output path
// [ccWidePath] -- Size of output path
// [ccNewString] -- Reference characters in new path
// including the NULL
// [nCodePage] -- Must be CP_ACP || CP_OEMCP. This is
// the first code page to be used in
// the attempted conversion. If the
// conversion fails, the other CP is
// tried.
//
// Requires:
//
// if pWidePath != NULL, then this routine uses pWidePath as the return
// buffer, which should be ccWidePath in length.
//
// Otherwise, it will allocate a buffer on your behalf.
//
// Returns:
//
// pWidePath != NULL
// ccNewString == number of characters in new path include NULL
//
// pWidePath == NULL (NULL string)
//
// if ccNewString returns 0, then pWidePath may not be valid. In this
// case, there are no valid characters in pWidePath.
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
// 2-3-95 scottsk Added nCodePage param
//
// Notes:
//
// Why so complex you ask? In the file moniker case, we want know that the
// buffer can be MAX_PATH in length, so we pass a stack buffer in to handle
// it. In the CItemMoniker case, the limit jumps to 32767 bytes, which is
// too big to declare on the stack. I wanted to use the same routine for
// both, since we may end up changing this later.
//
// Passing in your own buffer is best for this routine.
//
//----------------------------------------------------------------------------
HRESULT MnkMultiToUnicode(LPSTR pszAnsiPath,
LPWSTR & pWidePath,
ULONG ccWidePath,
USHORT & ccNewString,
UINT nCodePage)
{
LPWSTR pwcsTempPath = NULL;
HRESULT hr;
Assert(nCodePage == CP_ACP || nCodePage == CP_OEMCP);
//
// If the pszAnsiPath is NULL, then so should be the UNICODE one
//
if (pszAnsiPath == NULL)
{
ccNewString = 0;
return(NOERROR);
}
Assert( (pWidePath == NULL) || (ccWidePath > 0));
//
// If the buffer is NULL, be sure that ccWide is zero
//
if (pWidePath == NULL)
{
ccWidePath = 0;
}
ConvertAfterAllocate:
ccNewString = MultiByteToWideChar(nCodePage,
MB_PRECOMPOSED,
pszAnsiPath,
-1,
pWidePath,
ccWidePath);
if (ccNewString == FALSE)
{
mnkDebugOut((DEB_IERROR,
"::MnkMultiToUnicode failed on (%s) err (%x)\n",
pszAnsiPath,
GetLastError()));
//
// We were not able to convert to UNICODE.
//
hr = E_UNEXPECTED;
goto errRet;
}
//
// ccNewString holds the total string length, including the terminating
// NULL.
//
if (pWidePath == NULL)
{
//
// The first time through did no allocations. Allocate the
// correct buffer, and actually do the conversion
//
pWidePath = (WCHAR *)PrivMemAlloc(sizeof(WCHAR)*ccNewString);
if (pWidePath == NULL)
{
hr = E_OUTOFMEMORY;
goto errRet;
}
pwcsTempPath = pWidePath;
ccWidePath = ccNewString;
goto ConvertAfterAllocate;
}
//
// ccNewString holds the total number of characters converted,
// including the NULL. We really want it to have the count of
// characeters
//
Assert (ccNewString != 0);
ccNewString--;
hr = NOERROR;
return(hr);
errRet:
mnkDebugOut((DEB_IERROR,
"::MnkMultiToUnicode failed on (%s) err (%x)\n",
pszAnsiPath,
GetLastError()));
PrivMemFree(pwcsTempPath);
return(hr);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::DetermineUnicodePath
//
// Synopsis: Given the input path, determine the path to store and use
// as the initialized path.
//
// Effects:
//
// When loading or creating a CFileMoniker, its possible that the 'path'
// that was serialized is not valid. This occurs when the original
// UNICODE path could not be translated into ANSI. In this case, there
// will be a MONIKEREXTENT that holds the original UNICODE based path.
//
// If a UNICODE extent exists, then the path will be ignored, and the
// path in the extent will be used.
//
// If a UNICODE extent doesn't exist, then the path will be translated
// into UNICODE. In theory, this will not fail, since there is supposed
// to always be a mapping from ANSI to UNICODE (but not the inverse).
// However, it is possible that the conversion will fail because the
// codepage needed to translate the ANSI path to UNICODE may not be
// loaded.
//
// In either case, the CFileMoniker::m_szPath should return set
// with some UNICODE path set. If not, then an error is returned
//
// Arguments: [pszPath] -- The ANSI version of the path.
// [pWidePath] -- Reference to pointer recieving new path
// [cbWidePath]-- Length of new path
//
// Requires:
//
// Returns:
// pWidePath is returned, as allocated from PrivMemAlloc
// cbWidePath holds length of new path
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-08-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT
CFileMoniker::DetermineUnicodePath(LPSTR pszAnsiPath,
LPWSTR & pWidePath,
USHORT &ccWidePath)
{
wValidateMoniker();
mnkDebugOut((DEB_ITRACE,
"DetermineUnicodePath(%x) pszAnsiPath(%s)\n",
this,
pszAnsiPath));
HRESULT hr = NOERROR;
//
// Check to see if a MONIKEREXTENT exists with mnk_UNICODE
//
MONIKEREXTENT UNALIGNED *pExtent = m_ExtentList.FindExtent(mnk_UNICODE);
//
// Normal fall through case is no UNICODE path, which means that there
// was a conversion between mbs and unicode in the original save.
//
if (pExtent == NULL)
{
m_fUnicodeExtent = FALSE;
//
// If the pszAnsiPath is NULL, then so should be the UNICODE one
//
if (pszAnsiPath == NULL)
{
pWidePath = NULL;
ccWidePath = 0;
return(NOERROR);
}
//
// It turns out to be cheaper to just assume a MAX_PATH size
// buffer, and to copy the resulting string. We use MAX_PATH + 1
// so we always have room for the terminating NULL
//
WCHAR awcTempPath[MAX_PATH+1];
WCHAR *pwcsTempPath = awcTempPath;
hr = MnkMultiToUnicode( pszAnsiPath,
pwcsTempPath,
MAX_PATH+1,
ccWidePath,
AreFileApisANSI() ? CP_ACP : CP_OEMCP);
if (FAILED(hr) || ccWidePath == 0)
{
goto errRet;
}
pWidePath = (WCHAR *)PrivMemAlloc(sizeof(WCHAR)*(ccWidePath+1));
if (pWidePath == NULL)
{
hr = E_OUTOFMEMORY;
goto errRet;
}
memcpy(pWidePath,pwcsTempPath,(ccWidePath+1)*sizeof(WCHAR));
hr = NOERROR;
}
else
{
//
// Get the UNICODE path from the extent.
//
mnkDebugOut((DEB_ITRACE,
"DeterminePath(%x) Found UNICODE extent\n",
this));
m_fUnicodeExtent = TRUE;
hr = CopyPathFromUnicodeExtent(pExtent,pWidePath,ccWidePath);
}
errRet:
if (FAILED(hr))
{
if (pWidePath != NULL)
{
PrivMemFree(pWidePath);
}
mnkDebugOut((DEB_IERROR,
"DeterminePath(%x) ERROR: Returning %x\n",
this,
hr));
}
else
{
mnkDebugOut((DEB_ITRACE,
"DeterminePath(%x) pWidePath(%ws) ccWidePath(0x%x)\n",
this,
pWidePath?pWidePath:L"<NULL PATH>",
ccWidePath));
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GetComparisonData
//
// Synopsis: Get comparison data for registration in the ROT
//
// Arguments: [pbData] - buffer to put the data in.
// [cbMax] - size of the buffer
// [pcbData] - count of bytes used in the buffer
//
// Returns: NOERROR
// E_OUTOFMEMORY
//
// Algorithm: Build ROT data for file moniker. This puts the classid
// followed by the display name.
//
// History: 03-Feb-95 ricksa Created
//
// Note: Validating the arguments is skipped intentionally because this
// will typically be called internally by OLE with valid buffers.
//
//----------------------------------------------------------------------------
STDMETHODIMP CFileMoniker::GetComparisonData(
byte *pbData,
ULONG cbMax,
DWORD *pcbData)
{
mnkDebugOut((DEB_ITRACE,
"_IN GetComparisionData(%x,%x,%x) for CFileMoniker(%ws)\n",
pbData,
cbMax,
*pcbData,
m_szPath));
CLock lck(m_mxs); // protect all internal state
ULONG ulLength = sizeof(CLSID_FileMoniker) + GetDisplayNameLength();
Assert(pcbData != NULL);
Assert(pbData != NULL);
if (cbMax < ulLength)
{
mnkDebugOut((DEB_ITRACE,
"OUT GetComparisionData() Buffer Too Small!\n"));
return(E_OUTOFMEMORY);
}
memcpy(pbData,&CLSID_FileMoniker,sizeof(CLSID_FileMoniker));
GenerateDisplayName((WCHAR *)(pbData+sizeof(CLSID_FileMoniker)));
//
// Insure this is an upper case string.
//
CharUpperW((WCHAR *)(pbData+sizeof(CLSID_FileMoniker)));
*pcbData = ulLength;
mnkDebugOut((DEB_ITRACE,
"OUT GetComparisionData() *pcbData == 0x%x\n",
*pcbData));
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Method: CTrackingFileMoniker::*
//
// Synopsis: These members implement ITrackingMoniker on behalf of
// the file moniker.
//
// Algorithm: The CTrackingFileMoniker object has a pointer to
// the CFileMoniker object and forwards any QI's (other than
// ITrackingMoniker) and AddRefs/Releases to the CFileMoniker.
//
//----------------------------------------------------------------------------
#ifdef _TRACKLINK_
VOID
CTrackingFileMoniker::SetParent(CFileMoniker *pCFM)
{
_pCFM = pCFM;
}
STDMETHODIMP CTrackingFileMoniker::QueryInterface(REFIID riid, void **ppv)
{
if (IsEqualIID(IID_ITrackingMoniker, riid))
{
*ppv = (ITrackingMoniker*) this;
_pCFM->AddRef();
return(S_OK);
}
else
return(_pCFM->QueryInterface(riid, ppv));
}
STDMETHODIMP_(ULONG) CTrackingFileMoniker::AddRef()
{
return(_pCFM->AddRef());
}
STDMETHODIMP_(ULONG) CTrackingFileMoniker::Release()
{
return(_pCFM->Release());
}
STDMETHODIMP CTrackingFileMoniker::EnableTracking( IMoniker *pmkToLeft, ULONG ulFlags )
{
return(_pCFM->EnableTracking(pmkToLeft, ulFlags));
}
#endif
#ifdef _DEBUG
STDMETHODIMP_(void) NC(CFileMoniker,CDebug)::Dump ( IDebugStream FAR * pdbstm)
{
VOID_VDATEIFACE(pdbstm);
*pdbstm << "CFileMoniker @" << (VOID FAR *)m_pFileMoniker;
*pdbstm << '\n';
pdbstm->Indent();
*pdbstm << "Refcount is " << (int)(m_pFileMoniker->m_refs) << '\n';
*pdbstm << "Path is " << m_pFileMoniker->m_szPath << '\n';
*pdbstm << "Anti count is " << (int)(m_pFileMoniker->m_cAnti) << '\n';
pdbstm->UnIndent();
}
STDMETHODIMP_(BOOL) NC(CFileMoniker,CDebug)::IsValid ( BOOL fSuspicious )
{
return ((LONG)(m_pFileMoniker->m_refs) > 0);
// add more later, maybe
}
#endif
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::RestoreShellLink, private
//
// Synopsis: Restore a ShellLink object by creating it, and
// loading the object's persistent state from the Extent
// of this Moniker (where the state was saved by an earlier
// instantiation).
//
// Arguments: [void]
//
// Returns: [HRESULT]
// - S_FALSE: the shell link had already been restored.
//
// Algorithm: If ShellLink object doesn't already exist
// GetShellLink()
// Load ShellLink object from moniker Extent.
// On Error,
// Release ShellLink
//
// Notes: - This routine does not restore the information
// in mnk_TrackingInformation. This is restored
// in Load().
//
//----------------------------------------------------------------------------
INTERNAL CFileMoniker::RestoreShellLink()
{
MONIKEREXTENT UNALIGNED * pExtent;
LARGE_INTEGER li0;
ULARGE_INTEGER uli;
HRESULT hr = E_FAIL;
IStream * pstm = NULL;
IPersistStream * pps = NULL;
// If we've already, successfully, initialized the shell link object,
// then we're done.
if( m_fShellLinkInitialized )
{
hr = S_FALSE;
goto Exit;
}
// Create the ShellLink object.
if( FAILED( hr = GetShellLink() ))
goto Exit;
Assert( m_pShellLink != NULL );
//
// Load ShellLink from Extent list by
// writing the MONIKEREXTENT to an in memory stream and then doing
// IPersistStream::Load.
//
pExtent = m_ExtentList.FindExtent(mnk_ShellLink);
if (pExtent == NULL) // no extent, exit.
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::RestoreShellLink no shell link in extent.\n",
this));
hr = E_FAIL;
goto Exit;
}
if (S_OK != (hr=CreateStreamOnHGlobal(NULL, TRUE, &pstm)))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::RestoreShellLink CreateStreamOnHGlobal failed %08X.\n",
this,
hr));
goto Exit;
}
if (S_OK != (hr=pstm->Write(((char*)pExtent)+MONIKEREXTENT_HEADERSIZE,
pExtent->cbExtentBytes,
NULL)))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::RestoreShellLink pstm->Write failed %08X.\n",
this,
hr));
goto Exit;
}
// Get the Shell Link's IPersistStream interface, and
// load it with the data from the Extent.
Verify(S_OK == m_pShellLink->QueryInterface(IID_IPersistStream,
(void**)&pps));
memset(&li0, 0, sizeof(li0));
Verify(S_OK == pstm->Seek(li0, STREAM_SEEK_SET, &uli));
Assert(uli.LowPart == 0 && uli.HighPart == 0);
if (S_OK != (hr=pps->Load(pstm)))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::RestoreShellLink pps->Load failed %08X.\n",
this,
hr));
goto Exit;
}
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::RestoreShellLink successfully loaded shell link (%08X) from extent.\n",
this,
m_pShellLink));
// ----
// Exit
// ----
Exit:
if( FAILED( hr ))
{
if( m_pShellLink )
{
m_pShellLink->Release();
m_pShellLink = NULL;
}
}
return( hr );
} // RestoreShellLink()
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::SetPathShellLink, private
//
// Synopsis: Set the path in the ShellLink object.
//
// Arguments: [void]
//
// Returns: [HRESULT]
// - S_OK: The path is set successfully.
// - S_FALSE: The path was not set.
//
// Algorithm: If a SetPath isn't necessary/valid, exit (S_OK).
// Get the ShellLink object (create if necessary)
// Perform IShellLink->SetPath()
// If this succeeds, set the m_fShellLinkInitialized
//
// Notes: Setting the path in the ShellLink object causes it to
// read data (attributes) from the file. This data makes that
// file trackable later on if the file is moved. This routine
// can be called any number of times, since it exits early if
// it has executed sucessfully before. Success is indicated by
// the m_fShellLinkInitialized flag.
//
//----------------------------------------------------------------------------
INTERNAL CFileMoniker::SetPathShellLink()
{
HRESULT hr = S_FALSE;
IPersistStream* pps = NULL;
LPCTSTR ptszPath = NULL;
WIN32_FILE_ATTRIBUTE_DATA fadLinkSource;
// ----------
// Initialize
// ----------
// If the path has already been set, or this moniker is not
// performing any tracking, then we needn't do anything.
if( m_fShellLinkInitialized )
{
hr = S_OK;
goto Exit;
}
// If necessary, create the ShellLink object.
if( FAILED( hr = GetShellLink() ))
goto Exit;
Assert( m_pShellLink != NULL );
// ----------------------------------
// Get the correct path into ptszPath
// ----------------------------------
#ifdef _CHICAGO_
char *pszAnsiPath;
USHORT cbAnsiPath;
BOOL fFastConvert;
hr = MnkUnicodeToMulti(m_szPath,
lstrlenW(m_szPath),
pszAnsiPath,
cbAnsiPath,
fFastConvert);
if( FAILED( hr ))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::SetPathShellLink(%ls) -- Could not convert Unicode to Ansi\n",
this,
m_szPath));
goto Exit;
}
ptszPath = pszAnsiPath;
#else // !_CHICAGO_
ptszPath = m_szPath;
#endif // !_CHICAGO_
// ------------------------------
// Set the path of the shell link
// ------------------------------
hr = m_pShellLink->SetPath( (char *) ptszPath );
#ifdef _CHICAGO_
PrivMemFree( (void *) ptszPath);
#endif
// Was the link source missing?
if (S_FALSE == hr)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::SetPathShellLink(%ls) -- readtrackinginfo -- NOT FOUND\n",
this,
m_szPath));
}
else if (SUCCEEDED(hr))
{
// Remember that we've done this so we won't have to again.
m_fShellLinkInitialized = TRUE;
// Set the moniker's dirty bit according to the ShellLink's
// dirty bit. (It should be dirty.)
Verify (S_OK == m_pShellLink->
QueryInterface(IID_IPersistStream, (void**)&pps));
if (pps->IsDirty() == S_OK)
{
m_fDirty = TRUE;
}
else
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::SetPathShellLink(%ls) -- IsDirty not dirty\n",
this,
m_szPath));
}
} // ShellLink->SetPath ... if (SUCCEEDED(hr))
else
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::SetPathShellLink(%ls) -- m_pShellLink->SetPath failed %08X.\n",
this,
m_szPath,
hr));
} // ShellLink->SetPath ... if (SUCCEEDED(hr)) ... else
// ----
// Exit
// ----
Exit:
if( pps )
pps->Release();
return( hr );
} // CFileMoniker::SetPathShellLink()
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::ResolveShellLink, private
//
// Synopsis: Perform an IShellLink->Resolve, and updates the data
// in this moniker accordingly.
//
// Arguments: [IBindCtx*] pbc
// - The caller's bind context.
//
// Outputs: [HRESULT]
// S_OK if the link is successfully resolved.
// S_FALSE if the link is not resolved, but there were no errors.
//
// Algorithm: Get the caller's Bind_Opts
// Get IShellLinkTracker from the ShellLink object.
// Perform IShellLinkTracker->Resolve
// Set the dirty flag if necessary.
// If we found a new path
// ReInitialize this moniker with the new path.
//
// Notes: This routine does not restore the information in
// mnk_TrackingInformation. The information is restored
// Load().
//
//----------------------------------------------------------------------------
INTERNAL CFileMoniker::ResolveShellLink( IBindCtx* pbc )
{
HRESULT hr = E_FAIL;
IPersistStream* pps = NULL;
WCHAR * pwszWidePath = NULL; // Path in Unicode format
char * ptszPath = NULL; // Path in either ANSI or Unicode
USHORT ccNewString = 0;
DWORD dwTrackFlags = 0L;
DWORD dwTickCountDeadline = 0L;
USHORT ccPathBufferSize = 0;
// Validate the inputs
Assert( pbc != NULL );
Assert( m_fIsTracking || m_fTrackingEnabled );
if( !m_fIsTracking )
{
// This is not a tracking moniker, which means that
// we may not yet have a ShellLink object. If so,
// then create one and restore its persistent state
// (which is stored in this Moniker's CExtent).
if( FAILED( hr = RestoreShellLink() ))
goto Exit;
}
Assert( m_pShellLink != NULL );
#ifdef _CAIRO_
//
// Now, we've got a shell link. Let's get its Tracker
// interface.
//
if( FAILED( hr = GetShellLinkTracker() ))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink GetShellLinkTracker failed %08X.\n",
this,
hr));
goto Exit;
}
Assert( m_pShellLinkTracker != NULL );
// Since the Resolve may require tracking, we need to
// determine the tracking restrictions and deadline. These were
// defaulted above, but can be overridden by the caller
// (with values from the bind opts) for tracking monikers.
if( m_fIsTracking )
{
// Check the bind opts for restrictions and the deadline.
BIND_OPTS2 bind_opts;
bind_opts.cbStruct = sizeof( bind_opts );
if( SUCCEEDED( hr = pbc->GetBindOptions( (LPBIND_OPTS) &bind_opts )))
{
if( !ValidateBindOpts( (LPBIND_OPTS) &bind_opts ))
{
hr = E_INVALIDARG;
goto Exit;
}
dwTrackFlags = bind_opts.dwTrackFlags;
dwTickCountDeadline = bind_opts.dwTickCountDeadline;
}
else
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink pbc->GetBindOptions failed %08X.\n",
this,
hr));
goto Exit;
} // if( SUCCEEDED( hr = pbc->GetBindOptions( &bind_opts ))) ... else
} // if( m_fIsTracking )
#endif // _CAIRO_
// Finally, resolve the link.
#ifdef _CAIRO_
if (S_OK != (hr = m_pShellLinkTracker->Resolve(
GetDesktopWindow(),
SLR_ANY_MATCH | SLR_NO_UI,
dwTrackFlags,
dwTickCountDeadline,
0L // Reserved
)
)
)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink IShellLinkTracker->Resolve failed %08X.\n",
this,
hr));
goto Exit;
}
#else
if (S_OK != (hr = m_pShellLink->Resolve(
GetDesktopWindow(),
0xFFFF0000 | SLR_ANY_MATCH | SLR_NO_UI
)
)
)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink IShellLink->Resolve failed %08X.\n",
this,
hr));
goto Exit;
}
#endif // _CAIRO_
//
// The above Resolve may have made the Shell Link object dirty,
// in which case this FileMoniker should be dirty as well.
//
Verify(S_OK == m_pShellLink->QueryInterface(IID_IPersistStream,
(void**)&pps));
if (pps->IsDirty() == S_OK)
{
m_fDirty = TRUE;
}
//
// We appear to have found a matching file. We will
// check that we can activate it properly before updating
// the file moniker's internal path.
// Before we can attempt activation we might have to get the
// path into unicode.
//
#ifdef _CHICAGO_
ccPathBufferSize = MAX_PATH + sizeof( '\0' );
#else
ccPathBufferSize = sizeof( WCHAR ) * MAX_PATH + sizeof( L'\0' );
#endif
ptszPath = (char*)PrivMemAlloc( ccPathBufferSize );
if (ptszPath == NULL)
{
hr = E_OUTOFMEMORY;
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink PrivMemAlloc failed.\n",
this));
goto Exit;
}
WIN32_FIND_DATA fd;
if (S_OK != (hr=m_pShellLink->GetPath(ptszPath,
MAX_PATH, &fd, IsWOWProcess() ? SLGP_SHORTPATH : 0)))
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink m_pShellLink->GetPath failed %08X.\n",
this,
hr));
goto Exit;
}
#ifdef _CHICAGO_
// Convert the path to Unicode.
hr = MnkMultiToUnicode(ptszPath,
pwszWidePath /*OUT*/,
0,
ccNewString, /*OUT*/
CP_OEMCP );
if (hr != S_OK)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink MnkUnicodeToMulti failed %08X.\n",
this,
hr));
goto Exit;
}
#else // !_CHICAGO_
// The path is already in Unicode. Transfer responsibility
// from 'ptszPath' to 'pwszWidePath'.
pwszWidePath = (WCHAR *) ptszPath;
ptszPath = NULL;
ccNewString = (USHORT) -1;
#endif // !_CHICAGO_
// Verify that we received an actual path from IShellLink::GetPath.
if (*pwszWidePath == L'\0' || ccNewString == 0)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink MnkUnicodeToMulti failed 2 %08X.\n",
this,
hr));
hr = E_FAIL;
goto Exit;
}
//
// If the path to the linked file has changed, update the internal
// state of this File Moniker.
//
if( lstrcmpW( pwszWidePath, m_szPath )) // Cmp wide path; Ansi may not exist.
{
// Re-initialize this moniker with the new
// path. We will save and restore the fClassVerified, because
// if it is set we might avoid a redundant verification.
BOOL fClassVerified = m_fClassVerified;
if( !Initialize(m_cAnti,
ptszPath, // Either the ANSI path or NULL
ptszPath ? strlen( ptszPath ) + 1 : 0,
pwszWidePath,
lstrlenW( pwszWidePath ),
m_endServer )
)
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::ResolveShellLink Initialize (with new path) failed.\n",
this));
hr = E_OUTOFMEMORY;
goto Exit;
}
// Restore the previous fClassVerified.
if( !m_fClassVerified )
m_fClassVerified = fClassVerified;
// The paths are now the responsibility of the CFileMoniker.
ptszPath = NULL;
pwszWidePath = NULL;
} // if( !strcmp( pszAnsiPath, m_szPath )
// ----
// Exit
// ----
Exit:
if( pps )
pps->Release();
if (ptszPath)
PrivMemFree(ptszPath);
if (pwszWidePath)
PrivMemFree(pwszWidePath);
return( hr );
} // CFileMoniker::ResolveShellLink()
#ifdef _CAIRO_
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GetTrackFlags, private
//
// Synopsis: Get the TrackFlags from the Shell Link object.
//
// Arguments: [DWORD *] pdwTrackFlags
// - On return holds the Track Flags.
//
// Returns: [HRESULT]
// - E_FAIL is returned if there is no Shell Link object.
//
// Algorithm: Use the Tracker interface to get the Track Flags
//
//----------------------------------------------------------------------------
INTERNAL CFileMoniker::GetTrackFlags( DWORD * pdwTrackFlags )
{
HRESULT hr = E_FAIL;
*pdwTrackFlags = 0L;
if (m_pShellLink)
{
// Get the Shell Link's Tracker interface.
hr = GetShellLinkTracker();
if( FAILED(hr) )
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::GetTrackFlags(%ls) -- Could not get ShellLinkTracker (%08X).\n",
this,
m_szPath,
hr));
}
else
{
Assert( m_pShellLinkTracker != NULL );
// Ask the SLTracker for the Track Flags.
hr = m_pShellLinkTracker->GetTrackFlags( pdwTrackFlags );
if( FAILED(hr) )
{
*pdwTrackFlags = 0L;
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::GetTrackFlags(%ls) -- Could not get TrackFlags %08X.\n",
this,
m_szPath,
hr));
}
} // if( FAILED(hr) )
} // if (m_pShellLink)
// ----
// Exit
// ----
return( hr );
} // CFileMoniker::GetTrackFlags
#endif // _CAIRO_
#ifdef _CAIRO_
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::GetShellLinkTracker, private
//
// Synopsis: Ensure that m_pShellLinkTracker is valid, or return error.
//
// Arguments: [void]
//
// Returns: [HRESULT]
//
// Algorithm: if m_pShellLinkTracker is already valid return S_OK, else
// query the ShellLink object for one.
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL CFileMoniker::GetShellLinkTracker()
{
HRESULT hr;
if (m_pShellLinkTracker == NULL)
{
if( FAILED( hr = GetShellLink()))
goto Exit;
Assert( m_pShellLink != NULL );
hr = m_pShellLink->QueryInterface(IID_IShellLinkTracker,
(void**) &m_pShellLinkTracker );
if( FAILED(hr) )
{
mnkDebugOut((DEB_TRACK,
"CFileMoniker(%x)::GetShellLinkTracker -- Could not QI(IShellLinkTracker) %08X.\n",
this,
hr));
}
}
// ----
// Exit
// ----
Exit:
return( hr );
} // CFileMoniker::GetShellLinkTracker()
#endif // _CAIRO_
#ifdef _CAIRO_
//+---------------------------------------------------------------------------
//
// Method: CFileMoniker::QuickShellLinkResolve, private
//
// Synopsis: Attempt to quickly resolve the ShellLink object.
// This routine never fails.
//
// Arguments: [pbc] A Bind Context.
//
// Returns: [HRESULT]
// S_OK -- The link was resolved.
// S_FALSE -- The link was not resolved.
//
// Algorithm: If this is a tracking moniker, then further restrict the
// bind_opts to track only local/indexed volumes, and then
// attempt a resolve.
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL CFileMoniker::QuickShellLinkResolve( IBindCtx* pbc )
{
HRESULT hr = S_FALSE;
BIND_OPTS2 bind_opts;
BOOL bBindOptsModified = FALSE;
DWORD dwOriginalTrackFlags = 0L;
Assert( pbc != NULL );
// We only attempt the Resolve on tracking monikers.
if( !m_fIsTracking )
{
goto Exit;
}
// Get the caller's bind_opts.
bind_opts.cbStruct = sizeof( bind_opts );
if( FAILED( hr = pbc->GetBindOptions( (LPBIND_OPTS) &bind_opts )))
{
mnkDebugOut(( DEB_ITRACE,
"CFileMoniker::QuickShellLinkResolve(%x) -- could not get caller's bind_opts (%x)\n",
this,
hr ));
goto Exit;
}
// No need to validate this bind_opts since we're not using it yet.
// Further restrict the caller's bind_opts to only track local indexed
// drives.
dwOriginalTrackFlags = bind_opts.dwTrackFlags;
bind_opts.dwTrackFlags |= TRACK_LOCALONLY | TRACK_INDEXEDONLY;
// Set this modified bind_opts back into the caller's bind context, and attempt
// a resolve.
if( FAILED( hr = pbc->SetBindOptions( (LPBIND_OPTS) &bind_opts )))
{
mnkDebugOut(( DEB_ITRACE,
"CFileMoniker::QuickShellLinkResolve(%x) -- could not set bind options (%x)\n",
this,
hr ));
goto Exit;
}
bBindOptsModified = TRUE;
if( FAILED( hr = ResolveShellLink( pbc )))
{
mnkDebugOut(( DEB_ITRACE,
"CFileMoniker::QuickShellLinkResolve(%x) -- could not resolve shell link (%x)\n",
this,
hr ));
goto Exit;
}
// ----
// Exit
// ----
Exit:
// Restore the caller's bind_opts
if( bBindOptsModified )
{
bind_opts.dwTrackFlags = dwOriginalTrackFlags;
if( FAILED( hr = pbc->SetBindOptions( (LPBIND_OPTS) &bind_opts )))
{
mnkDebugOut(( DEB_ITRACE,
"CFileMoniker::QuickShellLinkResolve(%x) -- could not restore caller's bind options (%x)\n",
this,
hr ));
}
}
// Standardize hresult to either S_OK or S_FALSE.
if( hr != S_OK )
{
hr = S_FALSE;
}
return( hr );
} // CFileMoniker::QuickShellLinkResolve()
#endif // _CAIRO_