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

2056 lines
44 KiB
C++

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1993 - 1993.
//
// File: citemmon.cxx
//
// Contents: Implementation of CItemMoniker
//
// Classes:
//
// Functions:
//
// History: 12-27-93 ErikGav Created
// 01-14-94 KevinRo Updated so it actually works
// 06-14-94 Rickhi Fix type casting
// 10-13-95 stevebl threadsafty
//
//----------------------------------------------------------------------------
#include <ole2int.h>
#include "cbasemon.hxx"
#include "citemmon.hxx"
#include "cantimon.hxx"
#include "mnk.h"
#include <olepfn.hxx>
#include <rotdata.hxx>
INTERNAL RegisterContainerBound(LPBC pbc, LPOLEITEMCONTAINER pOleCont);
INTERNAL_(CItemMoniker *) IsItemMoniker( LPMONIKER pmk )
{
CItemMoniker *pIMk;
if ((pmk->QueryInterface(CLSID_ItemMoniker, (void **)&pIMk)) == S_OK)
{
// we release the AddRef done by QI, but still return the pointer
pIMk->Release();
return pIMk;
}
// dont rely on user implementations to set pIMk to NULL on failed QI.
return pIMk;
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::CItemMoniker
//
// Synopsis: Constructor
//
// Effects:
//
// Arguments: (none)
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
CItemMoniker::CItemMoniker() CONSTR_DEBUG
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::CItemMoniker(%x)\n",
this));
m_lpszItem = NULL;
m_lpszDelimiter = NULL;
m_pszAnsiItem = NULL;
m_pszAnsiDelimiter = NULL;
m_fHashValueValid = FALSE;
m_ccItem = 0;
m_cbAnsiItem = 0;
m_cbAnsiDelimiter = 0;
m_ccDelimiter = 0;
m_dwHashValue = 0x12345678;
//
// CoQueryReleaseObject needs to have the address of the this objects
// query interface routine.
//
if (adwQueryInterfaceTable[QI_TABLE_CItemMoniker] == 0)
{
adwQueryInterfaceTable[QI_TABLE_CItemMoniker] =
**(DWORD **)((IMoniker *)this);
}
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::ValidateMoniker
//
// Synopsis: Check the consistency of this moniker
//
// Effects: In a DBG build, check to see if the member variables are
// sane values.
//
// Arguments: (none)
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
#if DBG == 1
void CItemMoniker::ValidateMoniker()
{
Assert( (m_lpszItem == NULL && m_ccItem == 0) ||
(m_ccItem == lstrlenW(m_lpszItem)));
Assert( (m_lpszDelimiter == NULL && m_ccDelimiter == 0) ||
(m_ccDelimiter == lstrlenW(m_lpszDelimiter)));
//
// cbAnsi* fields are NOT string lengths. However, the size of the
// buffer should be at least equal or bigger to the length of the
// Ansi part.
//
Assert( (m_pszAnsiItem == NULL && m_cbAnsiItem == 0) ||
(m_cbAnsiItem >= strlen(m_pszAnsiItem)+1));
Assert( (m_pszAnsiDelimiter == NULL && m_cbAnsiDelimiter == 0) ||
(m_cbAnsiDelimiter >= strlen(m_pszAnsiDelimiter)+1));
Assert( !m_fHashValueValid || (m_dwHashValue != 0x12345678) );
}
#endif
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::~CItemMoniker
//
// Synopsis:
//
// Effects:
//
// Arguments: [void] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
CItemMoniker::~CItemMoniker( void )
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::~CItemMoniker(%x)\n",
this));
UnInit();
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::UnInit
//
// Synopsis: Uninitialize the Item moniker
//
// Effects: Free's path memory stored in Item Moniker.
//
// Arguments: (none)
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
void
CItemMoniker::UnInit()
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::UnInit(%x)\n",
this));
ValidateMoniker();
if (m_lpszDelimiter != NULL)
{
PrivMemFree(m_lpszDelimiter);
m_lpszDelimiter = NULL;
m_ccDelimiter = 0;
}
if (m_pszAnsiDelimiter != NULL)
{
PrivMemFree(m_pszAnsiDelimiter);
m_pszAnsiDelimiter = NULL;
m_cbAnsiDelimiter = 0;
}
if (m_lpszItem != NULL)
{
PrivMemFree(m_lpszItem);
m_lpszItem = NULL;
m_ccItem = 0;
}
if (m_pszAnsiItem != NULL)
{
PrivMemFree(m_pszAnsiItem);
m_pszAnsiItem = NULL;
m_cbAnsiItem = 0;
}
m_fHashValueValid = FALSE;
m_dwHashValue = 0x12345678;
ValidateMoniker();
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::Initialize
//
// Synopsis: Initilaize an Item Moniker
//
// Effects: Clears the current state, then sets new state
//
// Arguments: [lpwcsDelimiter] -- Delimiter string
// [ccDelimiter] -- char count of delimiter
// [lpszAnsiDelimiter] -- Ansi version of delimiter
// [cbAnsiDelimiter] -- Count of bytes in AnsiDelimiter
// [lpwcsItem] -- Item string
// [ccItem] -- Count of characters in item string
// [lpszAnsiItem] -- Ansi version of item string
// [cbAnsiItem] -- Count of bytes in Ansi version
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
void
CItemMoniker::Initialize ( LPWSTR lpwcsDelimiter,
USHORT ccDelimiter,
LPSTR lpszAnsiDelimiter,
USHORT cbAnsiDelimiter,
LPWSTR lpwcsItem,
USHORT ccItem,
LPSTR lpszAnsiItem,
USHORT cbAnsiItem )
{
//
// OleLoadFromStream causes two inits; the member vars may already be set
// UnInit() will free existing resources
//
UnInit();
ValidateMoniker();
m_lpszItem = lpwcsItem;
m_ccItem = ccItem;
m_pszAnsiItem = lpszAnsiItem;
m_cbAnsiItem = cbAnsiItem;
m_lpszDelimiter = lpwcsDelimiter;
m_ccDelimiter = ccDelimiter;
m_pszAnsiDelimiter = lpszAnsiDelimiter;
m_cbAnsiDelimiter = cbAnsiDelimiter;
ValidateMoniker();
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::Initialize
//
// Synopsis: Initialize the contents of this moniker
//
// Effects:
// Copies the input parameters using PrivMemAlloc(), then passes them
// to the other version of Initialize, which takes control of the
// pointers.
//
// Arguments: [lpszDelimiter] --
// [lpszItemName] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
INTERNAL_(BOOL)
CItemMoniker::Initialize ( LPCWSTR lpszDelimiter,
LPCWSTR lpszItemName )
{
ValidateMoniker();
USHORT ccItem;
USHORT ccDelimiter;
LPWSTR pwcsDelimiter = NULL;
LPWSTR pwcsItem = NULL;
//VDATEPTRIN rejects NULL
if( lpszDelimiter )
{
GEN_VDATEPTRIN(lpszDelimiter,WCHAR, FALSE);
}
if( lpszItemName )
{
GEN_VDATEPTRIN(lpszItemName,WCHAR, FALSE);
}
if (FAILED(DupWCHARString(lpszDelimiter,
pwcsDelimiter,
ccDelimiter)))
{
goto errRet;
}
if (FAILED(DupWCHARString(lpszItemName,pwcsItem,ccItem)))
{
goto errRet;
}
Initialize(pwcsDelimiter,
ccDelimiter,
NULL,
0,
pwcsItem,
ccItem,
NULL,
0);
return TRUE;
errRet:
if (pwcsDelimiter != NULL)
{
PrivMemFree(pwcsDelimiter);
}
return(FALSE);
}
CItemMoniker FAR *CItemMoniker::Create (
LPCWSTR lpszDelimiter, LPCWSTR lpszItemName)
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::Create() item(%ws) delim(%ws)\n",
lpszItemName,
lpszDelimiter));
//
// Parameter validation is handled in Initialize
//
CItemMoniker FAR * pCIM = new CItemMoniker();
if (pCIM)
{
pCIM->AddRef();
if (pCIM->Initialize( lpszDelimiter, lpszItemName ))
return pCIM;
delete pCIM;
}
return NULL;
}
STDMETHODIMP CItemMoniker::QueryInterface (THIS_ REFIID riid,
LPVOID FAR* ppvObj)
{
VDATEIID (riid);
VDATEPTROUT(ppvObj, LPVOID);
#ifdef _DEBUG
if (riid == IID_IDebug)
{
*ppvObj = &(m_Debug);
return NOERROR;
}
#endif
if (IsEqualIID(riid, CLSID_ItemMoniker))
{
// called by IsItemMoniker.
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) CItemMoniker::Release (void)
{
mnkDebugOut((DEB_TRACE, "%p CItemMoniker::Release(%ld)\n",
this, m_refs - 1));
Assert(m_refs != 0);
if (InterlockedDecrement((long *)&m_refs) == 0)
{
delete this;
return 0;
}
return m_refs;
}
STDMETHODIMP CItemMoniker::GetClassID (LPCLSID lpClassId)
{
VDATEPTROUT(lpClassId, CLSID);
*lpClassId = CLSID_ItemMoniker;
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Function: WriteDoubleString
//
// Synopsis: Writes a double string to stream. See ExtractUnicodeString
//
// Effects:
//
// Arguments: [pStm] --
// [pwcsWide] --
// [ccWide] --
// [pszAnsi] --
// [cbAnsi] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT
WriteDoubleString( LPSTREAM pStm,
LPWSTR pwcsWide,
USHORT ccWide,
LPSTR pszAnsi,
USHORT cbAnsi)
{
mnkDebugOut((DEB_ITRACE,
"WriteDoubleString pwcsWide(%ws) cbWide(0x%x) psz(%s) cb(0x%x)\n",
pwcsWide?pwcsWide:L"<NULL>",
ccWide,
pszAnsi?pszAnsi:"<NULL>",
cbAnsi));
HRESULT hr;
//
// The string size is always written, but not including the size of the
// preceding DWORD so we conform to the way WriteAnsiString does it
//
ULONG ulTotalSize = 0;
//
// The entire reason we are supposed to be in this routine is that the
// pwcsWide could not be converted to ANSI. Therefore, it had better
// be valid.
//
Assert( (pwcsWide != NULL) && (ccWide == lstrlenW(pwcsWide)));
Assert( (pszAnsi == NULL) || (cbAnsi == (strlen(pszAnsi) + 1)));
ulTotalSize += ccWide * sizeof(WCHAR);
// Lets assume most ItemStrings will fit in this buffer
BYTE achQuickBuffer[256];
BYTE *pcbQuickBuffer = achQuickBuffer;
//
// Since we are going to cheat, and write something to the back of the
// ANSI string, the ANSI string must contain at least a NULL character.
// If it doesn't, we are going to cheat one in.
//
if (pszAnsi == NULL)
{
ulTotalSize += sizeof(char);
}
else
{
ulTotalSize += cbAnsi;
}
//
// If we don't fit in the QuickBuffer, allocate some memory
//
if (ulTotalSize > sizeof(achQuickBuffer))
{
pcbQuickBuffer = (BYTE *)PrivMemAlloc(ulTotalSize);
if (pcbQuickBuffer == NULL)
{
return(E_OUTOFMEMORY);
}
}
//
// First DWORD in the buffer is the total size of the string. This
// value includes strlen's of both strings, plus the size of the NULL
// on the Ansi string.
//
// Intrinsics will make this into a move of the correct alignment.
// Casting pcbQuickBuffer to a ULONG pointer is dangerous, since
// the alignment may be incorrect. Let the compiler figure out the
// correct thing to do.
//
memcpy(pcbQuickBuffer,&ulTotalSize,sizeof(ulTotalSize));
//
// Here, we make sure that pszAnsi ends up writing at least the NULL
// character
//
ULONG ulAnsiWritten;
memcpy(pcbQuickBuffer + sizeof(ulTotalSize),
pszAnsi?pszAnsi:"",
ulAnsiWritten = pszAnsi?cbAnsi:1);
//
// At this point, there should be a ULONG followed by at least 1
// character. The pointer arithmetic below puts us just past the
// null terminator of the Ansi string
//
memcpy(pcbQuickBuffer + sizeof(ulTotalSize) + ulAnsiWritten,
pwcsWide,
ccWide * sizeof(WCHAR));
mnkDebugOut((DEB_ITRACE,
"WriteDoubleString ulTotalSize(0x%x)\n",
ulTotalSize));
hr = pStm->Write(pcbQuickBuffer, ulTotalSize + sizeof(ULONG) ,NULL);
if (pcbQuickBuffer != achQuickBuffer)
{
PrivMemFree(pcbQuickBuffer);
}
return(hr);
}
//+---------------------------------------------------------------------------
//
// Function: ExtractUnicodeString
//
// Synopsis: Given an ANSI string buffer, return a UNICODE path
//
// Effects:
// If it exists, this routine will extract the UNICODE section
// of a string written out in the following format:
//
// <ANSI string><0><UNICODESTRING>
// ^ cbAnsiString ^
//
//
// If the UNICODE string doesn't exist, then the Ansi string is converted
// to UNICODE and returned
//
// Arguments: [pszAnsiString] -- Ansi string with potential UNICODE end
// [cbAnsiString] -- Total number of bytes in pszAnsiString
// [pwcsWideString] -- Reference to output string pointer
// [ccWideString] -- Reference to output cound of characters
//
// Requires:
//
// Returns:
// pwcsWideString will be a PrivMemAlloc()'d UNICODE string
// ccWideString will be the character count (excluding the NULL)
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT ExtractUnicodeString(LPSTR pszAnsiString,
USHORT cbAnsiString,
LPWSTR & pwcsString,
USHORT & ccString)
{
mnkDebugOut((DEB_ITRACE,
"ExtractUnicodeString pszAnsi(%s) cbAnsi(0x%x)\n",
ANSICHECK(pszAnsiString),
cbAnsiString));
USHORT cbAnsiStrLen;
HRESULT hr;
//
// If the Ansi string is NULL, then the Wide char will be also
//
if (pszAnsiString == NULL)
{
pwcsString = NULL;
ccString = 0;
return(NOERROR);
}
Assert( pwcsString == NULL);
//
// If strlen(pszAnsiString)+1 == cbAnsiString, then there is no
// UNICODE extent.
//
cbAnsiStrLen = strlen(pszAnsiString);
if ((cbAnsiStrLen + 1) == cbAnsiString)
{
//
// There is not a UNICODE extent. Convert from Ansi to UNICODE
//
pwcsString = NULL;
hr= MnkMultiToUnicode(pszAnsiString,
pwcsString,
0,
ccString,
CP_ACP);
mnkDebugOut((DEB_ITRACE,
"ExtractUnicodeString converted (%s) to (%ws) ccString(0x%x)\n",
ANSICHECK(pszAnsiString),
WIDECHECK(pwcsString),
ccString));
}
else
{
mnkDebugOut((DEB_ITRACE,
"ExtractUnicodeString found UNICODE extent\n"));
//
// There are extra characters following the AnsiString. Make a
// new buffer to copy them. Don't forget to add an extra WCHAR for
// the NULL termination
//
// AnsiStrLen + AnsiNull char
USHORT cbWideString = cbAnsiString - (cbAnsiStrLen + 1);
Assert(cbWideString != 0);
//
// There had best be an even number of bytes, or else something
// has gone wrong.
//
Assert( ! (cbWideString & 1));
// Strlen + sizeof(1 NULL char)
pwcsString = (WCHAR *) PrivMemAlloc(cbWideString + sizeof(WCHAR));
if (pwcsString == NULL)
{
hr = E_OUTOFMEMORY;
goto errRet;
}
memcpy(pwcsString,pszAnsiString + cbAnsiStrLen + 1, cbWideString);
ccString = cbWideString / sizeof(WCHAR);
pwcsString[ccString] = 0;
hr = NOERROR;
}
errRet:
mnkDebugOut((DEB_ITRACE,
"ExtractUnicodeString result hr(%x) pwcsString(%ws) ccString(0x%x)\n",
hr,
WIDECHECK(pwcsString),
ccString));
return(hr);
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::Load
//
// Synopsis: Loads an Item moniker from a stream
//
// Effects: The first version of CItemMoniker stored two counted strings
// in the stream. Both were stored in ANSI.
//
// This version saves a UNICODE string on the end of the ANSI
// string. Therefore, when it is read back in, the count of
// bytes read as the ANSI string may include a UNICODE string.
//
// See ::Save() for more details.
//
//
// Arguments: [pStm] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CItemMoniker::Load (LPSTREAM pStm)
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::Load(%x)\n",
this));
VDATEIFACE(pStm);
ValidateMoniker();
HRESULT hresult;
LPSTR pszAnsiItem = NULL;
LPSTR pszAnsiDelim = NULL;
LPWSTR pwcsItem = NULL;
LPWSTR pwcsDelim = NULL;
USHORT cbAnsiDelim;
USHORT cbAnsiItem;
USHORT ccDelim;
USHORT ccItem;
//
// First, read in the two strings into the Ansi versions
//
hresult = ReadAnsiStringStream( pStm, pszAnsiDelim,cbAnsiDelim );
if (hresult != NOERROR)
{
goto errRet;
}
mnkDebugOut((DEB_ITRACE,
"::Load(%x) pszAnsiDelim(%s) cbAnsiDelim(0x%x)\n",
this,
pszAnsiDelim,
cbAnsiDelim));
hresult = ReadAnsiStringStream( pStm, pszAnsiItem, cbAnsiItem );
if (hresult != NOERROR)
{
goto errRet;
}
mnkDebugOut((DEB_ITRACE,
"::Load(%x) pszAnsiItem(%s) cbAnsiItem(0x%x)\n",
this,
pszAnsiItem,
cbAnsiItem));
//
// Now, determine the UNICODE strings. They may be stashed at the
// end of the Ansi strings.
//
hresult = ExtractUnicodeString(pszAnsiDelim,
cbAnsiDelim,
pwcsDelim,
ccDelim);
if (FAILED(hresult))
{
goto errRet;
}
hresult = ExtractUnicodeString(pszAnsiItem,
cbAnsiItem,
pwcsItem,
ccItem);
if (FAILED(hresult))
{
goto errRet;
}
Initialize ( pwcsDelim,
ccDelim,
pszAnsiDelim,
cbAnsiDelim,
pwcsItem,
ccItem,
pszAnsiItem,
cbAnsiItem );
ValidateMoniker();
return(NOERROR);
errRet:
PrivMemFree(pszAnsiItem);
PrivMemFree(pszAnsiDelim);
PrivMemFree(pwcsItem);
PrivMemFree(pwcsDelim);
return hresult;
}
//+---------------------------------------------------------------------------
//
// Function: SaveUnicodeAsAnsi
//
// Synopsis: This function will save a string to the stream
//
// Effects:
// This routine always attempts to save the string in Ansi. If it isn't
// possible to save an Ansi version of the string, it will save an
// Ansi version (which may be NULL), and a UNICODE version.
//
// Arguments: [pStm] -- Stream to write to
// [pwcsWide] -- Unicode string
// [ccWide] -- Count of UNICODE characters (EXCL NULL)
// [pszAnsi] -- Ansi string
// [cbAnsi] -- Count of bytes (INCL NULL)
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
HRESULT SaveUnicodeAsAnsi( LPSTREAM pStm,
LPWSTR pwcsWide,
USHORT ccWide,
LPSTR pszAnsi,
USHORT cbAnsi)
{
mnkDebugOut((DEB_ITRACE,
"::SaveUnicodeAsAnsi pwcsWide(%ws) ccWide(0x%x) pwsAnsi(%s) cbAnsi(0x%x)\n",
WIDECHECK(pwcsWide),
ccWide,
ANSICHECK(pszAnsi),
cbAnsi));
HRESULT hr;
BOOL fFastConvert;
//
// If the Ansi version exists, or the Unicode string is NULL,
// write out the Ansi version
//
if ((pszAnsi != NULL) || (pwcsWide == NULL))
{
mnkDebugOut((DEB_ITRACE,
"::SaveUnicodeAsAnsi Ansi Only (%s)\n",
ANSICHECK(pszAnsi)));
hr = WriteAnsiStringStream( pStm,
pszAnsi,
cbAnsi);
}
else
{
//
// There isn't an AnsiVersion, and the UNICODE version isn't NULL
// Try to convert the UNICODE to Ansi
//
Assert( (pwcsWide != NULL) &&
(ccWide == lstrlenW(pwcsWide)));
mnkDebugOut((DEB_ITRACE,
"::SaveUnicodeAsAnsi Unicode string exists(%ws)\n",
WIDECHECK(pwcsWide)));
//
// We can use the pszAnsi pointer since it is NULL
//
Assert( pszAnsi == NULL);
hr = MnkUnicodeToMulti( pwcsWide,
ccWide,
pszAnsi,
cbAnsi,
fFastConvert);
Assert( (pszAnsi == NULL) || (cbAnsi == strlen(pszAnsi)+1) );
//
// A failure would mean out of memory, or some other terrible thing
// happened.
//
if (FAILED(hr))
{
goto errRet;
}
//
// If fFastConvert, then the UnicodeString was converted using a
// truncation algorithm, and the resulting Ansi string can be saved
// without the Unicode section appended
//
if (fFastConvert)
{
mnkDebugOut((DEB_ITRACE,
"::SaveUnicodeAsAnsi Fast converted wide(%ws) to (%s) cbAnsi(0x%x)\n",
WIDECHECK(pwcsWide),
ANSICHECK(pszAnsi),
cbAnsi));
hr = WriteAnsiStringStream( pStm,
pszAnsi,
cbAnsi);
}
else
{
mnkDebugOut((DEB_ITRACE,
"::SaveUnicodeAsAnsi Full conversion wide(%ws) to (%s) cbAnsi(0x%x)\n",
WIDECHECK(pwcsWide),
ANSICHECK(pszAnsi),
cbAnsi));
hr = WriteDoubleString( pStm,
pwcsWide,
ccWide,
pszAnsi,
cbAnsi);
}
//
// We are done with the Ansi string.
//
// BUGBUG: (KevinRo) It would be nice if we could get the double
// string back from WriteDoubleString and cache it. Perhaps later
// this can be done.
//
PrivMemFree(pszAnsi);
}
errRet:
return(hr);
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::Save
//
// Synopsis: Save the moniker to a stream
//
// Effects: The first version of CItemMoniker stored two counted strings
// in the stream. Both were stored in ANSI.
//
// This version saves a UNICODE string on the end of the ANSI
// string. Therefore, when it is read back in, the count of
// bytes read as the ANSI string may include a UNICODE string.
//
// We don't actually write the UNICODE string unless we have
// to.
//
// Arguments: [pStm] --
// [fClearDirty] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-16-94 kevinro Created
//
// Notes:
//
// There are two ways to create a CItemMoniker. Using CreateItemMoniker(),
// and Load().
//
// If we were Load()'d, then it will be the case that we already have an
// Ansi version of both strings. In this case, we will just save those
// Ansi strings back out. If they already contain the UNICODE sections,
// then they are saved as part of the package. Hopefully, this will be
// the common case.
//
// Another possibility is the moniker was created using CreateItemMoniker.
// If this is the case, then there are no Ansi versions of the strings.
// We need to create Ansi strings, if possible. If we can't convert the
// string to Ansi cleanly, then we need to save away a UNICODE section
// of the string.
//
//----------------------------------------------------------------------------
STDMETHODIMP CItemMoniker::Save (LPSTREAM pStm, BOOL fClearDirty)
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::Save(%x)\n",
this));
ValidateMoniker();
VDATEIFACE(pStm);
UNREFERENCED(fClearDirty);
HRESULT hr;
//
// If a AnsiDelimiter exists, OR the UNICODE version is NULL, then
// write out the Ansi version
//
hr = SaveUnicodeAsAnsi( pStm,
m_lpszDelimiter,
m_ccDelimiter,
m_pszAnsiDelimiter,
m_cbAnsiDelimiter);
if (FAILED(hr))
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::Save(%x) SaveUnicodeAsAnsi Delim failed (%x)\n",
this,
hr));
goto errRet;
}
hr = SaveUnicodeAsAnsi( pStm,
m_lpszItem,
m_ccItem,
m_pszAnsiItem,
m_cbAnsiItem);
if (FAILED(hr))
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::Save(%x) SaveUnicodeAsAnsi Item failed (%x)\n",
this,
hr));
goto errRet;
}
errRet:
return hr;
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::GetSizeMax
//
// Synopsis: Get the maximum size required to serialize this moniker
//
// Effects:
//
// Arguments: [pcbSize] -- Place to return value
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CItemMoniker::GetSizeMax (ULARGE_INTEGER FAR* pcbSize)
{
ValidateMoniker();
VDATEPTROUT(pcbSize, ULARGE_INTEGER);
UINT cb;
//
// BUGBUG: (KevinRo) Revisit this calculation. Is there a better
// way of determining the size?
//
// The largest a UNICODE to MBSTRING should end up being is
// 2 * character count.
//
if (m_lpszItem)
{
cb = (m_ccItem + 1) * 2 * sizeof(WCHAR);
}
if (m_lpszDelimiter)
{
cb += (m_ccDelimiter + 1) * 2 * sizeof(WCHAR);
}
//
// sizeof(CLSID) accounts for the GUID that OleSaveToStream might
// write on our behalf. The two ULONGs are the string lengths,
// and cb is the max string sizes.
//
ULISet32(*pcbSize, sizeof(CLSID) + 2*(1 + sizeof(ULONG)) + cb);
return(NOERROR);
}
#define dwModerateTime 2500
// 2.5 seconds divides immediate from moderate.
DWORD BindSpeedFromBindCtx( LPBC pbc )
{
BIND_OPTS bindopts;
HRESULT hresult;
DWORD dwBindSpeed = BINDSPEED_INDEFINITE;
bindopts.cbStruct = sizeof(bindopts);
hresult = pbc->GetBindOptions( &bindopts );
if (hresult != NOERROR) return dwBindSpeed;
Assert( bindopts.cbStruct >= 16);
if (bindopts.dwTickCountDeadline != 0)
{
if (bindopts.dwTickCountDeadline < dwModerateTime)
dwBindSpeed = BINDSPEED_IMMEDIATE;
else dwBindSpeed = BINDSPEED_MODERATE;
}
// else speed = default, BINDSPEED_INDEFINITE
return dwBindSpeed;
}
STDMETHODIMP CItemMoniker::BindToObject ( LPBC pbc,
LPMONIKER pmkToLeft, REFIID iidResult,
VOID FAR * FAR * ppvResult)
{
M_PROLOG(this);
VDATEPTROUT(ppvResult, LPVOID);
*ppvResult = NULL;
VDATEIFACE(pbc);
if (pmkToLeft) VDATEIFACE(pmkToLeft);
VDATEIID(iidResult);
ValidateMoniker();
HRESULT hresult;
LPOLEITEMCONTAINER pOleCont;
if (pmkToLeft)
{
hresult = pmkToLeft->BindToObject( pbc, NULL, IID_IOleItemContainer,
(LPVOID FAR*)&pOleCont);
// AssertOutPtrIface(hresult, pOleCont);
if (hresult != NOERROR) return hresult;
hresult = RegisterContainerBound(pbc, pOleCont);
hresult = pOleCont->GetObject(m_lpszItem, BindSpeedFromBindCtx(pbc),
pbc, iidResult, ppvResult);
// AssertOutPtrIface(hresult, *ppvResult);
pOleCont->Release();
return hresult;
}
return ResultFromScode(E_INVALIDARG); // needs non-null moniker to left.
}
STDMETHODIMP CItemMoniker::BindToStorage (LPBC pbc, LPMONIKER
pmkToLeft, REFIID riid, LPVOID FAR* ppvObj)
{
M_PROLOG(this);
VDATEPTROUT(ppvObj,LPVOID);
*ppvObj = NULL;
VDATEIFACE(pbc);
if (pmkToLeft) VDATEIFACE(pmkToLeft);
VDATEIID(riid);
HRESULT hresult;
LPOLEITEMCONTAINER pOleCont;
ValidateMoniker();
if (pmkToLeft)
{
hresult = pmkToLeft->BindToObject( pbc, NULL, IID_IOleItemContainer,
(LPVOID FAR*)&pOleCont);
// AssertOutPtrIface(hresult, pOleCont);
if (hresult != NOERROR) return hresult;
hresult = RegisterContainerBound(pbc, pOleCont);
hresult = pOleCont->GetObjectStorage(m_lpszItem, pbc,
riid, ppvObj);
// AssertOutPtrIface(hresult, *ppvObj);
pOleCont->Release();
return hresult;
}
return ResultFromScode(E_INVALIDARG); // needs non-null moniker to left.
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::ComposeWith
//
// Synopsis: Compose this moniker with another moniker
//
// Effects:
//
// Arguments: [pmkRight] --
// [fOnlyIfNotGeneric] --
// [ppmkComposite] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 2-04-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CItemMoniker::ComposeWith ( LPMONIKER pmkRight,
BOOL fOnlyIfNotGeneric, LPMONIKER FAR* ppmkComposite)
{
VDATEPTROUT(ppmkComposite,LPMONIKER);
*ppmkComposite = NULL;
VDATEIFACE(pmkRight);
HRESULT hresult = NOERROR;
ValidateMoniker();
//
// If this is an AntiMoniker, then we are going to ask the AntiMoniker
// for the composite. This is a backward compatibility problem. Check
// out the CAntiMoniker::EatOne() routine for details.
//
CAntiMoniker *pCAM = IsAntiMoniker(pmkRight);
if (pCAM)
{
pCAM->EatOne(ppmkComposite);
}
else
{
if (!fOnlyIfNotGeneric)
{
hresult = CreateGenericComposite( this, pmkRight, ppmkComposite );
}
else
{
hresult = ResultFromScode(MK_E_NEEDGENERIC);
*ppmkComposite = NULL;
}
}
return hresult;
}
STDMETHODIMP CItemMoniker::Enum (THIS_ BOOL fForward, LPENUMMONIKER FAR* ppenumMoniker)
{
M_PROLOG(this);
VDATEPTROUT(ppenumMoniker,LPENUMMONIKER);
*ppenumMoniker = NULL;
noError;
}
STDMETHODIMP CItemMoniker::IsEqual (THIS_ LPMONIKER pmkOtherMoniker)
{
M_PROLOG(this);
VDATEIFACE(pmkOtherMoniker);
ValidateMoniker();
CItemMoniker FAR* pCIM = IsItemMoniker(pmkOtherMoniker);
if (!pCIM)
return ResultFromScode(S_FALSE);
// the other moniker is a item moniker.
// for the names, we do a case-insensitive compare.
if (m_lpszItem && pCIM->m_lpszItem)
{
if (0 == lstrcmpiW(pCIM->m_lpszItem, m_lpszItem))
return NOERROR; // S_TRUE;
}
else
return (m_lpszItem || pCIM->m_lpszItem ? ResultFromScode(S_FALSE ) : NOERROR /*S_TRUE*/);
return ResultFromScode(S_FALSE);
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::Hash
//
// Synopsis: Compute a hash value
//
// Effects: Computes a hash using the same basic algorithm as the
// file moniker. If possible, use the same routine as the
// file monikers. However, if the string is longer than
// MAX_PATH, calc it on our own.
//
// Arguments: [pdwHash] --
//
// Requires:
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 1-17-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CItemMoniker::Hash (THIS_ LPDWORD pdwHash)
{
CLock lck(m_mxs); // protect m_fHashValueValid and m_dwHashValue
VDATEPTROUT(pdwHash, DWORD);
DWORD dwTemp = 0;
LPWSTR lp;
WCHAR ch;
ValidateMoniker();
if (m_fHashValueValid == FALSE)
{
//
// The CalcFileMonikerHash function does a STRUPR before running
// the length of the string. In UNICODE, that is faster.
// It uses a MAX_PATH buffer to do this. If the length is
// greater than MAX_PATH, then calculate it the old fashioned way.
//
if (m_ccItem < MAX_PATH)
{
m_dwHashValue = CalcFileMonikerHash(m_lpszItem);
}
else
{
lp = m_lpszItem;
if (lp != NULL)
{
while (*lp != 0)
{
dwTemp *= 3;
ch = (WCHAR)CharUpperW((LPWSTR)*lp);
dwTemp ^= ch;
lp++;
}
}
m_dwHashValue = dwTemp;
}
m_fHashValueValid = TRUE;
}
*pdwHash = m_dwHashValue;
return(NOERROR);
}
STDMETHODIMP CItemMoniker::IsRunning (THIS_ LPBC pbc, LPMONIKER pmkToLeft,
LPMONIKER pmkNewlyRunning)
{
VDATEIFACE (pbc);
HRESULT hresult;
if (pmkToLeft)
VDATEIFACE (pmkToLeft);
if (pmkNewlyRunning)
VDATEIFACE (pmkNewlyRunning);
LPMONIKER pmkWild = NULL;
LPMONIKER pmk = NULL;
LPOLEITEMCONTAINER pCont = NULL;
LPRUNNINGOBJECTTABLE prot = NULL;
hresult = CreateItemMoniker(L"\\", NULL, &pmkWild);
if (hresult != NOERROR) goto errRet;
if (pmkToLeft == NULL)
{
if (pmkNewlyRunning != NULL)
{
hresult = IsEqual(pmkNewlyRunning);
if (hresult == NOERROR) goto errRet;
hresult = IsEqual(pmkNewlyRunning);
if (hresult != NOERROR)
{
hresult = ResultFromScode(S_FALSE);
goto errRet;
}
}
pbc->GetRunningObjectTable( &prot );
// check to see if "this" is in ROT
hresult = prot->IsRunning(this);
goto errRet;
}
hresult = pmkToLeft->IsRunning(pbc, NULL, NULL);
if (hresult == NOERROR)
{
hresult = pmkToLeft->BindToObject(pbc, NULL, IID_IOleItemContainer,
(LPVOID FAR *)&pCont );
// AssertOutPtrIface(hresult, pCont);
if (hresult == NOERROR)
{
// don't use RegisterContainerBound(pbc, pCont) here since we
// will lock/unlock the container unecessarily and possibly
// shut it down.
hresult = pbc->RegisterObjectBound(pCont);
if (hresult != NOERROR) goto errRet;
hresult = pCont->IsRunning(m_lpszItem);
}
}
errRet:
if (pmkWild) pmkWild->Release();
if (pCont) pCont->Release();
if (prot) prot->Release();
return hresult;
}
STDMETHODIMP CItemMoniker::GetTimeOfLastChange (THIS_ LPBC pbc, LPMONIKER pmkToLeft,
FILETIME FAR* pfiletime)
{
M_PROLOG(this);
VDATEIFACE(pbc);
if (pmkToLeft) VDATEIFACE(pmkToLeft);
VDATEPTROUT(pfiletime, FILETIME);
ValidateMoniker();
HRESULT hresult;
LPMONIKER pmkTemp = NULL;
LPRUNNINGOBJECTTABLE prot = NULL;
if (pmkToLeft == NULL)
return ResultFromScode(MK_E_NOTBINDABLE);
// Getting time of last change
// for an orphan item moniker.
// Check to see if the composite is in the running object table
hresult = CreateGenericComposite( pmkToLeft, this, &pmkTemp );
if (hresult != NOERROR) goto errRet;
hresult = pbc->GetRunningObjectTable(& prot);
if (hresult != NOERROR) goto errRet;
hresult = prot->GetTimeOfLastChange( pmkTemp, pfiletime);
if (hresult != MK_E_UNAVAILABLE) goto errRet;
// if not, pass on to the left.
hresult = pmkToLeft->GetTimeOfLastChange(pbc, NULL, pfiletime);
errRet:
if (pmkTemp) pmkTemp->Release();
if (prot) prot->Release();
return hresult;
}
STDMETHODIMP CItemMoniker::Inverse (THIS_ LPMONIKER FAR* ppmk)
{
M_PROLOG(this);
VDATEPTROUT(ppmk, LPMONIKER);
return CreateAntiMoniker(ppmk);
}
STDMETHODIMP CItemMoniker::CommonPrefixWith (LPMONIKER pmkOther, LPMONIKER FAR*
ppmkPrefix)
{
ValidateMoniker();
VDATEPTROUT(ppmkPrefix,LPMONIKER);
*ppmkPrefix = NULL;
VDATEIFACE(pmkOther);
if (!IsItemMoniker(pmkOther))
{
return(MonikerCommonPrefixWith(this,pmkOther,ppmkPrefix));
}
if (NOERROR == IsEqual(pmkOther))
{
*ppmkPrefix = this;
AddRef();
return ResultFromScode(MK_S_US);
}
return ResultFromScode(MK_E_NOPREFIX);
}
STDMETHODIMP CItemMoniker::RelativePathTo (THIS_ LPMONIKER pmkOther, LPMONIKER FAR*
ppmkRelPath)
{
ValidateMoniker();
VDATEPTROUT(ppmkRelPath,LPMONIKER);
*ppmkRelPath = NULL;
VDATEIFACE(pmkOther);
return ResultFromScode(MK_E_NOTBINDABLE);
}
STDMETHODIMP CItemMoniker::GetDisplayName ( LPBC pbc, LPMONIKER
pmkToLeft, LPWSTR FAR * lplpszDisplayName )
{
mnkDebugOut((DEB_ITRACE,
"CItemMoniker::GetDisplayName(%x)\n",
this));
ValidateMoniker();
VDATEPTROUT(lplpszDisplayName, LPWSTR);
*lplpszDisplayName = NULL;
VDATEIFACE(pbc);
if (pmkToLeft)
{
VDATEIFACE(pmkToLeft);
}
ULONG cc = m_ccItem + m_ccDelimiter;
//
// cc holds the count of characters. Make extra room for the NULL
//
*lplpszDisplayName = (LPWSTR) CoTaskMemAlloc(sizeof(WCHAR)*(1 + cc));
if (*lplpszDisplayName == NULL)
{
mnkDebugOut((DEB_ITRACE,
"::GetDisplayName(%x) returning out of memory\n",
this));
return E_OUTOFMEMORY;
}
//
// To handle the case where both strings are NULL, we set the first
// (and perhaps only) character to 0
//
*lplpszDisplayName[0] = 0;
//
// Concat the two strings. Don't forget the NULL! Thats what the
// extra character is for.
//
if (m_lpszDelimiter != NULL)
{
memcpy( *lplpszDisplayName,
m_lpszDelimiter,
(m_ccDelimiter + 1) * sizeof(WCHAR));
}
//
// The existing string was NULL terminated to just in case the
// Item string is NULL.
//
// Concat the Item string on the end of the Delimiter Again, don't
// forget the NULL.
//
if (m_lpszItem != NULL)
{
memcpy( *lplpszDisplayName + m_ccDelimiter,
m_lpszItem,
(m_ccItem + 1) * sizeof(WCHAR));
}
mnkDebugOut((DEB_ITRACE,
"::GetDisplayName(%x) returning %ws\n",
this,
*lplpszDisplayName));
return(NOERROR);
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::ParseDisplayName
//
// Synopsis:
//
// Effects:
//
// Arguments: [pbc] -- Bind Context
// [pmkToLeft] -- Left moniker
// [lpszDisplayName] -- String to parse
// [pchEaten] -- Output number of characters eaten
// [ppmkOut] -- Output moniker
//
// Requires:
// pmkToLeft MUST be valid. NULL is inappropriate
//
// Returns:
//
// Signals:
//
// Modifies:
//
// Derivation:
//
// Algorithm:
//
// History: 2-03-94 kevinro Created
//
// Notes:
//
//----------------------------------------------------------------------------
STDMETHODIMP CItemMoniker::ParseDisplayName ( LPBC pbc,
LPMONIKER pmkToLeft,
LPWSTR lpszDisplayName,
ULONG FAR* pchEaten,
LPMONIKER FAR* ppmkOut)
{
HRESULT hresult;
VDATEPTRIN(lpszDisplayName, WCHAR);
VDATEPTROUT(pchEaten,ULONG);
VDATEPTROUT(ppmkOut,LPMONIKER);
IParseDisplayName FAR * pPDN = NULL;
IOleItemContainer FAR * pOIC = NULL;
ValidateMoniker();
*pchEaten = 0;
*ppmkOut = NULL;
VDATEIFACE(pbc);
//
// Item monikers require the moniker on the left to be non-null, so
// they can get the container to parse the display name
//
if (pmkToLeft != NULL)
{
VDATEIFACE(pmkToLeft);
}
else
{
hresult = MK_E_SYNTAX;
goto errRet;
}
hresult = pmkToLeft->BindToObject( pbc,
NULL,
IID_IOleItemContainer,
(VOID FAR * FAR *)&pOIC );
if (FAILED(hresult))
{
goto errRet;
}
hresult = RegisterContainerBound(pbc, pOIC);
if (FAILED(hresult))
{
goto errRet;
}
hresult = pOIC->GetObject( m_lpszItem,
BindSpeedFromBindCtx(pbc),
pbc,
IID_IParseDisplayName,
(LPVOID FAR*)&pPDN);
if (FAILED(hresult))
{
goto errRet;
}
hresult = pPDN->ParseDisplayName(pbc,
lpszDisplayName,
pchEaten,
ppmkOut );
if (FAILED(hresult))
{
goto errRet;
}
hresult = pbc->RegisterObjectBound( pPDN );
errRet:
if (pPDN)
{
pPDN->Release();
}
if (pOIC)
{
pOIC->Release();
}
return hresult;
}
STDMETHODIMP CItemMoniker::IsSystemMoniker (THIS_ LPDWORD pdwType)
{
M_PROLOG(this);
VDATEPTROUT(pdwType,DWORD);
*pdwType = MKSYS_ITEMMONIKER;
return NOERROR;
}
//+---------------------------------------------------------------------------
//
// Method: CItemMoniker::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 the ROT data from internal data of the item moniker.
//
// 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 CItemMoniker::GetComparisonData(
byte *pbData,
ULONG cbMax,
DWORD *pcbData)
{
//
// CLSID plus delimiter plus item plus one NULL
//
ULONG ulLength = sizeof(CLSID_ItemMoniker) +
(m_ccItem + m_ccDelimiter + 1) * sizeof(WCHAR);
Assert(pcbData != NULL);
Assert(pbData != NULL);
if (cbMax < ulLength)
{
return(E_OUTOFMEMORY);
}
//
// Copy the classID
//
memcpy(pbData,&CLSID_ItemMoniker,sizeof(CLSID_FileMoniker));
//
// Copy the delimiter WITHOUT the NULL. This saves a little space,
// and allows us to uppercase them both as a single string
//
memcpy(pbData+sizeof(CLSID_FileMoniker),
m_lpszDelimiter,
m_ccDelimiter * sizeof(WCHAR));
//
// Copy the item plus the NULL
//
memcpy(pbData+sizeof(CLSID_FileMoniker)+(m_ccDelimiter * sizeof(WCHAR)),
m_lpszItem,
(m_ccItem + 1)*sizeof(WCHAR));
//
// Insure entire string is upper case, since the item monikers are spec'd
// (and already do) case insensitive comparisions
//
CharUpperW((WCHAR *)(pbData+sizeof(CLSID_FileMoniker)));
*pcbData = ulLength;
return NOERROR;
}
#ifdef _DEBUG
STDMETHODIMP_(void) NC(CItemMoniker,CDebug)::Dump ( IDebugStream FAR * pdbstm)
{
VOID_VDATEIFACE(pdbstm);
*pdbstm << "CItemMoniker @" << (VOID FAR *)m_pItemMoniker;
*pdbstm << '\n';
pdbstm->Indent();
*pdbstm << "Refcount is " << (int)(m_pItemMoniker->m_refs) << '\n';
*pdbstm << "Item string is " << m_pItemMoniker->m_lpszItem << '\n';
*pdbstm << "Delimiter is " << m_pItemMoniker->m_lpszDelimiter << '\n';
pdbstm->UnIndent();
}
STDMETHODIMP_(BOOL) NC(CItemMoniker,CDebug)::IsValid ( BOOL fSuspicious )
{
return ((LONG)(m_pItemMoniker->m_refs) > 0);
// add more later, maybe
}
#endif
//
// Unlock Delay object
//
class FAR CDelayUnlockContainer : public CPrivAlloc, public IUnknown
{
public:
STDMETHOD(QueryInterface) ( REFIID iid, LPVOID FAR* ppvObj);
STDMETHOD_(ULONG,AddRef) (void);
STDMETHOD_(ULONG,Release) (void);
CDelayUnlockContainer();
private:
ULONG m_refs;
IOleItemContainer FAR * m_pOleCont;
friend INTERNAL RegisterContainerBound(LPBC pbc, LPOLEITEMCONTAINER pOleCont);
};
CDelayUnlockContainer::CDelayUnlockContainer()
{
m_pOleCont = NULL;
m_refs = 1;
}
STDMETHODIMP CDelayUnlockContainer::QueryInterface (REFIID iid, LPLPVOID ppv)
{
M_PROLOG(this);
if (IsEqualIID(iid, IID_IUnknown))
{
*ppv = this;
AddRef();
return NOERROR;
}
else {
*ppv = NULL;
return ResultFromScode(E_NOINTERFACE);
}
}
STDMETHODIMP_(ULONG) CDelayUnlockContainer::AddRef ()
{
return(InterlockedIncrement((long *)&m_refs));
}
STDMETHODIMP_(ULONG) CDelayUnlockContainer::Release ()
{
if (InterlockedDecrement((long *)&m_refs) == 0)
{
if (m_pOleCont != NULL)
{
m_pOleCont->LockContainer(FALSE);
m_pOleCont->Release();
}
delete this;
return 0;
}
return 1;
}
INTERNAL RegisterContainerBound (LPBC pbc, LPOLEITEMCONTAINER pOleCont)
{
// don't give the delay object the pOleCont before we lock it; do in
// this order so error checking is correct and we don't lock/unlock
// inappropriately.
CDelayUnlockContainer FAR* pCDelay = new CDelayUnlockContainer();
if (pCDelay == NULL)
return ResultFromScode(E_OUTOFMEMORY);
HRESULT hresult;
if ((hresult = pbc->RegisterObjectBound(pCDelay)) != NOERROR)
goto errRet;
if ((hresult = pOleCont->LockContainer(TRUE)) != NOERROR) {
// the delay object is still in the bindctx; no harm
hresult = E_FAIL;
goto errRet;
}
pCDelay->m_pOleCont = pOleCont;
pOleCont->AddRef();
errRet:
pCDelay->Release();
return hresult;
}