//----------------------------------------------------------------------------- // Microsoft OLE DB RowsetViewer // Copyright (C) 1994 - 1999 By Microsoft Corporation. // // @doc // // @module CBASE.CPP // //----------------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////// #include "Headers.h" ///////////////////////////////////////////////////////////////// // CBase::CBase // ///////////////////////////////////////////////////////////////// CBase::CBase(SOURCE eObjectType, CMainWindow* pCMainWindow, CMDIChild* pCMDIChild) { //IUnknown m_cRef = 1; //BackPointers ASSERT(pCMainWindow || pCMDIChild); m_pCMDIChild = pCMDIChild; m_pCMainWindow = pCMainWindow ? pCMainWindow : pCMDIChild->m_pCMainWindow; //Common OLE DB Interfaces m_pIUnknown = NULL; m_pISupportErrorInfo = NULL; m_pIAggregate = NULL; m_pIService = NULL; //Data m_hTreeItem = NULL; m_eObjectType = eObjectType; m_eBaseClass = eCBase; m_dwCLSCTX = CLSCTX_INPROC_SERVER; //Parent Info m_pCParent = NULL; m_guidSource = GUID_NULL; } ///////////////////////////////////////////////////////////////// // CBase::~CBase // ///////////////////////////////////////////////////////////////// CBase::~CBase() { //ReleaseObject should have already been called... //If you hit this ASSERT put a "ReleaseObject(0)" in the obhjects destructor... //NOTE: We can't just call ReleaseObject here since its a virtual function. //Calling a virtual function would invoke the derived class, which when we are here //(in the base destructor) the derived class is already garbaged! ASSERT(m_pIUnknown == NULL); ASSERT(m_pCParent == NULL); //Make sure this item is removed from the tree... CObjTree* pCObjTree = m_pCMainWindow->m_pCMDIObjects->m_pCObjTree; if(pCObjTree && m_hTreeItem) { //NOTE: The object (after this desctructor) is no longer available, //so make sure that even if the node cannot be removed (child nodes), //we need to still remove the object reference so it doesn't try and access it anymore... pCObjTree->RemoveObject(this); pCObjTree->SetItemParam(m_hTreeItem, NULL); } } ///////////////////////////////////////////////////////////////// // CBase::AddRef // ///////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CBase::AddRef() { //AddRef return ++m_cRef; } ///////////////////////////////////////////////////////////////// // CBase::Release // ///////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CBase::Release() { //NOTE: The only thing that we have to be careful with is that our objects are refcounted, //but they contain a pointer to the window, which might have gone away or be going away. //So if the window is no longer valid, make sure we NULL out our window pointer... if(m_pCMDIChild && !m_pCMDIChild->m_hWnd) m_pCMDIChild = NULL; //Release if(--m_cRef) return m_cRef; delete this; return 0; } ///////////////////////////////////////////////////////////////// // CBase::QueryInterface // ///////////////////////////////////////////////////////////////// STDMETHODIMP CBase::QueryInterface(REFIID riid, LPVOID *ppv) { if(!ppv) return E_INVALIDARG; *ppv = NULL; //IUNKNOWN if(riid == IID_IUnknown) *ppv = this; if(*ppv) { ((IUnknown*)(*ppv))->AddRef(); return S_OK; } return E_NOINTERFACE; } ///////////////////////////////////////////////////////////////// // CBase::ObjectAddRef // ///////////////////////////////////////////////////////////////// ULONG CBase::ObjectAddRef() { //Record the number of times the user has addref'd this object. //So we can know when to correctly NULL out the interface, so they can longer use it... AddRef(); //IUnknown::AddRef return TRACE_ADDREF(m_pIUnknown, GetObjectName()); } ///////////////////////////////////////////////////////////////// // CBase::ObjectRelease // ///////////////////////////////////////////////////////////////// ULONG CBase::ObjectRelease() { IUnknown* pIUnknown = m_pIUnknown; ULONG ulRefCount = 0; //IUnknown::Release ulRefCount = CIntTrace::TraceRelease(&pIUnknown, GetObjectName()); //We need to disable this interface once it hits zero so //its not used incorrectly after the object is released... if(ulRefCount == 0 || m_cRef<=1) { m_pIUnknown = NULL; ReleaseObject(); } else { Release(); } return ulRefCount; } ///////////////////////////////////////////////////////////////// // HRESULT CBase::SetInterface // ///////////////////////////////////////////////////////////////// HRESULT CBase::SetInterface(REFIID riid, IUnknown* pIUnknown) { //We need to put the obtained interface in the appropiate member... if(pIUnknown) { //First obtain the correct interface member variable... IUnknown** ppInterfaceMember = GetInterfaceAddress(riid); if(!ppInterfaceMember) return E_NOINTERFACE; TRACE_RELEASE(*ppInterfaceMember, GetObjectName()); TRACE_ADDREF(pIUnknown, GetObjectName()); *ppInterfaceMember = pIUnknown; } return S_OK; } ///////////////////////////////////////////////////////////////// // HRESULT CBase::GetInterface // ///////////////////////////////////////////////////////////////// IUnknown* CBase::GetInterface(REFIID riid) { //Delegate to the derived class... IUnknown** ppIUnknown = GetInterfaceAddress(riid); if(ppIUnknown) return *ppIUnknown; return NULL; } ///////////////////////////////////////////////////////////////// // HRESULT CBase::ObjectQI // ///////////////////////////////////////////////////////////////// HRESULT CBase::ObjectQI(REFIID riid, IUnknown** ppIUnknown) { HRESULT hr = S_OK; //IUnknown::QueryInterface XTESTC(hr = TRACE_QI(m_pIUnknown, riid, ppIUnknown, GetObjectName())); //We need to put the obtained interface in the appropiate member... if(ppIUnknown && *ppIUnknown) hr = SetInterface(riid, *ppIUnknown); CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // CBase::ReleaseObject // ///////////////////////////////////////////////////////////////// HRESULT CBase::ReleaseObject(ULONG ulExpectedRefCount) { //Release Derived interfaces first... AutoRelease(); //IUnknown TRACE_RELEASE_(m_pIUnknown, L"IUnknown", ulExpectedRefCount); //Cleanup when there are no more references if(ulExpectedRefCount==0) { //Remove this object from the tree, unless there is a window associated with it m_pCMainWindow->m_pCMDIObjects->m_pCObjTree->RemoveObject(this); m_guidSource = GUID_NULL; //Release Parent SAFE_RELEASE(m_pCParent); } return S_OK; } ///////////////////////////////////////////////////////////////// // CBase::ReleaseChildren // ///////////////////////////////////////////////////////////////// HRESULT CBase::ReleaseChildren() { //Make our lives easier... CObjTree* pCObjTree = m_pCMainWindow->m_pCMDIObjects->m_pCObjTree; //No-op if(!m_hTreeItem || !pCObjTree->m_hWnd) return S_OK; BOOL bAllRemoved = TRUE; //Try to obtain the first child... HTREEITEM hChildItem = pCObjTree->GetChildItem(m_hTreeItem); while(hChildItem) { //NOTE: Before deleting this node of the tree, obtain the //next sibling (if there is one...). Since we can't do this after the node has been deleted! HTREEITEM hNextSibling = pCObjTree->GetNextItem(hChildItem); CBase* pCChild = (CBase*)pCObjTree->GetItemParam(hChildItem); if(pCChild) { //Make sure all its children are released (recurse) //If this child cannot be released, we still can continue on to the next child //(ie: release everything we can - not all or nothing...) if(SUCCEEDED(pCChild->ReleaseChildren())) { //Now we can release this child pCChild->ReleaseObject(); } } //Go to the next sibling... hChildItem = hNextSibling; } return bAllRemoved ? S_OK : E_FAIL; } ///////////////////////////////////////////////////////////////// // HRESULT CBase::CreateObject // ///////////////////////////////////////////////////////////////// HRESULT CBase::CreateObject(CBase* pCSource, REFIID riid, IUnknown* pIUnkObject, DWORD dwCreateOpts) { //No-op... if(!pIUnkObject) return E_INVALIDARG; //Use exsiting Connection IUnknown* pIUnknown = NULL; if(dwCreateOpts == -1 /*Default*/) dwCreateOpts = GetOptions()->m_dwCreateOpts; HRESULT hr = S_OK; //Store the Parent Object... SAFE_RELEASE(m_pCParent); SAFE_ADDREF(pCSource); m_pCParent = pCSource; //First we QI for IID_IUnknown the object passed in. //NOTE: We do this before we call ReleaseObject since the caller of this //method could have passed in our own member variable (m_pIUnknown) to "recreate" the object. TRACE_QI(pIUnkObject, IID_IUnknown, &pIUnknown, GetObjectName()); //Release all Previous interfaces... ReleaseObject(1); //[MANDATORY] //Used the AddRef'd input as our IUnknown, since all interfaces inherit from IUnknown. m_pIUnknown = pIUnknown; //Now set the pointer pased into the appropiate interface member //NOTE: We already handled IUnknown above... if(riid != IID_IUnknown) SetInterface(riid, pIUnkObject); //Now AutoQI for derived object interfaces... AutoQI(dwCreateOpts); //Add this object to the Objects Window m_pCMainWindow->m_pCMDIObjects->m_pCObjTree->AddObject(pCSource, this); //CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CBase::AutoRelease // ///////////////////////////////////////////////////////////////// HRESULT CBase::AutoRelease() { //Common OLE DB Interface RELEASE_INTERFACE(ISupportErrorInfo); RELEASE_INTERFACE(IAggregate); RELEASE_INTERFACE(IService); return S_OK; } ///////////////////////////////////////////////////////////////// // HRESULT CBase::AutoQI // ///////////////////////////////////////////////////////////////// HRESULT CBase::AutoQI(DWORD dwCreateOpts) { //[MANDATORY] Obtain [mandatory] interfaces if(dwCreateOpts & CREATE_QI_MANDATORY) { } //[OPTIONAL] if(dwCreateOpts & CREATE_QI_OPTIONAL) { OBTAIN_INTERFACE(ISupportErrorInfo); OBTAIN_INTERFACE(IAggregate); OBTAIN_INTERFACE(IService); } return S_OK; } ///////////////////////////////////////////////////////////////// // IUnknown** CBase::GetInterfaceAddress // ///////////////////////////////////////////////////////////////// IUnknown** CBase::GetInterfaceAddress(REFIID riid) { HANDLE_GETINTERFACE(IUnknown); HANDLE_GETINTERFACE(ISupportErrorInfo); HANDLE_GETINTERFACE(IAggregate); HANDLE_GETINTERFACE(IService); return NULL; } ///////////////////////////////////////////////////////////////// // CBase::IsSameObject // ///////////////////////////////////////////////////////////////// BOOL CBase::IsSameObject(IUnknown* pIUnkObject) { CComPtr spUnknown = m_pIUnknown; return spUnknown.IsEqualObject(pIUnkObject); } ///////////////////////////////////////////////////////////////// // CBase::GetParent // ///////////////////////////////////////////////////////////////// CBase* CBase::GetParent(SOURCE eSource) { CBase* pCParent = m_pCParent; //Try to find the requested parent object type while(pCParent) { //Do we have a match... if(pCParent->m_eObjectType == eSource) return pCParent; //Try the previous parent... pCParent = pCParent->m_pCParent; } return NULL; } //////////////////////////////////////////////////////////////// // CBase::SetObjectDesc // ///////////////////////////////////////////////////////////////// void CBase::SetObjectDesc(WCHAR* pwszObjectDesc, BOOL fCopy) { //Optmization: If the caller no longer needs the name, //no sense in reallocing, copying, freeing, freeing. Just reference it... if(fCopy) { m_strObjectDesc.CopyFrom(pwszObjectDesc); } else { m_strObjectDesc.Attach(pwszObjectDesc); } //Update the object in the tree... if(m_strObjectDesc && m_hTreeItem) m_pCMainWindow->m_pCMDIObjects->m_pCObjTree->AddObject(NULL, this); } //////////////////////////////////////////////////////////////// // CBase::OnDefOperation // ///////////////////////////////////////////////////////////////// void CBase::OnDefOperation() { //The default implementation (unless its overridden) //Activate the MDIChild window assoicated with this object if(m_pCMDIChild && m_pCMDIChild->m_hWnd) m_pCMainWindow->MDIActivate(m_pCMDIChild->m_hWnd); } //////////////////////////////////////////////////////////////// // CBase::DisplayObject // ///////////////////////////////////////////////////////////////// HRESULT CBase::DisplayObject() { //Update the object in the tree... if(m_hTreeItem) m_pCMainWindow->m_pCMDIObjects->m_pCObjTree->AddObject(NULL, this); return S_OK; } //////////////////////////////////////////////////////////////// // CBase::GetOptions // ///////////////////////////////////////////////////////////////// COptionsSheet* CBase::GetOptions() { return m_pCMainWindow->GetOptions(); } // {CB21F4D6-878D-11d1-9528-00C04FB66A50} static const IID IID_IAggregate = { 0xcb21f4d6, 0x878d, 0x11d1, { 0x95, 0x28, 0x0, 0xc0, 0x4f, 0xb6, 0x6a, 0x50 } }; /////////////////////////////////////////////////////////////////////////////// // CAggregate // /////////////////////////////////////////////////////////////////////////////// CAggregate::CAggregate() { m_cRef = 1; } /////////////////////////////////////////////////////////////////////////////// // ~CAggregate // /////////////////////////////////////////////////////////////////////////////// CAggregate::~CAggregate() { //COM Aggregation rule #6 //To free an inner pointer (other than IUnknown), the outer object calls its //own outer unknown's AddRef followed by Release on the inner object's pointer //Currently we don't have this case, since we only have IUnknown //AddRef() //SAFE_RELEASE(m_pNonIUnknownInner); //Inner object free ReleaseInner(); } ///////////////////////////////////////////////////////////////////////////// // CAggregate::SetInner // ///////////////////////////////////////////////////////////////////////////// HRESULT CAggregate::SetInner(IUnknown* pIUnkInner) { if(!pIUnkInner) return E_INVALIDARG; TRACE_RELEASE(m_spUnkInner.p, L"IUnknown"); return TRACE_QI(pIUnkInner, IID_IUnknown, (IUnknown**)&m_spUnkInner); } ///////////////////////////////////////////////////////////////////////////// // CAggregate::ReleaseInner // ///////////////////////////////////////////////////////////////////////////// HRESULT CAggregate::ReleaseInner() { //Only release the inner if the RefCount of the outer has gone to its //orginal refcount if(m_cRef <= 1) { TRACE_RELEASE(m_spUnkInner.p, L"IUnknown"); return S_OK; } return S_FALSE; } ///////////////////////////////////////////////////////////////////////////// // CAggregate::HandleAggregation // ///////////////////////////////////////////////////////////////////////////// HRESULT CAggregate::HandleAggregation(REFIID riid, IUnknown** ppIUnknown) { HRESULT hr = S_OK; IUnknown* pIUnkInner = ppIUnknown ? *ppIUnknown : NULL; if(pIUnkInner) { //This would be a bug in the provider if aggregaiton succeeded //but the user didn't request IID_IUnknown. if(riid != IID_IUnknown) { //NOTE: We don't want to just continue here since this is dangerous. //If the provider succeeds, this means the outer object was probably returned rather //than the inner non-delegating IUnknown, this will cause our outer controlling object //to have a circular QI problem when asked for an inner interface, and will have refcounting //problems as well. Might as well let the use know, and don't further this provider bug... if(IDNO == wMessageBox ( GetFocus(), MB_TASKMODAL | MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON1, wsz_ERROR, L"Provider Bug: Allowed Aggregation and riid!=IID_IUnknown!\n\n" L"This may crash your Provider...\n" L"Do you wish to continue anyway?" )) { //return a failure if the user wished not to continue... TESTC(hr = E_INVALIDARG); } } //We need to "hook" up our objects. So that our outer controlling object //has a pointer to the inner for delegating QI calls. This must be an non-delegating //inner IUnknown, (see below). Also the caller of this function will ALWAYS //release their CAggregate object, and we return a the Aggregate object with our //reference count. (consistent with COM, calle never releases callers IUnknown, and //calle always addref's if they hold onto the callers object). if(SUCCEEDED(hr = SetInner(pIUnkInner))) { //SetInner TRACE_RELEASE(pIUnkInner, L"IUnknown"); hr = QueryInterface(IID_IUnknown, (void**)ppIUnknown); } } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////////////////// // CAggregate::AddRef // ///////////////////////////////////////////////////////////////////////////// ULONG CAggregate::AddRef(void) { return ++m_cRef; } ///////////////////////////////////////////////////////////////////////////// // CAggregate::Release // ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP_(ULONG) CAggregate::Release(void) { ASSERT(m_cRef); if(--m_cRef) return m_cRef; //COM Aggregation rule #5 //The outer object must protect its implementation of Release from //reentrantcy with an artifical reference count arround its destruction code m_cRef++; //Delete this object delete this; return 0; } ///////////////////////////////////////////////////////////////////////////// // CAggregate::QueryInterface // ///////////////////////////////////////////////////////////////////////////// HRESULT CAggregate::QueryInterface(REFIID riid, LPVOID *ppv) { HRESULT hr = S_OK; //TEST_ NULL if(ppv == NULL) return E_INVALIDARG; *ppv = NULL; //Support IID_IUnknown if(riid == IID_IUnknown) { *ppv = (IUnknown*)this; SAFE_ADDREF((IUnknown*)*ppv); } else if(riid == IID_IAggregate) { *ppv = (IUnknown*)this; SAFE_ADDREF((IUnknown*)*ppv); } else if(m_spUnkInner) { //Delegate the the Inner Object //This is not "circular" since this interface is the IID_IUnknown //interface only, which has its own non-delegating QI... hr = m_spUnkInner->QueryInterface(riid, ppv); } else { return E_NOINTERFACE; } return hr; } ///////////////////////////////////////////////////////////////// // CContainerBase::CContainerBase // ///////////////////////////////////////////////////////////////// CContainerBase::CContainerBase(SOURCE eObjectType, CMainWindow* pCMainWindow, CMDIChild* pCMDIChild) : CBase(eObjectType, pCMainWindow, pCMDIChild) { //eBaseClass m_eBaseClass = BASE_CLASS(m_eBaseClass | eCContainerBase); //Common OLE DB Interfaces m_pIConnectionPointContainer = NULL; } ///////////////////////////////////////////////////////////////// // CContainerBase::~CContainerBase // ///////////////////////////////////////////////////////////////// CContainerBase::~CContainerBase() { ReleaseObject(0); } ///////////////////////////////////////////////////////////////// // HRESULT CContainerBase::AutoRelease // ///////////////////////////////////////////////////////////////// HRESULT CContainerBase::AutoRelease() { //Common OLE DB Interface RELEASE_INTERFACE(IConnectionPointContainer); //Delegate return CBase::AutoRelease(); } ///////////////////////////////////////////////////////////////// // HRESULT CContainerBase::AutoQI // ///////////////////////////////////////////////////////////////// HRESULT CContainerBase::AutoQI(DWORD dwCreateOpts) { //Delegate First so we have base interfaces CBase::AutoQI(dwCreateOpts); //[MANDATORY] Obtain [mandatory] interfaces if(dwCreateOpts & CREATE_QI_MANDATORY) { } //[OPTIONAL] if(dwCreateOpts & CREATE_QI_OPTIONAL) { OBTAIN_INTERFACE(IConnectionPointContainer); } return S_OK; } ///////////////////////////////////////////////////////////////// // IUnknown** CContainerBase::GetInterfaceAddress // ///////////////////////////////////////////////////////////////// IUnknown** CContainerBase::GetInterfaceAddress(REFIID riid) { HANDLE_GETINTERFACE(IConnectionPointContainer); //Otherwise delegate return CBase::GetInterfaceAddress(riid); } ///////////////////////////////////////////////////////////////// // HRESULT CContainerBase::FindConnectionPoint // ///////////////////////////////////////////////////////////////// HRESULT CContainerBase::FindConnectionPoint(REFIID riid, IConnectionPoint** ppIConnectionPoint) { HRESULT hr = S_OK; if(m_pIConnectionPointContainer) { //IConnectionPointContainer::FindConnectionPoint hr = m_pIConnectionPointContainer->FindConnectionPoint(riid, ppIConnectionPoint); TESTC(TRACE_METHOD(hr, L"IConnectionPointContainer::FindConnectionPoint(%s, &0x%p)", GetInterfaceName(riid), ppIConnectionPoint ? *ppIConnectionPoint : NULL)); } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CContainerBase::AdviseListener // ///////////////////////////////////////////////////////////////// HRESULT CContainerBase::AdviseListener(REFIID riid, DWORD* pdwCookie) { HRESULT hr = S_OK; if(m_pIConnectionPointContainer) { if(riid == IID_IDBAsynchNotify) { if(GetOptions()->m_dwNotifyOpts & NOTIFY_IDBASYNCHNOTIFY) hr = m_pCMainWindow->m_pCListener->Advise(this, riid, pdwCookie); } else if(riid == IID_IRowsetNotify) { if(GetOptions()->m_dwNotifyOpts & NOTIFY_IROWSETNOTIFY) hr = m_pCMainWindow->m_pCListener->Advise(this, riid, pdwCookie); } else if(riid == IID_IRowPositionChange) { if(GetOptions()->m_dwNotifyOpts & NOTIFY_IROWPOSITIONCHANGE) hr = m_pCMainWindow->m_pCListener->Advise(this, riid, pdwCookie); } else { ASSERT(!"Unhandled Notification Type!"); } } return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CContainerBase::UnadviseListener // ///////////////////////////////////////////////////////////////// HRESULT CContainerBase::UnadviseListener(REFIID riid, DWORD* pdwCookie) { ASSERT(pdwCookie); HRESULT hr = S_OK; if(m_pIConnectionPointContainer) { if(*pdwCookie) hr = m_pCMainWindow->m_pCListener->Unadvise(this, riid, pdwCookie); } return hr; } ///////////////////////////////////////////////////////////////// // CConnectionPoint::CConnectionPoint // ///////////////////////////////////////////////////////////////// CConnectionPoint::CConnectionPoint(CMainWindow* pCMainWindow, CMDIChild* pCMDIChild) : CBase(eCConnectionPoint, pCMainWindow, pCMDIChild) { //Common OLE DB Interfaces m_pIConnectionPoint = NULL; //Connection interface //Data m_dwCookie = 0; } ///////////////////////////////////////////////////////////////// // CConnectionPoint::~CConnectionPoint // ///////////////////////////////////////////////////////////////// CConnectionPoint::~CConnectionPoint() { ReleaseObject(0); } ///////////////////////////////////////////////////////////////// // HRESULT CConnectionPoint::AutoRelease // ///////////////////////////////////////////////////////////////// HRESULT CConnectionPoint::AutoRelease() { //Common OLE DB Interface RELEASE_INTERFACE(IConnectionPoint); //Delegate return CBase::AutoRelease(); } ///////////////////////////////////////////////////////////////// // HRESULT CConnectionPoint::AutoQI // ///////////////////////////////////////////////////////////////// HRESULT CConnectionPoint::AutoQI(DWORD dwCreateOpts) { //Delegate First so we have base interfaces CBase::AutoQI(dwCreateOpts); //[MANDATORY] Obtain [mandatory] interfaces if(dwCreateOpts & CREATE_QI_MANDATORY) { OBTAIN_INTERFACE(IConnectionPoint); } //[OPTIONAL] if(dwCreateOpts & CREATE_QI_OPTIONAL) { } return S_OK; } ///////////////////////////////////////////////////////////////// // IUnknown** CConnectionPoint::GetInterfaceAddress // ///////////////////////////////////////////////////////////////// IUnknown** CConnectionPoint::GetInterfaceAddress(REFIID riid) { HANDLE_GETINTERFACE(IConnectionPoint); //Otherwise delegate return CBase::GetInterfaceAddress(riid); } ///////////////////////////////////////////////////////////////////////////// // CConnectionPoint::GetObjectDesc // ///////////////////////////////////////////////////////////////////////////// WCHAR* CConnectionPoint::GetObjectDesc() { if(!m_strObjectDesc && m_pIConnectionPoint) { IID iid; if(SUCCEEDED(GetConnectionInterface(&iid))) m_strObjectDesc.CopyFrom(GetInterfaceName(iid)); } return m_strObjectDesc; } ///////////////////////////////////////////////////////////////// // HRESULT CConnectionPoint::GetConnectionInterface // ///////////////////////////////////////////////////////////////// HRESULT CConnectionPoint::GetConnectionInterface(IID* pIID) { HRESULT hr = S_OK; if(m_pIConnectionPoint) { hr = m_pIConnectionPoint->GetConnectionInterface(pIID); TESTC(TRACE_METHOD(hr, L"IConnectionPoint::GetConnectionInterface(&%s)", GetInterfaceName(pIID ? *pIID : IID_NULL))); } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // CAsynchBase::CAsynchBase // ///////////////////////////////////////////////////////////////// CAsynchBase::CAsynchBase(SOURCE eObjectType, CMainWindow* pCMainWindow, CMDIChild* pCMDIChild) : CContainerBase(eObjectType, pCMainWindow, pCMDIChild) { //eBaseClass m_eBaseClass = BASE_CLASS(m_eBaseClass | eCAsynchBase); //OLE DB Interfaces m_pIDBInitialize = NULL; //OLE DB interface m_pIDBAsynchStatus = NULL; //OLE DB interface //Extra interfaces m_dwCookieAsynchNotify = 0; //Data m_fInitialized = FALSE; } ///////////////////////////////////////////////////////////////// // CAsynchBase::~CAsynchBase // ///////////////////////////////////////////////////////////////// CAsynchBase::~CAsynchBase() { } ///////////////////////////////////////////////////////////////// // IUnknown** CAsynchBase::GetInterfaceAddress // ///////////////////////////////////////////////////////////////// IUnknown** CAsynchBase::GetInterfaceAddress(REFIID riid) { HANDLE_GETINTERFACE(IDBInitialize); HANDLE_GETINTERFACE(IDBAsynchStatus); //Otherwise delegate return CContainerBase::GetInterfaceAddress(riid); } //////////////////////////////////////////////////////////////// // CAsynchBase::AutoRelease // ///////////////////////////////////////////////////////////////// HRESULT CAsynchBase::AutoRelease() { //UnadviseListeners UnadviseListener(IID_IDBAsynchNotify, &m_dwCookieAsynchNotify); //OLE DB interfaces RELEASE_INTERFACE(IDBInitialize); RELEASE_INTERFACE(IDBAsynchStatus); //Extra interfaces //Data m_fInitialized = FALSE; //Delegate return CContainerBase::AutoRelease(); } ///////////////////////////////////////////////////////////////// // HRESULT CAsynchBase::AutoQI // ///////////////////////////////////////////////////////////////// HRESULT CAsynchBase::AutoQI(DWORD dwCreateOpts) { //Delegate First so we have base interfaces CContainerBase::AutoQI(dwCreateOpts); //[MANDATORY] if(dwCreateOpts & CREATE_QI_MANDATORY) { } //AutoQI if(dwCreateOpts & CREATE_QI_OPTIONAL) { //[OPTIONAL] OBTAIN_INTERFACE(IDBInitialize); OBTAIN_INTERFACE(IDBAsynchStatus); } //Listeners AdviseListener(IID_IDBAsynchNotify, &m_dwCookieAsynchNotify); return S_OK; } ///////////////////////////////////////////////////////////////// // HRESULT CAsynchBase::Initialize // ///////////////////////////////////////////////////////////////// HRESULT CAsynchBase::Initialize() { HRESULT hr = S_OK; DWORD dwCreateOpts = GetOptions()->m_dwCreateOpts; if(!m_pIDBInitialize) goto CLEANUP; //Initailize //NOTE: Expect S_OK or DB_E_CANCELED, since canceling the dialog always returns DB_E_CANCELED XTEST_(hr = m_pIDBInitialize->Initialize(), DB_E_CANCELED); TRACE_METHOD(hr, L"IDBInitialize::Initialize()"); //Display any property errors... TESTC(hr = DisplayPropErrors(hr, IID_IDBProperties, m_pIDBInitialize)); //We are now Initialized m_fInitialized = TRUE; //Obtain all interfaces, now that we are initialized TESTC(hr = AutoQI(dwCreateOpts)); //Also "redraw" the object now that more interfaces are available for use //For Example: If the rowset was originally obtained Asynchronously and now interfaces //to obtain columns and data are available, display the object... TESTC(hr = DisplayObject()); CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CAsynchBase::Uninitialize // ///////////////////////////////////////////////////////////////// HRESULT CAsynchBase::Uninitialize() { HRESULT hr = S_OK; if(!m_pIDBInitialize) goto CLEANUP; //Uninitailize XTEST(hr = m_pIDBInitialize->Uninitialize()); TESTC(TRACE_METHOD(hr, L"IDBInitialize::Uninitialize()")); //We are now Uninitialized m_fInitialized = FALSE; CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CAsynchBase::Abort // ///////////////////////////////////////////////////////////////// HRESULT CAsynchBase::Abort(HCHAPTER hChapter, DBASYNCHOP eOperation) { HRESULT hr = S_OK; if(!m_pIDBAsynchStatus) goto CLEANUP; //IDBAsynchStatus::Abort XTEST(hr = m_pIDBAsynchStatus->Abort(hChapter, eOperation)); TRACE_METHOD(hr, L"IDBAsynchStatus::Abort(0x%p, %s)", hChapter, GetAsynchReason(eOperation)); CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // HRESULT CAsynchBase::GetStatus // ///////////////////////////////////////////////////////////////// HRESULT CAsynchBase::GetStatus(HCHAPTER hChapter, DBASYNCHOP eOperation, DBCOUNTITEM* pulProgress, DBCOUNTITEM* pulProgressMax, DBASYNCHPHASE* peAsynchPhase, LPOLESTR* ppwszStatusText) { HRESULT hr = S_OK; if(!m_pIDBAsynchStatus) goto CLEANUP; //IDBAsynchStatus::GetStatus XTEST(hr = m_pIDBAsynchStatus->GetStatus(hChapter, eOperation, pulProgress, pulProgressMax, peAsynchPhase, ppwszStatusText)); TRACE_METHOD(hr, L"IDBAsynchStatus::GetStatus(0x%p, %s, &%lu, &%lu, &%s, &\"%s\")", hChapter, GetAsynchReason(eOperation), pulProgress ? *pulProgress : 0, pulProgressMax ? *pulProgressMax : 0, GetAsynchPhase(peAsynchPhase ? *peAsynchPhase : 0), ppwszStatusText ? *ppwszStatusText : NULL); CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // CPropertiesBase::CPropertiesBase // ///////////////////////////////////////////////////////////////// CPropertiesBase::CPropertiesBase(SOURCE eObjectType, CMainWindow* pCMainWindow, CMDIChild* pCMDIChild) : CAsynchBase(eObjectType, pCMainWindow, pCMDIChild) { //eBaseClass m_eBaseClass = BASE_CLASS(m_eBaseClass | eCPropertiesBase); //OLE DB Interfaces m_pIDBProperties = NULL; //OLE DB interface } ///////////////////////////////////////////////////////////////// // CPropertiesBase::~CPropertiesBase // ///////////////////////////////////////////////////////////////// CPropertiesBase::~CPropertiesBase() { } ///////////////////////////////////////////////////////////////// // IUnknown** CPropertiesBase::GetInterfaceAddress // ///////////////////////////////////////////////////////////////// IUnknown** CPropertiesBase::GetInterfaceAddress(REFIID riid) { HANDLE_GETINTERFACE(IDBProperties); //Otherwise delegate return CAsynchBase::GetInterfaceAddress(riid); } //////////////////////////////////////////////////////////////// // CPropertiesBase::AutoRelease // ///////////////////////////////////////////////////////////////// HRESULT CPropertiesBase::AutoRelease() { //OLE DB interfaces RELEASE_INTERFACE(IDBProperties); //Delegate return CAsynchBase::AutoRelease(); } ///////////////////////////////////////////////////////////////// // HRESULT CPropertiesBase::AutoQI // ///////////////////////////////////////////////////////////////// HRESULT CPropertiesBase::AutoQI(DWORD dwCreateOpts) { //Delegate First so we have IConnectionPointContainer CAsynchBase::AutoQI(dwCreateOpts); //[MANDATORY] if(dwCreateOpts & CREATE_QI_MANDATORY) { OBTAIN_INTERFACE(IDBProperties); //NOTE: Since this class inherits from CAsynchBase which already has an IDBInitialize //pointer we will just use that, but the problem is that in the object its an optional //interface, and under the DSO its a required interface. So we just do a QI again //if the pointer has not already been retrived... OBTAIN_INTERFACE(IDBInitialize); } //AutoQI if(dwCreateOpts & CREATE_QI_OPTIONAL) { //[OPTIONAL] } return S_OK; } ///////////////////////////////////////////////////////////////// // HRESULT CPropertiesBase::SetProperties // ///////////////////////////////////////////////////////////////// HRESULT CPropertiesBase::SetProperties(ULONG cPropSets, DBPROPSET* rgPropSets) { HRESULT hr = S_OK; if(m_pIDBProperties && cPropSets) { //SetProperties XTEST_(hr = m_pIDBProperties->SetProperties(cPropSets, rgPropSets),S_OK); TRACE_METHOD(hr, L"IDBProperties::SetProperties(%d, 0x%p)", cPropSets, rgPropSets); //Display any property errors... TESTC(hr = DisplayPropErrors(hr, cPropSets, rgPropSets)); } CLEANUP: return hr; } ///////////////////////////////////////////////////////////////// // GuidToSourceType // ///////////////////////////////////////////////////////////////// SOURCE GuidToSourceType(REFGUID guidType) { if(guidType == DBGUID_ROWSET) return eCRowset; else if(guidType == DBGUID_ROW) return eCRow; else if(guidType == DBGUID_COMMAND) return eCCommand; else if(guidType == DBGUID_SESSION) return eCSession; else if(guidType == DBGUID_DSO) return eCDataSource; else if(guidType == DBGUID_STREAM) return eCStream; return eCUnknown; } //////////////////////////////////////////////////////////////// // DetermineObjectType // ///////////////////////////////////////////////////////////////// SOURCE DetermineObjectType(IUnknown* pIUnkObject, SOURCE eSource) { //Don't rely upon the caller knowing exactly what the object type is... //Since many OLE DB methods can return different objects depending upon interface or properties //requested. So use the "suggested" type as an optimizing starting point, and if not //then proceed the hard way to determine exactly what it is... //Do we need to figure out what type of object this is? if(!pIUnkObject) return eCUnknown; IUnknown* pIUnknown = NULL; HRESULT hr = E_NOINTERFACE; //See of the object is what the user's "guess" indicates... //If it is where done... switch(eSource) { case eCRow: hr = TRACE_QI(pIUnkObject, IID_IRow, &pIUnknown); break; case eCRowset: hr = TRACE_QI(pIUnkObject, IID_IRowset, &pIUnknown); break; case eCCommand: hr = TRACE_QI(pIUnkObject, IID_ICommand, &pIUnknown); break; case eCSession: hr = TRACE_QI(pIUnkObject, IID_IOpenRowset, &pIUnknown); break; case eCMultipleResults: hr = TRACE_QI(pIUnkObject, IID_IMultipleResults, &pIUnknown); break; case eCEnumerator: hr = TRACE_QI(pIUnkObject, IID_ISourcesRowset, &pIUnknown); break; case eCBinder: hr = TRACE_QI(pIUnkObject, IID_IBindResource, &pIUnknown); break; case eCDataSource: //IDBInitialize is no longer enough to fully indentify wither the object returned //is a DataSource or not. Other objects also have these: ie: Enumerator, Binder, etc if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IDBInitialize, &pIUnknown))) { TRACE_RELEASE(pIUnknown, L"IDBInitialize"); hr = TRACE_QI(pIUnkObject, IID_IPersist, &pIUnknown); } break; case eCServiceComp: hr = TRACE_QI(pIUnkObject, IID_IDataInitialize, &pIUnknown); break; case eCDataLinks: hr = TRACE_QI(pIUnkObject, IID_IDBPromptInitialize, &pIUnknown); break; case eCStream: //Some providers for some reason may not support the inherited ISequentialStream interface if(FAILED(hr = TRACE_QI(pIUnkObject, IID_ISequentialStream, &pIUnknown))) hr = TRACE_QI(pIUnkObject, IID_IStream, &pIUnknown); break; case eCDataset: hr = TRACE_QI(pIUnkObject, IID_IMDDataset, &pIUnknown); break; case eCRowPosition: hr = TRACE_QI(pIUnkObject, IID_IRowPosition, &pIUnknown); break; case eCTransaction: hr = TRACE_QI(pIUnkObject, IID_ITransaction, &pIUnknown); break; case eCTransactionOptions: hr = TRACE_QI(pIUnkObject, IID_ITransactionOptions, &pIUnknown); break; case eCError: hr = TRACE_QI(pIUnkObject, IID_IErrorInfo, &pIUnknown); break; case eCCustomError: hr = TRACE_QI(pIUnkObject, IID_ISQLErrorInfo, &pIUnknown); break; case eCConnectionPoint: hr = TRACE_QI(pIUnkObject, IID_IConnectionPoint, &pIUnknown); break; default: hr = E_NOINTERFACE; break; }; //If the object doesn't match the users guess we will need to try and determine what it really is... if(FAILED(hr)) { //Since many objects support the same interface, //we need to actually QI for a "unique" interface on that object //to determine what type of object it really is... if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IRow, &pIUnknown))) eSource = eCRow; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IRowset, &pIUnknown))) eSource = eCRowset; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_ICommand, &pIUnknown))) eSource = eCCommand; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IOpenRowset, &pIUnknown))) eSource = eCSession; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IMultipleResults, &pIUnknown))) eSource = eCMultipleResults; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_ISourcesRowset, &pIUnknown))) eSource = eCEnumerator; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IBindResource, &pIUnknown))) eSource = eCBinder; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IDBProperties, &pIUnknown))) eSource = eCDataSource; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IDataInitialize, &pIUnknown))) eSource = eCServiceComp; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IDBPromptInitialize, &pIUnknown))) eSource = eCDataLinks; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_ISequentialStream, &pIUnknown)) || SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IStream, &pIUnknown))) eSource = eCStream; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IMDDataset, &pIUnknown))) eSource = eCDataset; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IRowPosition, &pIUnknown))) eSource = eCRowPosition; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_ITransaction, &pIUnknown))) eSource = eCTransaction; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_ITransactionOptions, &pIUnknown))) eSource = eCTransactionOptions; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IErrorInfo, &pIUnknown))) eSource = eCError; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_ISQLErrorInfo, &pIUnknown))) eSource = eCCustomError; else if(SUCCEEDED(hr = TRACE_QI(pIUnkObject, IID_IConnectionPoint, &pIUnknown))) eSource = eCConnectionPoint; else eSource = eCUnknown; } if(SUCCEEDED(hr)) TRACE_RELEASE(pIUnknown, L"IUnknown"); return eSource; }