|
|
// DocWrapImpl.cpp : Implementation of CDocWrap
#include "stdafx.h"
#include "MSAAText.h"
#include "DocWrap.h"
#include <list_dl.h>
#include "VersionInfo.h"
#include <msctf.h>
#include <msctfp.h>
#define INITGUID
#include <msctfx.h>
#include <tStr.h>
#include <fwd_macros.h> // currently in the DocModel\inc dir... adjust appropriately if porting...
/*
* IDoc - the variant of the ITextStoreACP we're using (ACP/Anchor) * ISink - the corresponding Sink interface (ACP/Anchor) * * ICicDoc - the Cicero doc interface, which extends IDoc * ICicSink - the Cicero sink interface, which extends ISink * * CDocWrap - the document wrapper class, implements ICicDoc (which includes IDoc) * CSinkWrap - the sink wrapper class, implements ICicSink (which includes ISink) * */
class BasicDocTraitsAnchor { public: typedef struct ITextStoreAnchor IDoc; typedef struct ITextStoreAnchorSink ISink;
typedef struct ITextStoreAnchor ICicDoc; typedef struct ITextStoreAnchorServices ICicSink;
typedef class CDocWrapAnchor CDocWrap; typedef class CSinkWrapAnchor CSinkWrap;
typedef struct ITextStoreAnchorEx IDocEx; typedef struct ITextStoreSinkAnchorEx ISinkEx;
typedef IAnchor * PosType; };
class BasicDocTraitsACP { public: typedef struct ITextStoreACP IDoc; typedef struct ITextStoreACPSink ISink;
typedef struct ITextStoreACP ICicDoc; typedef struct ITextStoreACPServices ICicSink;
typedef class CDocWrapACP CDocWrap; typedef class CSinkWrapACP CSinkWrap;
typedef struct ITextStoreACPEx IDocEx; typedef struct ITextStoreACPSinkEx ISinkEx;
typedef LONG PosType; };
// DocWrapExcept contains exception wrapper classes for
// some of the above interfaces...
#include "DocWrapExcept.h"
// Add the appropriate exception wrappers to the set of
// doc traits.
class DocTraitsAnchor: public BasicDocTraitsAnchor { public:
typedef SEHWrapPtr_TextStoreAnchor IDocSEHWrap; };
class DocTraitsACP: public BasicDocTraitsACP { public:
typedef SEHWrapPtr_TextStoreACP IDocSEHWrap; };
/////////////////////////////////////////////////////////////////
// Create an instance of a local non-externally-createable class.
// Just a wrapper for CComObject::CreateInstance, but also AddRef's so that
// the returned object has a ref of 1.
template< class C > HRESULT CreateLocalInstance( C ** p ) { CComObject< C > * p2 = NULL;
HRESULT hr = CComObject< C >::CreateInstance( & p2 );
if( FAILED( hr ) ) { TraceErrorHR( hr, TEXT("CreateLocalInstance") ); return hr; } if( p2 == NULL || hr != S_OK ) { TraceErrorHR( hr, TEXT("CreateLocalInstance returned NULL") ); return E_UNEXPECTED; }
p2->AddRef(); *p = p2; return S_OK; }
// Check hr and condition - return appropriate error if not S_OK and ! cond.
#define CHECK_HR_RETURN( hr, cond ) /**/ \
if( (hr) != S_OK ) \ return FAILED( (hr) ) ? (hr) : E_UNEXPECTED; \ if( (hr) == S_OK && ! ( cond ) ) \ return E_UNEXPECTED;
template < class T > inline void SafeReleaseClear( T * & ptr ) { if( ptr ) { ptr->Release(); ptr = NULL; } }
class CPrivateAddRef { public: CPrivateAddRef( long &rc ) : m_refcount( rc ) { m_refcount++; } ~CPrivateAddRef() { m_refcount--; } private: long &m_refcount; };
class _declspec(uuid("{54D5D291-D8D7-4870-ADE1-331D86FD9430}")) IWrapMgr: public IUnknown { public: virtual void STDMETHODCALLTYPE SetDoc( IUnknown * pDoc ) = 0; virtual HRESULT STDMETHODCALLTYPE CreateWrappedDoc( IUnknown ** ppDoc ) = 0; };
template < class DocTraits > class ATL_NO_VTABLE CWrapMgr : public CComObjectRootEx< CComSingleThreadModel >, public IWrapMgr { public:
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP( CWrapMgr< DocTraits > ) COM_INTERFACE_ENTRY( IWrapMgr ) END_COM_MAP()
private:
// Ptr to the original document.
// Each DocWrapper also has a copy of this ptr - but only we have a refcount on it.
// DocTraits::IDoc * m_pDoc;
DocTraits::IDocSEHWrap m_pDoc;
// Used to remember who requested a sync lock...
DocTraits::CDocWrap * m_pLockRequestedBy;
BOOL m_fInSyncLockRequest;
// Used to remember currently requested lock (ro vs rw)
DWORD m_dwPendingLock;
// Our sink - called by the doc
DocTraits::CSinkWrap * m_pSinkWrap;
// Current sink event mask - all client sink masks or'd together.
DWORD m_dwSinkMask; BOOL m_fSinkActive;
public:
// List of DocWraps (one per client)
List_dl< DocTraits::CDocWrap > m_DocWraps;
LONG m_lIterationRefCount; //
// Ctor, Dtor...
//
CWrapMgr() : m_pDoc( NULL ), m_pSinkWrap( NULL ), m_dwSinkMask( 0 ), m_fSinkActive( FALSE ), m_pLockRequestedBy( NULL ), m_fInSyncLockRequest( FALSE ), m_dwPendingLock( 0 ), m_lIterationRefCount( 0 )
{ // Done.
TraceInfo( TEXT("WrapMgr ctor") ); }
~CWrapMgr() { TraceInfo( TEXT("WrapMgr dtor") );
AssertMsg( m_pDoc, TEXT("CWrapMgr::SetDoc never called?") ); m_pDoc->Release(); Assert( m_DocWraps.empty() ); Assert( m_pSinkWrap == NULL ); Assert( m_dwSinkMask == 0 ); }
//
// IWrapMgr interface - used by the DocWrap holder to give us a doc and ask for wrappers for it...
//
void STDMETHODCALLTYPE SetDoc( IUnknown * pDoc ) { _SetDoc( static_cast< DocTraits::IDoc * >( pDoc ) ); }
HRESULT STDMETHODCALLTYPE CreateWrappedDoc( IUnknown ** ppDoc ) { return _CreateWrappedDoc( reinterpret_cast< DocTraits::IDoc ** >( ppDoc ) ); }
void RemoveDeadDocs() { for( Iter_dl < DocTraits::CDocWrap > i ( m_DocWraps ) ; ! i.AtEnd() ; ) { Iter_dl < DocTraits::CDocWrap > dead = i;
i++; if (dead->m_bUnadvised && m_lIterationRefCount == 0) m_DocWraps.remove( dead ); } }
//
// Called by DocWrap, to tell us when it's been released, and to get us (the wrapper manager)
// to handle a call that affects other wrappers on the same doc - advise/unadvise and lock calls.
//
void DocWrap_NotifyDisconnect( DocTraits::CDocWrap * pFrom ) { // A DocWrap has been released by a client and is going away...
// TODO - if using locks, check pFrom for lock, if it has it, release, broadcast relase.
// - how can this scenario occur?
// - doc is released in callback. Weird, but valid?
} HRESULT DocWrap_HandleRequestLock( DocTraits::CDocWrap * pFrom, DWORD dwLockFlags, HRESULT * phrSession ) { // Other clients may request a lock while it's being held by someone else, but the
// current holder should not request a lock.
// (This can happen when client1 is holding a lock, and issues an editing operation.
// The wrapper broadcasts a OnTextChange event to other clients, and they may request locks.)
AssertMsg( m_pLockRequestedBy != pFrom, TEXT("Lock owner re-request held lock? (Reentrancy?)") );
if( dwLockFlags == 0 || dwLockFlags & ~ ( TS_LF_SYNC | TS_LF_READ | TS_LF_READWRITE ) ) // check that only these bits present
{ AssertMsg( FALSE, TEXT("Bad lock flags") ); return E_INVALIDARG; }
if( dwLockFlags & TS_LF_SYNC ) { // Can't process a SYNC call while someone else is holding the lock...
if( m_pLockRequestedBy ) { return E_FAIL; }
// Sync lock - can just pass through - need to set up m_pLockRequestedBy so that the
// sink can pass it onto the correct client.
m_pLockRequestedBy = pFrom; m_fInSyncLockRequest = TRUE; HRESULT hr = m_pDoc->RequestLock( dwLockFlags, phrSession ); m_fInSyncLockRequest = FALSE; m_pLockRequestedBy = NULL; return hr; } // sync lock
else { // if async lock, update/upgrade the wrap's pending lock mask if necessary...
// This test (which assumes that dwLockFlags != 0) upgrades to r/w if necessary.
// TODO - should this update only be done conditionally if RequestLock succeeds?
// (or no upgrade necessary?)
Assert( dwLockFlags != 0 ); if( pFrom->m_dwPendingLock != TS_LF_READWRITE ) pFrom->m_dwPendingLock = dwLockFlags; if( m_pLockRequestedBy ) { // someone else is currently holding the lock.
// All we have to do is update the doc's PendingLock flags (see above) - they will
// be picked up and handled by the loop in Handle_OnLockGranted when the current
// lock holder returns.
// Nothing else to do here.
// But send it on the doc anyway if it's the same person
if ( m_pLockRequestedBy == pFrom ) { return m_pDoc->RequestLock( m_dwPendingLock, phrSession ); } return S_OK; } else { // We don't have a lock yet.
// Check our current request, if any, and if necessary, request a write,
// even if we've already requested a read.
// Update combined mask if necessary...
DWORD dwNewLock = m_dwPendingLock; // Calculate required lock...
if( dwNewLock != TS_LF_READWRITE ) dwNewLock = dwLockFlags;
HRESULT hr = E_FAIL;
// Do we need to request a new lock/upgrade?
if( dwNewLock != m_dwPendingLock ) { // May get an immediate response even for an async request, so need to set this up
// event if not a sync request...
m_pLockRequestedBy = pFrom;
DWORD OldPendingLock = m_dwPendingLock; m_dwPendingLock = dwNewLock;
HRESULT hrOut = E_FAIL; hr = m_pDoc->RequestLock( m_dwPendingLock, & hrOut );
m_pLockRequestedBy = NULL;
if( hr != S_OK ) { // After all that, the request failed...
// Revert to previous pending lock...
m_dwPendingLock = OldPendingLock;
// fall out...
} else { // Regardless of fail/success, copy the outgoing hr.
// Clearing of the pending flags is done in OnLockgranted, not here.
// Shouldn't get this for an async request...
Assert( hrOut != TS_E_SYNCHRONOUS );
*phrSession = hrOut; } } else { // Our existing pending lock covers this request - success.
*phrSession = TS_S_ASYNC; hr = S_OK; }
return hr; } } // async lock
}
HRESULT DocWrap_UpdateSubscription() { Assert( ( m_dwSinkMask & ~ TS_AS_ALL_SINKS ) == 0 ); // If there are no active sinks, then SinkMask should be 0.
Assert( m_fSinkActive || m_dwSinkMask == 0 );
// Work out required mask - by or'ing masks of all doc's sinks...
DWORD NewMask = 0; BOOL NewfSinkActive = FALSE; for( Iter_dl< DocTraits::CDocWrap > i ( m_DocWraps ) ; ! i.AtEnd() ; i++ ) { if( i->m_Sink.m_pSink != NULL ) { Assert( ( i->m_Sink.m_dwMask & ~ TS_AS_ALL_SINKS ) == 0 ); NewMask |= i->m_Sink.m_dwMask; NewfSinkActive = TRUE; } }
Assert( ( NewMask & ~ TS_AS_ALL_SINKS ) == 0 ); // If there are no active sinks, then NewMask should be 0.
Assert( NewfSinkActive || NewMask == 0 );
// Tricky bit:
// NewMask==0 does not mean that there's no sinks - some may have
// dwMask == 0 to receive LockGranted calls.
// So to check if the status has changed, we need to take whether
// there are any active sinks (not just the masks) into account.
if( NewfSinkActive == m_fSinkActive && NewMask == m_dwSinkMask ) { // No change - nothing to do.
return S_OK; }
// Three possibilities:
// (a) free to unregister,
// (b) need to register
// (c) need to update existing registration.
// We handle (b) and (c) as the same case.
if( ! NewfSinkActive ) { // No sinks - can unregister...
m_fSinkActive = FALSE; m_dwSinkMask = 0;
if( ! m_pSinkWrap ) { Assert( FALSE ); // Odd - where did our sink wrap go - we didn't unregister it yet, so it should
// still be around...
// (Possible that server pre-emptively released us - so not an error.)
} else { // Don't do anything else with m_pSinkWrap - the doc should release it.
HRESULT hr = m_pDoc->UnadviseSink( static_cast< DocTraits::ICicSink * >( m_pSinkWrap ) ); // shouldn't fail...
Assert( hr == S_OK );
// Doc should have released the sink (which would have called us back to NULL out
// m_pSinkWrap...)
Assert( m_pSinkWrap == NULL ); } return S_OK; } else { // Update existing / add new...
// If we already had an existing sink, there should be a sinkwrap...
// (unless doc let it go prematurely...)
Assert( ! m_fSinkActive || m_pSinkWrap );
BOOL NeedRelease = FALSE; if( ! m_pSinkWrap ) { HRESULT hr = CreateLocalInstance( & m_pSinkWrap ); if( hr != S_OK ) return hr; m_pSinkWrap->Init( this, & m_DocWraps );
// After doing the Advise, release our ref on the sink, so that only
// the doc holds a ref and controls its liftetime.
// We still keep a pointer, but it's a "weak reference" - if the
// sink goes away (because the doc releases its reference), the sink
// notifies us so we can NULL-out the ptr. (see SinkWrap_NotifyDisconnect)
NeedRelease = TRUE; }
// Always try advising with the Cicero sink first - if that
// doesn't work, fall back to the ITextStoreACP one.
//
// (Use of static_cast is necessary here to avoid ambiguity over
// which IUnknown we convert to - the ICicSink one, or the
// IServiceProvider one. We want the ICicSink one, to match the
// IID passed in.)
HRESULT hr = m_pDoc->AdviseSink( __uuidof( DocTraits::ICicSink ), static_cast< DocTraits::ICicSink * >( m_pSinkWrap ), NewMask ); if( hr != S_OK ) { hr = m_pDoc->AdviseSink( __uuidof( DocTraits::ISink ), static_cast< DocTraits::ISink * >( m_pSinkWrap ), NewMask ); }
if( NeedRelease ) { m_pSinkWrap->Release(); }
if( hr == S_OK ) { m_fSinkActive = TRUE; m_dwSinkMask = NewMask; } else { AssertMsg( FALSE, TEXT("AdviseSink failed") ); }
return hr; } }
void DocWrap_NotifyRevoke() {
// Send OnDisconnect to any SinkEx sinks,
CPrivateAddRef MyAddRef(m_lIterationRefCount);
for( Iter_dl< DocTraits::CDocWrap > i ( m_DocWraps ) ; ! i.AtEnd() ; i++ ) { // Is this the SinkExt sink?
if( i->m_Sink.m_iid == __uuidof( DocTraits::ISinkEx ) ) { DocTraits::ISinkEx * pSink = static_cast< DocTraits::ISinkEx * >( i->m_Sink.m_pSink );
if ( pSink ) pSink->OnDisconnect(); } }
for( Iter_dl < DocTraits::CDocWrap > j ( m_DocWraps ) ; ! j.AtEnd() ; ) { Iter_dl < DocTraits::CDocWrap > DocToDelete = j; j++;
m_DocWraps.remove( DocToDelete ); DocToDelete->Release(); }
}
//
// Called by SinkWrap to let us know when its been released...
//
void SinkWrap_NotifyDisconnect() { // The sink has been released by the application - it's now deleteing itself,
// so we NULL-out our weak reference to it. (If we need another one in future,
// we'll create a new one.)
// Clear our sinks to reflect this...
for( Iter_dl< DocTraits::CDocWrap > i ( m_DocWraps ) ; ! i.AtEnd() ; i++ ) { i->m_Sink.ClearAndRelease(); } m_pSinkWrap = NULL; }
HRESULT SinkWrap_HandleOnLockGranted ( DWORD dwLockFlags ) { // Is this servicing a sync or async request?
Assert( ( dwLockFlags & ~ ( TS_LF_SYNC | TS_LF_READ | TS_LF_READWRITE ) ) == 0 );
CPrivateAddRef MyAddRef(m_lIterationRefCount);
if( m_fInSyncLockRequest ) { // Sync lock - just pass it straight through to whoever requested it...
Assert( dwLockFlags & TS_LF_SYNC ); Assert( m_pLockRequestedBy && m_pLockRequestedBy->m_Sink.m_pSink );
if( ! m_pLockRequestedBy || ! m_pLockRequestedBy->m_Sink.m_pSink ) return E_UNEXPECTED; DocTraits::ISink * pSink = static_cast< DocTraits::ISink * > ( m_pLockRequestedBy->m_Sink.m_pSink );
HRESULT hr = pSink->OnLockGranted( dwLockFlags ); return hr; } else { // Async lock - hand it out to whoever wanted it...
Assert( ! ( dwLockFlags & TS_LF_SYNC ) );
// If we're waiting for a r/w lock, the lock granted should be r/w too...
Assert( ! ( m_dwPendingLock & TS_LF_READWRITE ) || ( dwLockFlags & TS_LF_READWRITE ) );
// Clear the pending lock, since we're now servicing them...
m_dwPendingLock = 0;
// Keep looking through the docs, servicing locks. We loop because some docs may
// request locks while another holds the lock, so we have to come back an service them.
// When we loop through all docs without seeing any locks, then we know all locks
// have been serviced.
//
// If this is a read lock, then we can only service read locks now - we'll have to
// ask for a separate write lock later if we need one.
BOOL fNeedWriteLock = FALSE; for( ; ; ) { BOOL fWorkDone = FALSE;
// Forward to all clients who had requested a lock...
for( Iter_dl< DocTraits::CDocWrap > i ( m_DocWraps ) ; ! i.AtEnd() ; i++ ) { // Is the mask we've been granted sufficient for the client's request (if any)?
DWORD ReqdLock = i->m_dwPendingLock; if( ReqdLock ) { if( ( ReqdLock | dwLockFlags ) == dwLockFlags ) { // tell the doc wrapper that it is in OnLockGranted and what kind of lock it has
i->m_dwGrantedLock = ReqdLock; // Clear the mask...
i->m_dwPendingLock = 0;
DocTraits::ISink * pSink = static_cast< DocTraits::ISink * > ( i->m_Sink.m_pSink );
// How about...
// (a) also call Next before callback.
// (b) store current value of iter in mgr - in doc's release, it can check, and bump if necessary;
pSink->OnLockGranted( ReqdLock );
// tell the doc wrapper that it is no longer in OnLockGranted
i->m_dwGrantedLock = 0; fWorkDone = TRUE; } else { // This client wants a write lock, but we've only got a read lock...
fNeedWriteLock = TRUE; } } }
// If we didn't need to handle any lock requests the last time around the loop,
// then our work is done. (If we did handle any lock requests, we should go back
// and re-check all docs, in case one of them requested a lock while one of the
// locks we serviced was holding it.)
if( ! fWorkDone ) break; }
if( fNeedWriteLock ) { // TODO - need to find a way to handle this. Can we call the doc's RequestLock again now?
AssertMsg( FALSE, TEXT("Write lock requested while holding read lock - not implemented yet") ); }
// All done!
return S_OK; } }
private: //
// Internal functions...
//
void _SetDoc( DocTraits::IDoc * pDoc ) { AssertMsg( ! m_pDoc, TEXT("CWrapMgr::SetDoc should be called once when m_pDoc is NULL") ); m_pDoc = pDoc; m_pDoc->AddRef(); }
HRESULT _CreateWrappedDoc( DocTraits::IDoc ** ppDoc ) { *ppDoc = NULL;
// Create a doc wrapper...
DocTraits::CDocWrap * pCDocWrap; HRESULT hr = CreateLocalInstance( & pCDocWrap ); CHECK_HR_RETURN( hr, pCDocWrap != NULL );
pCDocWrap->Init( this, m_pDoc );
// Add to our list...
m_DocWraps.AddToHead( pCDocWrap ); // And return it...
*ppDoc = pCDocWrap; return S_OK; } };
struct SinkInfo { IUnknown * m_pSink; IID m_iid; DWORD m_dwMask; IUnknown * m_pCanonicalUnk; // IUnknown used for identity comparisons
void Validate() { #ifdef DEBUG
if( m_pSink ) { Assert( m_pCanonicalUnk != NULL ); // Check that mask contains only valid bits
Assert( ( m_dwMask & ~ TS_AS_ALL_SINKS ) == 0 ); } else { Assert( m_pCanonicalUnk == NULL ); Assert( m_dwMask == 0 ); } #endif
}
SinkInfo() : m_pSink( NULL ), m_pCanonicalUnk( NULL ), m_dwMask( 0 ) { Validate(); // Done.
}
~SinkInfo() { Validate(); AssertMsg( m_pSink == NULL && m_pCanonicalUnk == NULL, TEXT("Sink not cleared" ) ); }
void Set( IUnknown * pSink, REFIID iid, DWORD dwMask, IUnknown * pCanonicalUnk ) { Validate();
AssertMsg( m_pSink == NULL, TEXT("Set() sink that's already in use" ) );
m_pSink = pSink; m_pSink->AddRef(); m_iid = iid; m_dwMask = dwMask; m_pCanonicalUnk = pCanonicalUnk; m_pCanonicalUnk->AddRef();
Validate(); }
void UpdateMask( DWORD dwMask ) { Validate();
AssertMsg( m_pSink != NULL, TEXT("UpdateMask() on empty sink") );
m_dwMask = dwMask;
Validate(); }
void ClearAndRelease() { Validate();
if( m_pSink ) { m_pSink->Release(); m_pSink = NULL; m_pCanonicalUnk->Release(); m_pCanonicalUnk = NULL; m_dwMask = 0; } } };
// Fwd. decl for the sink-wrap class, needed since we grant it frienship in the
// DocWrap class...
template< class DocTraits > class CSinkWrapBase;
//
// CDocWrapBase
//
// - Base from which Anchor and ACP document wrappers are derived.
//
// This class contains ACP/Anchor-neutral wrapping code - anything that is
// ACP/Anchor-specific is handled in the derived ..ACP or ...Anchor class
// instead.
//
// This class derives from the full Cicero doc interface (DocTraits::ICicDoc -
// which is a typedef for ITfTextStore[Anchor]), which in turn includes the
// ITextStoreACP doc interface. Currently the Cicero interface doesn't add any
// additional methods.
//
// This class is also on a list of document wrappers - so we're derived from
// Link_dl. (The list is managed by the wrapper manager.) The list will be a
// list of derived classes, so the type of the link is of the derived class
// (DocTraits::CDocWrap - which is a typedef for CDocWrapACP/Anchor), instead
// of being based on this class.
// (At the moment we don't actually use any of the derived-class functionality,
// but may do so in future.)
// {B5DCFDAF-FBAD-4ef6-A5F8-E7CC0833A3B1}
static const GUID DOCWRAP_IMPLID = { 0xb5dcfdaf, 0xfbad, 0x4ef6, { 0xa5, 0xf8, 0xe7, 0xcc, 0x8, 0x33, 0xa3, 0xb1 } };
template< class _DocTraits > class ATL_NO_VTABLE CDocWrapBase : public CComObjectRootEx< CComSingleThreadModel >, public Link_dl< _DocTraits::CDocWrap >, public _DocTraits::ICicDoc,
public _DocTraits::IDocEx,
public IClonableWrapper, public IInternalDocWrap, public ICoCreateLocally, public CVersionInfo, public IServiceProvider { public:
// This typedef makes the DocTraits type visible in this and in the
// Anchor/ACP-specific derived classes. (Otherwise, as a template
// parameter in this class, it would not be available to them.)
typedef _DocTraits DocTraits;
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP( CDocWrapBase< DocTraits > ) COM_INTERFACE_ENTRY( DocTraits::IDoc ) COM_INTERFACE_ENTRY( DocTraits::ICicDoc ) COM_INTERFACE_ENTRY( DocTraits::IDocEx ) COM_INTERFACE_ENTRY( IClonableWrapper ) COM_INTERFACE_ENTRY( IInternalDocWrap ) COM_INTERFACE_ENTRY( ICoCreateLocally ) COM_INTERFACE_ENTRY( IVersionInfo ) COM_INTERFACE_ENTRY( IServiceProvider ) END_COM_MAP()
private:
// WrapMgr uses the Link_dl base when adding this to its list.
friend CWrapMgr< DocTraits >;
// Used by WrapMgr to track what type of lock was requested.
DWORD m_dwPendingLock;
// Used by WrapMgr to track what type of lock granted.
DWORD m_dwGrantedLock;
// SinkWrapBase - and its derived Anchor/ACP-specific class - uses the list
// and the members of SinkInfo when broadcasting
friend CSinkWrapBase< DocTraits >; friend DocTraits::CSinkWrap;
protected:
// Each doc can have a corresponding sink:
SinkInfo m_Sink;
// Link back to the wrapper manager - so we can tell it when we're going
// away. We also get it to handle calls which affect other wrappers on the
// same document - especially sinks and locks.
CWrapMgr< DocTraits > * m_pMgr;
// Used by derived classes to forward calls to doc...
// DocTraits::IDoc * m_pDoc;
DocTraits::IDocSEHWrap m_pDoc;
// TEMP BUGBUG - used to access the attribute extentions for the moment...
DocTraits::IDocEx * m_pDocEx;
bool m_bUnadvised;
HRESULT STDMETHODCALLTYPE VerifyLock( DWORD dwLockFlags) { IMETHOD( VerifyLock );
if ( m_dwGrantedLock ) { // We have a lock, make sure it's the right kind
if ( (dwLockFlags & TS_LF_READWRITE) == m_dwGrantedLock || (dwLockFlags & TS_LF_READWRITE) == TS_LF_READ ) return S_OK; }
TraceDebug( TEXT("Lock rejected") );
return S_FALSE; } // This macro just forwards the call directly to the doc...
#define DocWrap_FORWARD( fname, c, params ) /**/ \
HRESULT STDMETHODCALLTYPE fname AS_DECL( c, params ) \ {\ IMETHOD( fname );\ return m_pDoc-> fname AS_CALL( c, params ) ;\ }
// This macro just forwards the call directly to the doc after checking for the correct lock
#define DocWrap_FORWARD_READLOCK( fname, c, params ) /**/ \
HRESULT STDMETHODCALLTYPE fname AS_DECL( c, params ) \ {\ IMETHOD( fname );\ if ( VerifyLock( TS_LF_READ ) == S_FALSE )\ return TS_E_NOLOCK;\ return m_pDoc-> fname AS_CALL( c, params ) ;\ }
#define DocWrap_FORWARDEXT( fname, c, params ) /**/ \
HRESULT STDMETHODCALLTYPE fname AS_DECL( c, params ) \ {\ IMETHOD( fname );\ if( ! m_pDocEx )\ return E_NOTIMPL;\ return m_pDocEx-> fname AS_CALL( c, params ) ;\ }
// This slightly more complicated one (!) forwards to the doc,
// and if the call succeeds, then broadcasts to all sinks except the one
// for this document.
// So if one client does a SetText, that SetText goes through, and
// all other clietns with callbacks for the TS_AS_TEXT_CHANGE event will
// also get an OnTextChange event.
#define DocWrap_FORWARD_AND_SINK( fname, c, params, mask, callsink ) /**/ \
HRESULT STDMETHODCALLTYPE fname AS_DECL( c, params )\ {\ IMETHOD( fname );\ Assert( m_pMgr );\ if ( VerifyLock( TS_LF_READWRITE ) == S_FALSE )\ return TS_E_NOLOCK;\ m_pMgr->RemoveDeadDocs();\ CPrivateAddRef MyAddRef(m_pMgr->m_lIterationRefCount);\ HRESULT hr = m_pDoc-> fname AS_CALL( c, params );\ if( hr != S_OK )\ {\ TraceDebugHR( hr, TEXT("failed") );\ return hr;\ }\ for( Iter_dl < DocTraits::CDocWrap > i ( m_pMgr->m_DocWraps ) ; ! i.AtEnd() ; i++ )\ {\ DocTraits::ISink * pSink = static_cast< DocTraits::ISink * >( i->m_Sink.m_pSink );\ DWORD dwMask = i->m_Sink.m_dwMask;\ DocTraits::CDocWrap * pTheDoc = i;\ if( pTheDoc != this && pSink && ( dwMask & mask ) )\ {\ callsink ;\ }\ }\ return S_OK ;\ }
public:
//
// Ctor, Dtor, and initialization...
//
CDocWrapBase() : m_pDoc( NULL ), m_pMgr( NULL ), m_dwPendingLock( 0 ), m_dwGrantedLock( 0 ), m_pDocEx( NULL ), m_bUnadvised( false ) {
}
~CDocWrapBase() { AssertMsg( m_pMgr != NULL, TEXT("CDocWrapBase::Init never got called?") );
// Clear up sink...
if( m_Sink.m_pSink ) { m_Sink.ClearAndRelease();
// Manager will unadvise, if we were the last to go...
m_pMgr->DocWrap_UpdateSubscription(); }
m_pMgr->DocWrap_NotifyDisconnect( static_cast< DocTraits::CDocWrap * >( this ) ); m_pMgr->Release();
if( m_pDocEx ) m_pDocEx->Release(); }
void Init( CWrapMgr< DocTraits > * pMgr, DocTraits::IDoc * pDoc ) { AssertMsg( m_pMgr == NULL, TEXT("CDocWrapBase::Init should only be called once when m_pMgr is NULL") ); m_pMgr = pMgr; m_pMgr->AddRef();
m_pDoc = pDoc; AddRef(); // Keep our own ref count so it dosn't go away until we remove from the list
CVersionInfo::Add( DOCWRAP_IMPLID, 1, 0, L"Microsoft MSAA Wrapper 1.0", L"na", NULL); m_pDoc->QueryInterface( __uuidof( DocTraits::IDocEx ), (void **) & m_pDocEx ); }
//
// Implementation of ACP/Anchor-neutral methods...
// These ones require special handling...
//
HRESULT STDMETHODCALLTYPE AdviseSink( REFIID riid, IUnknown *punk, DWORD dwMask ) { IMETHOD( AdviseSink );
Assert( m_pMgr );
if( punk == NULL ) { return E_INVALIDARG; }
// Accept the following sinks:
// * Original ITextStoreACPSink,
// * Cicero sink (ITextStoreACP + Cicero extentions)
// * ITextStoreACPSinkEx sink (ITextStoreACP + OnDisconnect method)
if( riid != __uuidof( DocTraits::ISink ) && riid != __uuidof( DocTraits::ICicSink ) && riid != __uuidof( DocTraits::ISinkEx ) ) { return E_NOINTERFACE; }
// check mask contains only valid bits
if( dwMask & ~ ( TS_AS_ALL_SINKS ) ) { return E_INVALIDARG; } // Get canonical unknown (for interface comparing...)
IUnknown * pCanonicalUnk = NULL; HRESULT hr = punk->QueryInterface( IID_IUnknown, (void **) & pCanonicalUnk ); if( hr != S_OK || pCanonicalUnk == NULL ) { return E_FAIL; }
// If this is first, set it...
if( m_Sink.m_pSink == NULL ) { // Allow the doc to work out the update cumulative mask and re-advise the doc if necessary...
m_Sink.Set( punk, riid, dwMask, pCanonicalUnk );
// Manager will scan through all sink masks, and re-Advise if necessary.
hr = m_pMgr->DocWrap_UpdateSubscription();
if( hr != S_OK ) { // advising didn't work, or something else went wrong - revert back to empty sink...
m_Sink.ClearAndRelease(); } } else { // Not the first time - check if we're updating the existing mask...
if( pCanonicalUnk != m_Sink.m_pCanonicalUnk ) { // Attempt to register a different sink - error...
hr = CONNECT_E_ADVISELIMIT; } else { // Remember the old mask - if the update doesn't work, revert back to this.
DWORD OldMask = m_Sink.m_dwMask; m_Sink.UpdateMask( dwMask );
// Manager will scan through all dwMasks, and re-Advise if necessary.
hr = m_pMgr->DocWrap_UpdateSubscription();
if( hr != S_OK ) m_Sink.UpdateMask( OldMask ); } }
pCanonicalUnk->Release(); return hr; }
HRESULT STDMETHODCALLTYPE UnadviseSink( IUnknown *punk ) { IMETHOD( UnadviseSink );
Assert( m_pMgr );
if( punk == NULL ) { return E_POINTER; }
// Get canonical unknown (for interface comparing...)
IUnknown * pCanonicalUnk = NULL; HRESULT hr = punk->QueryInterface( IID_IUnknown, (void **) & pCanonicalUnk );
if( hr != S_OK || pCanonicalUnk == NULL ) { return E_FAIL; } if( pCanonicalUnk != m_Sink.m_pCanonicalUnk ) { // Sink doesn't match!
return E_INVALIDARG; }
m_Sink.ClearAndRelease();
// Manager will scan through all dwMasks, and re-Advise if necessary.
hr = m_pMgr->DocWrap_UpdateSubscription();
// Not much we can do if the update fails - but even if it does fail,
// we want to disconnect this sink, and not fail the Unadvise.
Assert( hr == S_OK );
pCanonicalUnk->Release();
m_bUnadvised = true;
return S_OK; // NOT hr, since this should always succeed.
}
HRESULT STDMETHODCALLTYPE RequestLock( DWORD dwLockFlags, HRESULT * phrSession ) { IMETHOD( RequestLock );
Assert( m_pMgr ); // The cast here is safe, since we'll only be used as a derived class
DocTraits::CDocWrap * pThisAsDerived = static_cast< DocTraits::CDocWrap * >( this );
return m_pMgr->DocWrap_HandleRequestLock( pThisAsDerived, dwLockFlags, phrSession ); }
//
// These Anchor/ACP-neutral methods can just be forwarded directly to the real doc...
//
DocWrap_FORWARD( GetStatus, 1, ( TS_STATUS *, pdcs ) )
DocWrap_FORWARD_READLOCK( QueryInsert, 5, ( DocTraits::PosType, InsertStart, DocTraits::PosType, InsertEnd, ULONG, cch, DocTraits::PosType *, ppaInsertStart, DocTraits::PosType *, ppaInsertEnd ) )
DocWrap_FORWARD_READLOCK( QueryInsertEmbedded, 3, ( const GUID *, pguidService, const FORMATETC *, pFormatEtc, BOOL *, pfInsertable ) ) DocWrap_FORWARD( GetScreenExt, 2, ( TsViewCookie, vcView, RECT *, prc ) )
DocWrap_FORWARD( GetWnd, 2, ( TsViewCookie, vcView, HWND *, phwnd ) )
DocWrap_FORWARD_READLOCK( GetFormattedText, 3, ( DocTraits::PosType, Start, DocTraits::PosType, End, IDataObject **, ppDataObject ) )
DocWrap_FORWARD_READLOCK( GetTextExt, 5, ( TsViewCookie, vcView, DocTraits::PosType, Start, DocTraits::PosType, End, RECT *, prc, BOOL *, pfClipped ) )
DocWrap_FORWARDEXT( ScrollToRect, 4, ( DocTraits::PosType, Start, DocTraits::PosType, End, RECT, rc, DWORD, dwPosition ) )
DocWrap_FORWARD( GetActiveView, 1, ( TsViewCookie *, pvcView ) ) // IClonableWrapper
HRESULT STDMETHODCALLTYPE CloneNewWrapper( REFIID riid, void ** ppv ) { IMETHOD( CloneNewWrapper );
// Just call through to CWrapMgr's CreateWrappedDoc...
Assert( m_pMgr );
IUnknown * punk; HRESULT hr = m_pMgr->CreateWrappedDoc( & punk ); if( hr != S_OK ) return hr;
hr = punk->QueryInterface( riid, ppv ); punk->Release(); return hr; }
// IInternalDocWrap
HRESULT STDMETHODCALLTYPE NotifyRevoke() { // Just pass on to the CWrapMgr...
Assert( m_pMgr );
m_pMgr->DocWrap_NotifyRevoke();
return S_OK; } // ICoCreateLocally
#include <Rpcdce.h>
HRESULT STDMETHODCALLTYPE CoCreateLocally ( REFCLSID rclsid, DWORD dwClsContext, REFIID riid, IUnknown ** punk, REFIID riidParam, IUnknown * punkParam, VARIANT varParam ) { IMETHOD( CoCreateLocally );
LPCTSTR pPrivs = NULL; //Pointer to handle to privilege information
HRESULT hrSec = CoQueryClientBlanket( 0, 0, 0, 0, 0, (void **)&pPrivs, 0 ); if ( hrSec != S_OK ) return hrSec;
TSTR strUser(128); DWORD nSize = strUser.left(); if ( !GetUserName( strUser.ptr(), &nSize ) ) return E_ACCESSDENIED; strUser.advance( nSize - 1 ); TSTR strClientUser( pPrivs ); const int nSlashPos = strClientUser.find( TEXT("\\") ); if ( nSlashPos > 0 ) strClientUser = strClientUser.substr( nSlashPos + 1, strClientUser.size() - nSlashPos );
TraceDebug( TSTR() << TEXT("Current user = ") << strUser << TEXT(", Client user = ") << strClientUser ); if ( strClientUser.compare( strUser ) != 0 ) if ( strClientUser.compare( TEXT("SYSTEM") ) != 0 ) return E_ACCESSDENIED;
HRESULT hr = CoCreateInstance(rclsid, NULL, dwClsContext, riid, (void **)punk); if (hr != S_OK) return hr;
CComPtr<ICoCreatedLocally> pICoCreatedLocally; hr = (*punk)->QueryInterface(IID_ICoCreatedLocally, (void **)&pICoCreatedLocally); if (hr != S_OK) return hr;
hr = pICoCreatedLocally->LocalInit(m_pDoc, riidParam, punkParam, varParam); if (hr != S_OK) return hr;
return S_OK; }
//
// IServiceProvider - Cicero uses this to 'drill through' to the original anchor to pull out
// internal information. Just pass it through...
//
HRESULT STDMETHODCALLTYPE QueryService( REFGUID guidService, REFIID riid, void **ppvObject ) { IMETHOD( QueryService );
*ppvObject = NULL;
CComPtr<IServiceProvider> pISP; HRESULT hr = m_pDoc->QueryInterface( IID_IServiceProvider, (void **) & pISP ); if( hr != S_OK || pISP == NULL ) return E_FAIL;
hr = pISP->QueryService( guidService, riid, ppvObject );
return hr; } };
//
// CTextStoreWrapACP - ACP version of ITextStoreACP wrapper...
//
class ATL_NO_VTABLE CDocWrapACP : public CDocWrapBase< DocTraitsACP > { public:
// ITextStoreACP
DocWrap_FORWARD_READLOCK( GetSelection, 4, ( ULONG, ulIndex, ULONG, ulCount, TS_SELECTION_ACP *, pSelection, ULONG *, pcFetched ) ) DocWrap_FORWARD_READLOCK( GetText, 9, ( LONG, acpStart, LONG, acpEnd, WCHAR *, pchPlain, ULONG, cchPlainReq, ULONG *, pcchPlainRet, TS_RUNINFO *, prgRunInfo, ULONG, cRunInfoReq, ULONG *, pcRunInfoRet, LONG *, pacpNext ) ) DocWrap_FORWARD_READLOCK( GetEmbedded, 4, ( LONG, Pos, REFGUID, rguidService, REFIID, riid, IUnknown **, ppunk ) ) DocWrap_FORWARD_READLOCK( GetEndACP, 1, ( LONG *, pacp ) ) DocWrap_FORWARD_READLOCK( GetACPFromPoint, 4, ( TsViewCookie, vcView, const POINT *, ptScreen, DWORD, dwFlags, LONG *, pacp ) ) DocWrap_FORWARD( RequestSupportedAttrs, 3, ( DWORD, dwFlags, ULONG, cFilterAttrs, const TS_ATTRID *, paFilterAttrs ) ) DocWrap_FORWARD_READLOCK( RequestAttrsAtPosition, 4, ( DocTraits::PosType, Pos, ULONG, cFilterAttrs, const TS_ATTRID *, paFilterAttrs, DWORD, dwFlags ) ) DocWrap_FORWARD_READLOCK( RequestAttrsTransitioningAtPosition, 4, ( DocTraits::PosType, Pos, ULONG, cFilterAttrs, const TS_ATTRID *, paFilterAttrs, DWORD, dwFlags ) ) DocWrap_FORWARD_READLOCK( FindNextAttrTransition, 8, ( LONG, acpStart, LONG, acpEnd, ULONG, cFilterAttrs, const TS_ATTRID *, paFilterAttrs, DWORD, dwFlags, LONG *, pacpNext, BOOL *, pfFound, LONG *, plFoundOffset ) ) DocWrap_FORWARD( RetrieveRequestedAttrs, 3, ( ULONG, ulCount, TS_ATTRVAL *, paAttrVals, ULONG *, pcFetched ) ) DocWrap_FORWARD_AND_SINK( SetSelection, 2, ( ULONG, ulCount, const TS_SELECTION_ACP *, pSelection ), TS_AS_SEL_CHANGE, pSink->OnSelectionChange() )
DocWrap_FORWARD_AND_SINK( SetText, 6, ( DWORD, dwFlags, LONG, acpStart, LONG, acpEnd, const WCHAR *, pchText, ULONG, cch, TS_TEXTCHANGE *, pChange ), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, pChange ) )
DocWrap_FORWARD_AND_SINK( InsertEmbedded, 5, ( DWORD, dwFlags, LONG, acpStart, LONG, acpEnd, IDataObject *, pDataObject, TS_TEXTCHANGE *, pChange ), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, pChange ) )
DocWrap_FORWARD_AND_SINK( InsertTextAtSelection, 6, ( DWORD, dwFlags, const WCHAR *, pchText, ULONG, cch, LONG *, pacpStart, LONG *, pacpEnd, TS_TEXTCHANGE *, pChange), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, pChange ) )
DocWrap_FORWARD_AND_SINK( InsertEmbeddedAtSelection, 5, ( DWORD, dwFlags, IDataObject *, pDataObject, LONG *, pacpStart, LONG *, pacpEnd, TS_TEXTCHANGE *, pChange), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, pChange ) )
};
//
// CTextStoreWrapAnchor - Anchor version of ITextStoreACP wrapper
//
class ATL_NO_VTABLE CDocWrapAnchor : public CDocWrapBase< DocTraitsAnchor > { /*
// Used when generating OnTextChange events in response to InsertEmbedded.
// See forwarding macro for InsertEmbedded below...
void ProcessInsertEmbeddedOnTextChange( DocTraits::ISink * pSink, DWORD dwFlags, IAnchor * paPos ) { // Want to send a TextChange with anchors before and after the insert position -
// we have the before position - clone and move it to get the after position.
IAnchor * pAnchorAfter = NULL; HRESULT hr = paPos->Clone( & pAnchorAfter ); if( hr != S_OK || pAnchorAfter == NULL ) { TraceInteropHR( hr, TEXT("IAnchor::Clone failed") ); return; }
LONG cchShifted = 0; hr = pAnchorAfter->Shift( 1, & cchShifted, NULL ); if( hr != S_OK || cchShifted != 1 ) { TraceInteropHR( hr, TEXT("IAnchor::Shift failed?") ); return; }
pSink->OnTextChange( dwFlags, paPos, pAnchorAfter );
pAnchorAfter->Release(); } */
public:
CDocWrapAnchor() : m_cMaxAttrs(0), m_cAttrsTAP(0), m_iAttrsTAP(0), m_cAttrsTAPSize(0), m_paAttrsTAP(NULL), m_paAttrsSupported(NULL)
{
}
~CDocWrapAnchor() { ResetAttrs(); if ( m_paAttrsTAP ) { delete [] m_paAttrsTAP; m_paAttrsTAP = NULL; } if ( m_paAttrsSupported ) { delete [] m_paAttrsSupported; m_paAttrsSupported = NULL; } }
// ITextStoreAnchor
DocWrap_FORWARD_READLOCK( GetSelection, 4, ( ULONG, ulIndex, ULONG, ulCount, TS_SELECTION_ANCHOR *, pSelection, ULONG *, pcFetched ) ) DocWrap_FORWARD_READLOCK( GetText, 7, ( DWORD, dwFlags, IAnchor *, paStart, IAnchor *, paEnd, WCHAR *, pchText, ULONG, cchReq, ULONG *, pcch, BOOL, fUpdateAnchor ) ) DocWrap_FORWARD_READLOCK( GetEmbedded, 5, ( DWORD, dwFlags, IAnchor *, Pos, REFGUID, rguidService, REFIID, riid, IUnknown **, ppunk ) ) DocWrap_FORWARD_READLOCK( GetStart, 1, ( IAnchor **, ppaStart ) ) DocWrap_FORWARD_READLOCK( GetEnd, 1, ( IAnchor **, ppaEnd ) ) DocWrap_FORWARD_READLOCK( GetAnchorFromPoint, 4, ( TsViewCookie, vcView, const POINT *, ptScreen, DWORD, dwFlags, IAnchor **, ppaSite ) ) // DocWrap_FORWARD( RequestSupportedAttrs, 3, ( DWORD, dwFlags,
// ULONG, cFilterAttrs,
// const TS_ATTRID *, paFilterAttrs ) )
// DocWrap_FORWARD_READLOCK( RequestAttrsAtPosition, 4, ( DocTraits::PosType, Pos,
// ULONG, cFilterAttrs,
// const TS_ATTRID *, paFilterAttrs,
// DWORD, dwFlags ) )
// DocWrap_FORWARD_READLOCK( RequestAttrsTransitioningAtPosition,
// 4, ( DocTraits::PosType, Pos,
// ULONG, cFilterAttrs,
// const TS_ATTRID *, paFilterAttrs,
// DWORD, dwFlags ) )
// DocWrap_FORWARD_READLOCK( FindNextAttrTransition, 7, ( IAnchor *, paStart, IAnchor *, paEnd, ULONG, cFilterAttrs, const TS_ATTRID *, paFilterAttrs, DWORD, dwFlags, BOOL *, pfFound, LONG *, plFoundOffset ) )
DocWrap_FORWARD_AND_SINK( SetSelection, 2, ( ULONG, ulCount, const TS_SELECTION_ANCHOR *, pSelection ), TS_AS_SEL_CHANGE, pSink->OnSelectionChange() )
DocWrap_FORWARD_AND_SINK( SetText, 5, ( DWORD, dwFlags, IAnchor *, paStart, IAnchor *, paEnd, const WCHAR *, pchText, ULONG, cch ), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, paStart, paEnd ) )
DocWrap_FORWARD_AND_SINK( InsertEmbedded, 4, ( DWORD, dwFlags, IAnchor *, paStart, IAnchor *, paEnd, IDataObject *, pDataObject ), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, paStart, paEnd ) )
DocWrap_FORWARD_AND_SINK( InsertTextAtSelection, 5, ( DWORD, dwFlags, const WCHAR *, pchText, ULONG, cch, IAnchor **, ppaStart, IAnchor **, ppaEnd ), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, *ppaStart, *ppaEnd ) )
DocWrap_FORWARD_AND_SINK( InsertEmbeddedAtSelection, 4, ( DWORD, dwFlags, IDataObject *, pDataObject, IAnchor **, ppaStart, IAnchor **, ppaEnd ), TS_AS_TEXT_CHANGE, pSink->OnTextChange( dwFlags, *ppaStart, *ppaEnd ) )
HRESULT STDMETHODCALLTYPE RequestSupportedAttrs ( DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID * paFilterAttrs ) { IMETHOD( RequestSupportedAttrs ); ResetAttrs(); return m_pDoc->RequestSupportedAttrs( dwFlags, cFilterAttrs, paFilterAttrs ) ; } HRESULT STDMETHODCALLTYPE RequestAttrsAtPosition ( DocTraits::PosType Pos, ULONG cFilterAttrs, const TS_ATTRID * paFilterAttrs, DWORD dwFlags ) { IMETHOD( RequestAttrsAtPosition ); if ( VerifyLock( TS_LF_READ ) == S_FALSE ) return TS_E_NOLOCK; ResetAttrs(); return m_pDoc->RequestAttrsAtPosition( Pos, cFilterAttrs, paFilterAttrs, dwFlags ) ; }
HRESULT STDMETHODCALLTYPE RequestAttrsTransitioningAtPosition ( IAnchor * paStart, ULONG cFilterAttrs, const TS_ATTRID * paFilterAttrs, DWORD dwFlags ) { IMETHOD( RequestAttrsTransitioningAtPosition ); if ( VerifyLock( TS_LF_READ ) == S_FALSE ) return TS_E_NOLOCK; ResetAttrs();
// call through to the doc
HRESULT hr = m_pDoc->RequestAttrsTransitioningAtPosition( paStart, cFilterAttrs, paFilterAttrs, dwFlags ); if ( hr != E_NOTIMPL ) return hr; // if the server does not support this do it ourselves
// make sure there is really something to do
if ( paStart == NULL ) return S_OK; // make sure we can hold the attributes we find
hr = AllocateAttrs( cFilterAttrs ); if ( FAILED(hr) ) return hr;
ULONG cAlloAttrs = cFilterAttrs ? cFilterAttrs : m_cMaxAttrs;
TS_ATTRVAL * paCurrent = reinterpret_cast<TS_ATTRVAL *>( alloca( sizeof( TS_ATTRVAL ) * cAlloAttrs ) ); if ( !paCurrent ) return E_OUTOFMEMORY;
hr = GetAttr ( paStart, cFilterAttrs, paFilterAttrs, dwFlags, paCurrent); if ( FAILED(hr) ) return hr;
LONG cchShifted; ULONG iAttrs = 0; TS_ATTRVAL * paComp = NULL; CComPtr <IAnchor> paPos; paStart->Clone( &paPos ); hr = paPos->Shift( 0, -1, &cchShifted, NULL ); // TODO fix hidden text
if ( SUCCEEDED(hr) && cchShifted == -1 ) { paComp = reinterpret_cast<TS_ATTRVAL *>( alloca( sizeof( TS_ATTRVAL ) * cAlloAttrs ) ); if ( !paComp ) return E_OUTOFMEMORY; hr = GetAttr ( paPos, cFilterAttrs, paFilterAttrs, dwFlags, paComp); if ( FAILED(hr) ) return hr; if ( dwFlags & TS_ATTR_FIND_WANT_END ) CompareAttrs ( paCurrent, paComp, cAlloAttrs, iAttrs, TRUE ); else CompareAttrs ( paComp, paCurrent, cAlloAttrs, iAttrs, TRUE ); }
if ( !( dwFlags & TS_ATTR_FIND_WANT_VALUE ) ) { for ( int i= 0; i < cFilterAttrs; i++ ) { VariantClear( &m_paAttrsTAP[i].varValue ); } }
m_cAttrsTAP = iAttrs; return S_OK; }
HRESULT STDMETHODCALLTYPE FindNextAttrTransition ( IAnchor * paStart, IAnchor * paEnd, ULONG cFilterAttrs, const TS_ATTRID * paFilterAttrs, DWORD dwFlags, BOOL * pfFound, LONG * plFoundOffset ) { IMETHOD( FindNextAttrTransition ); if ( VerifyLock( TS_LF_READ ) == S_FALSE ) return TS_E_NOLOCK; HRESULT hr = m_pDoc->FindNextAttrTransition( paStart, paEnd, cFilterAttrs, paFilterAttrs, dwFlags, pfFound, plFoundOffset ); if ( hr != E_NOTIMPL ) return hr;
*pfFound = FALSE; *plFoundOffset = 0; // if the server does not support this do it ourselves
// make sure there is really something to do
if ( paStart == NULL ) return S_OK;
// make sure we can hold the attributes we find
hr = AllocateAttrs( cFilterAttrs ); if ( FAILED(hr) ) { TraceDebugHR( hr, TEXT("AllocateAttrs failed ") ); return hr; } ULONG cAlloAttrs = cFilterAttrs ? cFilterAttrs : m_cMaxAttrs; TS_ATTRVAL * paCurrent = reinterpret_cast<TS_ATTRVAL *>( alloca( sizeof( TS_ATTRVAL ) * cAlloAttrs ) ); TS_ATTRVAL * paNext = reinterpret_cast<TS_ATTRVAL *>( alloca( sizeof( TS_ATTRVAL ) * cAlloAttrs ) ); if ( !paCurrent || !paNext ) return E_OUTOFMEMORY;
hr = GetAttr ( paStart, cFilterAttrs, paFilterAttrs, dwFlags, paCurrent); if ( FAILED(hr) ) { TraceDebugHR( hr, TEXT("Current GetAttr failed ") ); return hr; }
LONG cchShifted; ULONG iAttrs = 0; BOOL fDone = TRUE; const LONG cchShift = ( dwFlags & TS_ATTR_FIND_BACKWARDS ) ? -1 : 1; CComPtr <IAnchor> paPos, paEndOfDoc; hr = paStart->Clone( &paPos ); if ( FAILED(hr) ) { TraceDebugHR( hr, TEXT("Clone failed ") ); return hr; } if ( paEnd == NULL ) { if ( dwFlags & TS_ATTR_FIND_BACKWARDS ) { m_pDoc->GetStart( &paEndOfDoc ); } else { BOOL fRegion = FALSE; hr = paStart->Clone( &paEndOfDoc ); while ( fRegion ) { paEndOfDoc->Shift( 0, LONG_MAX, &cchShifted, NULL ); paEndOfDoc->ShiftRegion( 0, TS_SD_FORWARD, &fRegion ); } } } while ( iAttrs == 0 ) { hr = paPos->Shift( 0, cchShift, &cchShifted, NULL ); // TODO fix hidden text
if ( SUCCEEDED(hr) && cchShifted == cchShift ) { *plFoundOffset += 1; hr = paPos->IsEqual( paEnd ? paEnd : paEndOfDoc, &fDone ); if ( FAILED(hr) ) { TraceDebugHR( hr, TEXT("IsEqual failed ") ); return hr; } if ( fDone ) break; hr = GetAttr ( paPos, cFilterAttrs, paFilterAttrs, dwFlags, paNext); if ( FAILED(hr) ) { TraceDebugHR( hr, TEXT("Next GetAttr failed ") ); return hr; } CompareAttrs ( paCurrent, paNext, cFilterAttrs, iAttrs, FALSE ); if ( iAttrs ) { *pfFound = TRUE; } } else { TraceDebugHR( hr, TEXT("Shift failed ") ); return hr; } }
return S_OK; } HRESULT STDMETHODCALLTYPE RetrieveRequestedAttrs ( ULONG ulCount, TS_ATTRVAL * paAttrVals, ULONG * pcFetched ) { // if there is no outstanding requests we satisfy then call through to the doc
if ( m_cAttrsTAP == 0 ) return m_pDoc->RetrieveRequestedAttrs( ulCount, paAttrVals, pcFetched ); if ( ( m_cAttrsTAP - m_iAttrsTAP ) < ulCount ) *pcFetched = m_cAttrsTAP - m_iAttrsTAP; else *pcFetched = ulCount; memcpy(paAttrVals, &m_paAttrsTAP[m_iAttrsTAP], *pcFetched * sizeof(TS_ATTRVAL)); memset(&m_paAttrsTAP[m_iAttrsTAP], 0, *pcFetched * sizeof(TS_ATTRVAL)); m_iAttrsTAP += *pcFetched;
if ( m_iAttrsTAP == m_cAttrsTAP ) ResetAttrs(); return S_OK; } private: ULONG m_cMaxAttrs; ULONG m_iAttrsTAP; ULONG m_cAttrsTAP; ULONG m_cAttrsTAPSize; TS_ATTRVAL * m_paAttrsTAP; TS_ATTRID * m_paAttrsSupported;
private:
HRESULT STDMETHODCALLTYPE CompareAttrs ( TS_ATTRVAL * paAttr1, TS_ATTRVAL * paAttr2, ULONG cAttrs, ULONG &iAttrs, BOOL fCopy) { cAttrs = cAttrs ? cAttrs : m_cMaxAttrs; for ( int i = 0; i < cAttrs; i++ ) { for ( int j = 0; j < cAttrs; j++ ) { if ( paAttr1[i].idAttr == paAttr2[j].idAttr ) { if ( CComVariant( paAttr1[i].varValue ) != CComVariant( paAttr2[j].varValue ) ) { if ( fCopy ) { char * cBuf = ( char * )&m_paAttrsTAP[iAttrs]; memcpy( cBuf, ( char * )&paAttr2[j], sizeof(TS_ATTRVAL) ); } iAttrs++; } break; } } }
return S_OK; }
HRESULT STDMETHODCALLTYPE GetAttr ( IAnchor * paStart, ULONG cFilterAttrs, const TS_ATTRID * paFilterAttrs, DWORD dwFlags, TS_ATTRVAL * paAttrVals) { ULONG cFetched; HRESULT hr; ULONG cAlloAttrs = cFilterAttrs ? cFilterAttrs : m_cMaxAttrs; const TS_ATTRID * paActualFilterAttrs = paFilterAttrs ? paFilterAttrs : m_paAttrsSupported;
hr = m_pDoc->RequestAttrsAtPosition( paStart, cAlloAttrs, paActualFilterAttrs, 0 ); if ( FAILED(hr) ) return hr;
hr = m_pDoc->RetrieveRequestedAttrs( cAlloAttrs, paAttrVals, &cFetched ); if ( FAILED(hr) ) return hr;
return S_OK;
}
HRESULT STDMETHODCALLTYPE AllocateAttrs ( ULONG cFilterAttrs ) { if ( cFilterAttrs == 0 && m_cMaxAttrs == 0 ) { const LONG cAttrs = 512; HRESULT hr = m_pDoc->RequestSupportedAttrs( 0, 0, NULL ); if ( FAILED(hr) ) return hr;
TS_ATTRVAL * paSupported = new TS_ATTRVAL[ cAttrs ]; if ( !paSupported ) return E_OUTOFMEMORY; hr = m_pDoc->RetrieveRequestedAttrs( cAttrs, paSupported, &m_cMaxAttrs ); if ( SUCCEEDED(hr) ) { m_paAttrsSupported = new TS_ATTRID[ m_cMaxAttrs ]; if ( m_paAttrsSupported ) { for ( int i = 0; i < m_cMaxAttrs; i++ ) { m_paAttrsSupported[i] = paSupported[i].idAttr; } } } delete [] paSupported;
if ( FAILED(hr) ) return hr; if ( !m_paAttrsSupported ) return E_OUTOFMEMORY; }
ULONG cAlloAttrs = cFilterAttrs ? cFilterAttrs : m_cMaxAttrs; if ( m_cAttrsTAPSize < cAlloAttrs ) { if ( m_paAttrsTAP ) delete [] m_paAttrsTAP;
m_paAttrsTAP = new TS_ATTRVAL[ cAlloAttrs ]; if ( !m_paAttrsTAP ) { m_cAttrsTAPSize = 0; return E_OUTOFMEMORY; } m_cAttrsTAPSize = cAlloAttrs; } return S_OK; }
void ResetAttrs() { m_cAttrsTAP = 0; m_iAttrsTAP = 0; } };
/* 12e53b1b-7d7f-40bd-8f88-4603ee40cf58 */ const IID IID_PRIV_CINPUTCONTEXT = { 0x12e53b1b, 0x7d7f, 0x40bd, {0x8f, 0x88, 0x46, 0x03, 0xee, 0x40, 0xcf, 0x58} };
/* aabf7f9a-4487-4b2e-8164-e54c5fe19204 */ const GUID GUID_SERVICE_CTF = { 0xaabf7f9a, 0x4487, 0x4b2e, {0x81, 0x64, 0xe5, 0x4c, 0x5f, 0xe1, 0x92, 0x04} };
//
// CDocSinkWrapBase
//
// - Base from which Anchor and ACP sink wrappers are derived.
//
// This class contains ACP/Anchor-neutral wrapping code - anything that is
// ACP/Anchor-specific is handled in the derived ..ACP or ...Anchor class
// instead.
//
// Since this class is the sink for the wrapper, it derives from the full Cicero
// sink (DocTraits::ICicSink - which is a typedef for ITfTextStoreSink[Anchor]),
// and that in turn includes the ITextStoreACP sink.
//
template < class _DocTraits > class ATL_NO_VTABLE CSinkWrapBase : public CComObjectRootEx<CComSingleThreadModel>, public _DocTraits::ISink, public _DocTraits::ICicSink, public IServiceProvider { public:
// This typedef makes the DocTraits type visible in this and in the
// Anchor/ACP-specific derived classes. (Otherwise, as a template
// parameter in this class, it would not be available to them.)
typedef _DocTraits DocTraits;
DECLARE_PROTECT_FINAL_CONSTRUCT()
BEGIN_COM_MAP( CSinkWrapBase<DocTraits> ) COM_INTERFACE_ENTRY( DocTraits::ISink ) COM_INTERFACE_ENTRY( DocTraits::ICicSink ) COM_INTERFACE_ENTRY( IServiceProvider ) END_COM_MAP()
/*
static HRESULT InternalQueryInterface( void* pThis, const _ATL_INTMAP_ENTRY* pEntries, REFIID iid, void** ppvObject ) { // Hack for cicero - they expect to be able to QI for IID_PRIV_CINPUTCONTEXT and get
// a ptr to one of ther internal class types (ick!)
// This breaks COM identity, but hey, it's a private IID, and is only ever used
// locally.
// We can't do that sort of goo using the interface map (above), so have to hijack InternalQI instead.
if( iid == IID_PRIV_CINPUTCONTEXT ) { CSinkWrapBase<DocTraits> * pTHIS = (CSinkWrapBase<DocTraits> *)pThis;
// Look for the cicero sink...
// recognize it by the iid - it will have reg'd with a ITf (not TextStore) IID...
for( Iter_dl< DocTraits::CDocWrap > i ( pTHIS->m_pMgr->m_DocWraps ) ; ! i.AtEnd(); i++ ) { if( i->m_Sink.m_pSink && i->m_Sink.m_iid == __uuidof( DocTraits::ICicSink ) ) { return i->m_Sink.m_pSink->QueryInterface( iid, ppvObject ); } }
return E_NOINTERFACE; }
return CComObjectRootEx<CComSingleThreadModel>::InternalQueryInterface( pThis, pEntries, iid, ppvObject ); } */ protected:
// Protected stuff - used in this class, and by the ACP/Anchor-specific
// derived classes.
// Link back to the wrap manager. Used to tell it when we are going
// away...
CWrapMgr< DocTraits > * m_pMgr;
// Ptr to the manager's list of docs (which contain sinks)...
List_dl< DocTraits::CDocWrap > * m_pDocs;
// This macro forwards a call by iterating all the sinks in the manager,
// and forwarding if their mask has the right bit set.
// Note - this CANNOT be used for OnLockGranted.
#define CSinkWrap_FORWARD( mask, fname, c, params ) /**/ \
HRESULT STDMETHODCALLTYPE fname AS_DECL( c, params )\ {\ IMETHOD( fname );\ Assert( m_pMgr && m_pDocs );\ m_pMgr->RemoveDeadDocs();\ CPrivateAddRef MyAddRef(m_pMgr->m_lIterationRefCount);\ for( Iter_dl < DocTraits::CDocWrap > i ( *m_pDocs ) ; ! i.AtEnd() ; i++ )\ {\ DocTraits::ISink * pSink = static_cast< DocTraits::ISink * >( i->m_Sink.m_pSink );\ DWORD dwMask = i->m_Sink.m_dwMask;\ if( pSink && ( mask == 0 || ( dwMask & mask ) ) )\ {\ pSink-> fname AS_CALL( c, params );\ }\ }\ return S_OK ;\ }
// This macro forwards Cicero's TextStoreSink calls - they're not really sinks since
// the return values are significant - true broadcast is not supported; only the
// first sink in the manager which supports the interface is used - and its
// return value gets returned.
#define CSinkWrap_FORWARD_CICERO( fname, c, params ) /**/ \
HRESULT STDMETHODCALLTYPE fname AS_DECL( c, params )\ {\ IMETHOD( fname );\ Assert( m_pMgr && m_pDocs );\ for( Iter_dl < DocTraits::CDocWrap > i ( *m_pDocs ) ; ! i.AtEnd() ; i++ )\ {\ if ( i->m_Sink.m_pSink )\ {\ CComPtr< DocTraits::ICicSink > pTheSink;\ HRESULT hr = i->m_Sink.m_pSink->QueryInterface( __uuidof(DocTraits::ICicSink), (void **)&pTheSink );\ if( hr == S_OK )\ {\ return pTheSink-> fname AS_CALL( c, params );\ }\ }\ }\ return E_FAIL ;\ }
public:
//
// Ctor, Dtor and initialization...
//
CSinkWrapBase() : m_pMgr( NULL ), m_pDocs( NULL ) { }
~CSinkWrapBase() { AssertMsg( m_pMgr != NULL && m_pDocs != NULL, TEXT("CSinkWrapBase::Init never got called?") ); m_pMgr->SinkWrap_NotifyDisconnect(); m_pMgr->Release(); }
void Init( CWrapMgr< DocTraits > * pMgr, List_dl< DocTraits::CDocWrap > * pDocs ) { AssertMsg( m_pMgr == NULL && m_pDocs == NULL, TEXT("CSinkWrapBase::Init should only be called once when m_pMgr is NULL") ); m_pMgr = pMgr; m_pMgr->AddRef();
m_pDocs = pDocs; }
//
// IServiceProvider - Cicero uses this to 'drill through' to the original anchor to pull out
// internal information. Just pass it through...
//
HRESULT STDMETHODCALLTYPE QueryService( REFGUID guidService, REFIID riid, void **ppvObject ) { IMETHOD( QueryService );
// Find the cicero sink...
DocTraits::ICicSink * pTheSink = NULL;
// Look for the cicero sink...
// The cicero sink supports the Services interfaces and other don't
for( Iter_dl< DocTraits::CDocWrap > i ( m_pMgr->m_DocWraps ) ; ! i.AtEnd(); i++ ) { if ( i->m_Sink.m_pSink ) { if( i->m_Sink.m_pSink->QueryInterface( __uuidof(DocTraits::ICicSink), (void **)&pTheSink ) == S_OK ) { break; } } }
if( pTheSink == NULL ) return E_FAIL;
CComPtr<IServiceProvider> pISP; HRESULT hr = pTheSink->QueryInterface( IID_IServiceProvider, (void **) & pISP ); if( hr != S_OK || pISP == NULL ) return E_FAIL;
hr = pISP->QueryService( guidService, riid, ppvObject );
return hr; }
//
// ACP/Anchor-neutral sinks - just broadcast these...
//
CSinkWrap_FORWARD( TS_AS_SEL_CHANGE, OnSelectionChange, 0, () ) CSinkWrap_FORWARD( TS_AS_LAYOUT_CHANGE, OnLayoutChange, 2, ( TsLayoutCode, lcode, TsViewCookie, vcView ) ) CSinkWrap_FORWARD( 0, OnStatusChange, 1, ( DWORD, dwFlags ) ) CSinkWrap_FORWARD( 0, OnStartEditTransaction,0, () ) CSinkWrap_FORWARD( 0, OnEndEditTransaction, 0, () )
//
// Special case for OnLockGranted...
// Handle single-client sync requests, and multiple queued async requests...
//
HRESULT STDMETHODCALLTYPE OnLockGranted ( DWORD dwLockFlags ) { IMETHOD( OnLockGranted );
Assert( m_pMgr ); return m_pMgr->SinkWrap_HandleOnLockGranted( dwLockFlags ); } };
//
// CSinkWrapACP
//
// - ACP sink wrapper
//
// Derived from the CSinkWrapBase, this adds ACP-specific methods; including
// those from both ITextStoreACP and the cicero-specific ITfTextStoreSink
// interfaces.
//
class ATL_NO_VTABLE CSinkWrapACP : public CSinkWrapBase< DocTraitsACP > {
public: //
// ITextStoreACPSink ACP/Anchor-specific methods - broadcast to all interested sinks...
// (See CSinkWrapBase for the forwarding macro.)
//
CSinkWrap_FORWARD( TS_AS_TEXT_CHANGE, OnTextChange, 2, ( DWORD, dwFlags, const TS_TEXTCHANGE *, pChange ) ) CSinkWrap_FORWARD( 0, OnAttrsChange, 4, ( LONG, acpStart, LONG, acpEnd, ULONG, cAttrs, const TS_ATTRID *, paAttrs ) )
//
// Cicero-specific sink methods - forward these to the first available sink that implements the cicero interface...
// (See CSinkWrapBase for the forwarding macro.)
//
CSinkWrap_FORWARD_CICERO( Serialize, 4, (ITfProperty *, pProp, ITfRange *, pRange, TF_PERSISTENT_PROPERTY_HEADER_ACP *, pHdr, IStream *, pStream) ) CSinkWrap_FORWARD_CICERO( Unserialize, 4, (ITfProperty *, pProp, const TF_PERSISTENT_PROPERTY_HEADER_ACP *, pHdr, IStream *, pStream, ITfPersistentPropertyLoaderACP *, pLoader) ) CSinkWrap_FORWARD_CICERO( ForceLoadProperty,1, (ITfProperty *, pProp) ) CSinkWrap_FORWARD_CICERO( CreateRange, 3, (LONG, acpStart, LONG, acpEnd, ITfRangeACP **, ppRange) )
};
//
// CSinkWrapAnchor
//
// - Anchor sink wrapper
//
// Derived from the CSinkWrapBase, this adds Anchor-specific methods; including
// those from both ITextStoreACP and the cicero-specific ITfTextStoreSink
// interfaces.
//
class ATL_NO_VTABLE CSinkWrapAnchor : public CSinkWrapBase< DocTraitsAnchor > {
public: //
// ITextStoreACPSink ACP/Anchor-specific methods - broadcast to all interested sinks...
// (See CSinkWrapBase for the forwarding macro.)
//
CSinkWrap_FORWARD( TS_AS_TEXT_CHANGE, OnTextChange, 3, ( DWORD, dwFlags, IAnchor *, paStart, IAnchor *, paEnd ) ) CSinkWrap_FORWARD( 0, OnAttrsChange, 4, ( IAnchor *, paStart, IAnchor *, paEnd, ULONG, cAttrs, const TS_ATTRID *, paAttrs ) )
//
// Cicero-specific sink methods - forward these to the first available sink that implements the cicero interface...
// (See CSinkWrapBase for the forwarding macro.)
//
CSinkWrap_FORWARD_CICERO( Serialize, 4, (ITfProperty *, pProp, ITfRange *, pRange, TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *, pHdr, IStream *, pStream) ) CSinkWrap_FORWARD_CICERO( Unserialize, 4, (ITfProperty *, pProp, const TF_PERSISTENT_PROPERTY_HEADER_ANCHOR *, pHdr, IStream *, pStream, ITfPersistentPropertyLoaderAnchor *, pLoader) ) CSinkWrap_FORWARD_CICERO( ForceLoadProperty,1, (ITfProperty *, pProp) ) CSinkWrap_FORWARD_CICERO( CreateRange, 3, (IAnchor *, paStart, IAnchor *, paEnd, ITfRangeAnchor **, ppRange) )
};
CDocWrap::CDocWrap() : m_punkDoc( NULL ), m_pWrapMgr( NULL ) { IMETHOD( CDocWrap ); // Done.
}
CDocWrap::~CDocWrap() { IMETHOD( ~CDocWrap );
_Clear(); }
HRESULT STDMETHODCALLTYPE CDocWrap::SetDoc( REFIID riid, IUnknown * pDocIn ) { IMETHOD( SetDoc );
_Clear();
if( pDocIn == NULL ) { TraceInfo( TEXT("CDocWrapp::SetDoc( NULL ) - doc cleared") ); return S_OK; }
HRESULT hr; if( riid == IID_ITextStoreACP || riid == IID_ITfTextStoreACP ) { CWrapMgr< DocTraitsACP > * pWrapMgrACP; hr = CreateLocalInstance( & pWrapMgrACP ); m_pWrapMgr = pWrapMgrACP; } else if( riid == IID_ITextStoreAnchor || riid == IID_ITfTextStoreAnchor ) { CWrapMgr< DocTraitsAnchor > * pWrapMgrAnchor; hr = CreateLocalInstance( & pWrapMgrAnchor ); m_pWrapMgr = pWrapMgrAnchor; } else { TraceParam( TEXT("CDocWrapp::SetDoc - given unknown IID") ); return E_NOINTERFACE; }
CHECK_HR_RETURN( hr, m_pWrapMgr != NULL );
if( hr != S_OK ) { TraceErrorHR( hr, TEXT("Couldn't create CWrapMgr") ); return FAILED( (hr) ) ? (hr) : E_UNEXPECTED; } if( hr == S_OK && ! m_pWrapMgr ) { TraceErrorHR( hr, TEXT("Couldn't create CWrapMgr") ); return E_UNEXPECTED; }
m_pWrapMgr->SetDoc( pDocIn ); m_iid = riid; m_punkDoc = pDocIn; m_punkDoc->AddRef();
TraceInfo( TEXT("CDocWrap::SetDoc - new doc set.") ); return S_OK; }
HRESULT STDMETHODCALLTYPE CDocWrap::GetWrappedDoc( REFIID riid, IUnknown ** pWrappedDocOut ) { IMETHOD( GetWrappedDoc );
if( ! m_punkDoc || ! m_pWrapMgr ) { TraceParam( TEXT("GetWrappedDoc called without prior successful call to SetDoc") ); return E_FAIL; } if( ! pWrappedDocOut ) { TraceParam( TEXT("GetWrappedDoc called without NULL pWrappedDocOut param") ); return E_POINTER; }
// Check that requested iid matches...
// We allow Doc/ITf mixes, provided the interfaces match ACP/Anchor-wise.
if( m_iid == IID_ITextStoreAnchor || m_iid == IID_ITfTextStoreAnchor ) { if( riid != IID_ITextStoreAnchor && riid != IID_ITfTextStoreAnchor ) { TraceParam( TEXT("Interface requested by GetWrappedDoc doesn't match that suplied by SetDoc") ); return E_NOINTERFACE; } } else { if( riid != IID_ITextStoreACP && riid != IID_ITfTextStoreACP ) { TraceParam( TEXT("Interface requested by GetWrappedDoc doesn't match that suplied by SetDoc") ); return E_NOINTERFACE; } }
TraceInfo( TEXT("GetWrappedDoc succeeded") );
return m_pWrapMgr->CreateWrappedDoc( pWrappedDocOut ); }
void CDocWrap::_Clear() { SafeReleaseClear( m_pWrapMgr ); SafeReleaseClear( m_punkDoc ); }
|