TDCCTL.H
//+----------------------------------------------------------------------- 
// 
//  Tabular Data Control 
//  Copyright (C) Microsoft Corporation, 1996, 1997 
// 
//  File:       TDCCtl.h 
// 
//  Contents:   Declaration of the CTDCCtl ActiveX Control. 
// 
//------------------------------------------------------------------------ 
 
 
#include "resource.h"       // main symbols 
#include "std.h" 
#include "wch.h" 
#include <wininet.h>        // for INTERNET_MAX_URL_LENGTH 
 
#pragma comment(lib, "wininet.lib") 
 
#ifndef DISPID_AMBIENT_CODEPAGE 
#define DISPID_AMBIENT_CODEPAGE (-725) 
#endif 
 
// Declare helper needed in IHttpNegotiateImpl 
HRESULT 
GetHostURL(IOleClientSite *pSite, LPOLESTR *ppszHostName); 
 
//------------------------------------------------------------------------ 
// 
//  Template:  CMyBindStatusCallback 
// 
//  Synopsis:  This is a temporary kludge to get around an ATL feature 
//             while we're waiting for it to become official code. 
// 
//------------------------------------------------------------------------ 
 
template <class T> 
class ATL_NO_VTABLE IServiceProviderImpl 
{ 
    public: 
        // IUnknown 
        // 
        STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0; 
        _ATL_DEBUG_ADDREF_RELEASE_IMPL(IServiceProviderImpl) 
 
        STDMETHOD(QueryService) (REFGUID guidService, 
                                 REFIID riid, 
                                 void **ppvObject) 
        { 
            return S_OK; 
        } 
 
}; 
 
template <class T> 
class ATL_NO_VTABLE IHttpNegotiateImpl 
{ 
    public: 
        // IUnknown 
        // 
        STDMETHOD(QueryInterface)(REFIID riid, void ** ppvObject) = 0; 
        _ATL_DEBUG_ADDREF_RELEASE_IMPL(IHttpNegotiateImpl) 
 
        STDMETHOD(BeginningTransaction) (LPCWSTR szURL, 
                                         LPCWSTR szHeaders, 
                                         DWORD dwReserved, 
                                         LPWSTR *pszAdditionalHeaders) 
        { 
            return S_OK; 
        } 
 
        STDMETHOD(OnResponse) (DWORD dwResponseCode, 
                               LPCWSTR szResponseHeaders, 
                               LPCWSTR szRequestHeaders, 
                               LPWSTR *pszAdditionalRequestHeaders) 
        { 
            return S_OK; 
        } 
 
}; 
 
 
template <class T> 
class ATL_NO_VTABLE CMyBindStatusCallback : 
    public CComObjectRootEx<T::_ThreadModel::ThreadModelNoCS>, 
    public IBindStatusCallbackImpl<T>, public IHttpNegotiateImpl<T>, public IServiceProviderImpl<T> 
{ 
    typedef void (T::*ATL_PDATAAVAILABLE)(CMyBindStatusCallback<T>* pbsc, BYTE* pBytes, DWORD dwSize); 
 
    public: 
 
        BEGIN_COM_MAP(CMyBindStatusCallback<T>) 
                COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallbackImpl<T>) 
                COM_INTERFACE_ENTRY_IID(IID_IHttpNegotiate, IHttpNegotiateImpl<T>) 
                COM_INTERFACE_ENTRY_IID(IID_IServiceProvider, IServiceProviderImpl<T>) 
        END_COM_MAP() 
 
        CMyBindStatusCallback() 
        { 
            m_pT = NULL; 
            m_pFunc = NULL; 
            m_fReload = FALSE; 
        } 
        ~CMyBindStatusCallback() 
        { 
            ATLTRACE(_T("~CMyBindStatusCallback\n")); 
        } 
 
        // IServiceProvider methods 
 
        STDMETHOD(QueryService) (REFGUID guidService, 
                                 REFIID riid, 
                                 void **ppvObject) 
        { 
            // As it turns out, the service ID for IHttpNegotiate is the same 
            // as it's IID (confusing).  This is the only service we support. 
            if (IsEqualGUID(IID_IHttpNegotiate, guidService)) 
            { 
                return ((IHttpNegotiate *)this)->QueryInterface(riid, ppvObject); 
            } 
            else return E_NOTIMPL; 
        } 
 
        // 
        // IHttpNegotiate methods 
        // 
 
        STDMETHOD(BeginningTransaction) (LPCWSTR szURL, 
                                         LPCWSTR szHeaders, 
                                         DWORD dwReserved, 
                                         LPWSTR *pszAdditionalHeaders) 
        { 
            HRESULT hr = S_OK; 
            WCHAR swzHostScheme[INTERNET_MAX_URL_LENGTH]; 
            DWORD cchHostScheme = INTERNET_MAX_URL_LENGTH; 
            WCHAR swzFileScheme[INTERNET_MAX_URL_LENGTH]; 
            DWORD cchFileScheme = INTERNET_MAX_URL_LENGTH;             
 
            LPOLESTR pszHostName; 
 
            *pszAdditionalHeaders = NULL; 
 
            hr = GetHostURL(m_spClientSite, &pszHostName); 
            if (FAILED(hr)) 
                goto Cleanup; 
 
            // PARSE_SCHEMA didn't work, so we'll just CANONICALIZE and then use the first N 
            // characters of the URL 
            hr = CoInternetParseUrl(pszHostName, PARSE_CANONICALIZE, 0, swzHostScheme, cchHostScheme, 
                                    &cchHostScheme, 0); 
            if (FAILED(hr)) 
                goto Cleanup; 
 
            // Don't send a referer which isn't http: or https:, it's none 
            // of the servers' business.  Further, don't send an https: 
            // referer when requesting an http: file. 
            if (0 != wch_incmp(swzHostScheme, L"https:", 6) && 
                0 != wch_incmp(swzHostScheme, L"http:", 5)) 
                goto Cleanup; 
 
            if (0 == wch_incmp(swzHostScheme, L"https:", 6)) 
            { 
                hr = CoInternetParseUrl(szURL, PARSE_CANONICALIZE, 0, swzFileScheme, cchFileScheme, 
                                        &cchFileScheme, 0); 
                if (0 == wch_incmp(swzFileScheme, L"http:", 65)) // don't send https: referer 
                    goto Cleanup;                                // to an http: file. 
            } 
 
            // 3*sizeof(WCHAR) is for CR, LF, & '\0' 
            *pszAdditionalHeaders = (WCHAR *)CoTaskMemAlloc(sizeof(L"Referer: ") + 
                                                            ocslen(pszHostName)*sizeof(WCHAR) + 
                                                            3*sizeof(WCHAR)); 
            if (NULL != *pszAdditionalHeaders) 
            { 
                ocscpy(*pszAdditionalHeaders, L"Referer: "); 
                ocscpy(&((*pszAdditionalHeaders)[9]), pszHostName); 
                ocscpy(&((*pszAdditionalHeaders)[9+ocslen(pszHostName)]), L"\r\n"); 
            } 
 
Cleanup: 
            CoTaskMemFree(pszHostName); 
            return hr; 
        } 
 
        STDMETHOD(OnResponse) (DWORD dwResponseCode, 
                               LPCWSTR szResponseHeaders, 
                               LPCWSTR szRequestHeaders, 
                               LPWSTR *pszAdditionalRequestHeaders) 
        { 
            return S_OK; 
        } 
 
 
 
        // 
        // IBindStatusCallback methods 
        // 
 
        STDMETHOD(OnStartBinding)(DWORD dwReserved, IBinding *pBinding) 
        { 
            ATLTRACE(_T("CMyBindStatusCallback::OnStartBinding\n")); 
            m_spBinding = pBinding; 
            return S_OK; 
        } 
 
        STDMETHOD(GetPriority)(LONG *pnPriority) 
        { 
            ATLTRACENOTIMPL(_T("CMyBindStatusCallback::GetPriority")); 
        } 
 
        STDMETHOD(OnLowResource)(DWORD reserved) 
        { 
            ATLTRACENOTIMPL(_T("CMyBindStatusCallback::OnLowResource")); 
        } 
 
        STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText) 
        { 
            if (BINDSTATUS_REDIRECTING == ulStatusCode && szStatusText != NULL) 
            { 
                ocscpy(m_pszURL, szStatusText); 
            } 
            return S_OK; 
        } 
 
        STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR szError) 
        { 
            //      ATLTRACE(_T("CMyBindStatusCallback::OnStopBinding\n")); 
            (m_pT->*m_pFunc)(this, NULL, 0); 
            m_spBinding.Release(); 
            m_spBindCtx.Release(); 
            m_spMoniker.Release(); 
            return S_OK; 
        } 
 
        STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo) 
        { 
            ATLTRACE(_T("CMyBindStatusCallback::GetBindInfo\n")); 
 
            if (!pbindInfo || !pbindInfo->cbSize || !pgrfBINDF) 
                return E_INVALIDARG; 
 
            *pgrfBINDF = BINDF_ASYNCHRONOUS 
                         | BINDF_ASYNCSTORAGE 
                         ; 
// ;begin_internal 
#ifdef NEVER 
            // I want DEBUG mode to NOT cache things!! -cfranks 
            *pgrfBINDF |= BINDF_GETNEWESTVERSION 
                          | BINDF_NOWRITECACHE 
                          | BINDF_RESYNCHRONIZE 
                          ; 
#endif 
// ;end_internal 
 
#ifndef DISPID_AMBIENT_OFFLINE 
#define DISPID_AMBIENT_OFFLINE          (-5501) 
#endif 
            // Get our offline property from container 
            VARIANT var; 
            VariantInit(&var); 
            DWORD dwConnectedStateFlags; 
            m_pT->GetAmbientProperty(DISPID_AMBIENT_OFFLINE, var); 
            if (var.vt==VT_BOOL && var.boolVal) 
            { 
                if (!(InternetGetConnectedState(&dwConnectedStateFlags, 0)) && 
                    (0 == (dwConnectedStateFlags & INTERNET_CONNECTION_MODEM_BUSY))) 
                { 
                    ATLTRACE(_T("CMyBindStatusCallback::GetBindInfo OFFLINE\n")); 
                   // We're not even dialed out to another connectoid 
                    *pgrfBINDF |= BINDF_OFFLINEOPERATION; 
                } 
                else 
                { 
                    ATLTRACE(_T("CMyBindStatusCallback::GetBindInfo OFFLINE\n")); 
                    *pgrfBINDF &= ~BINDF_OFFLINEOPERATION;                    
                } 
            } 
 
            // See if we should force a reload, iff we're not offline. 
            if (!(*pgrfBINDF & BINDF_OFFLINEOPERATION) && m_fReload) 
            { 
                *pgrfBINDF |= BINDF_RESYNCHRONIZE|BINDF_PRAGMA_NO_CACHE; 
            } 
 
            ULONG cbSize = pbindInfo->cbSize; 
            memset(pbindInfo, 0, cbSize); 
 
            pbindInfo->cbSize = cbSize; 
            pbindInfo->dwBindVerb = BINDVERB_GET; 
 
            return S_OK; 
        } 
 
        STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed) 
        { 
            ATLTRACE(_T("CMyBindStatusCallback::OnDataAvailable\n")); 
            HRESULT hr = S_OK; 
 
            // Get the Stream passed 
            if (BSCF_FIRSTDATANOTIFICATION & grfBSCF) 
            { 
                if (!m_spStream && pstgmed->tymed == TYMED_ISTREAM) 
                { 
                    m_spStream = pstgmed->pstm; 
                } 
            } 
 
            DWORD dwRead = dwSize - m_dwTotalRead; // Minimum amount available that hasn't been read 
            DWORD dwActuallyRead = 0;            // Placeholder for amount read during this pull 
 
            // If there is some data to be read then go ahead and read them 
            if (m_spStream) 
            { 
                if (dwRead > 0) 
                { 
                    BYTE* pBytes = NULL; 
                    ATLTRY(pBytes = new BYTE[dwRead + 1]); 
                    if (pBytes == NULL) 
                        return S_FALSE; 
                    hr = m_spStream->Read(pBytes, dwRead, &dwActuallyRead); 
                    if (SUCCEEDED(hr)) 
                    { 
                        pBytes[dwActuallyRead] = 0; 
                        if (dwActuallyRead>0) 
                        { 
                            (m_pT->*m_pFunc)(this, pBytes, dwActuallyRead); 
                            m_dwTotalRead += dwActuallyRead; 
                        } 
                    } 
                    delete[] pBytes; 
                } 
            } 
 
            if (BSCF_LASTDATANOTIFICATION & grfBSCF) 
                m_spStream.Release(); 
            return hr; 
        } 
 
        STDMETHOD(OnObjectAvailable)(REFIID riid, IUnknown *punk) 
        { 
            ATLTRACENOTIMPL(_T("CMyBindStatusCallback::OnObjectAvailable")); 
        } 
 
        HRESULT _StartAsyncDownload(BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative) 
        { 
            m_dwTotalRead = 0; 
            m_dwAvailableToRead = 0; 
            HRESULT hr = S_OK; 
            CComQIPtr<IServiceProvider, &IID_IServiceProvider> spServiceProvider(pUnkContainer); 
            CComPtr<IBindHost> spBindHost; 
            CComPtr<IStream> spStream; 
            if (spServiceProvider) 
                spServiceProvider->QueryService(SID_IBindHost, IID_IBindHost, (void**)&spBindHost); 
 
            // We don't bother checking this QI, because the only failure mode is that our 
            // BeginningNegotitation method won't be able able to properly add the referer string. 
            (void)pUnkContainer->QueryInterface(IID_IOleClientSite, (void **)&m_spClientSite); 
 
            if (spBindHost == NULL) 
            { 
                if (bRelative) 
                    return E_NOINTERFACE;  // relative asked for, but no IBindHost 
                hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker); 
                if (SUCCEEDED(hr)) 
                    hr = CreateBindCtx(0, &m_spBindCtx); 
 
                if (SUCCEEDED(hr)) 
                    hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L); 
                else 
                    m_spMoniker.Release(); 
 
                if (SUCCEEDED(hr)) 
                { 
                    LPOLESTR pszTemp = NULL; 
                    hr = m_spMoniker->GetDisplayName(m_spBindCtx, NULL, &pszTemp); 
                    if (!hr && pszTemp != NULL) 
                        ocscpy(m_pszURL, pszTemp); 
                    CoTaskMemFree(pszTemp); 
 
                    hr = m_spMoniker->BindToStorage(m_spBindCtx, 0, IID_IStream, (void**)&spStream); 
                } 
            } 
            else 
            { 
                hr = CreateBindCtx(0, &m_spBindCtx); 
                if (SUCCEEDED(hr)) 
                    hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L); 
 
                if (SUCCEEDED(hr)) 
                { 
                    if (bRelative) 
                        hr = spBindHost->CreateMoniker(bstrURL, m_spBindCtx, &m_spMoniker, 0); 
                    else 
                        hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker); 
                } 
 
                if (SUCCEEDED(hr)) 
                { 
                    LPOLESTR pszTemp = NULL; 
                    hr = m_spMoniker->GetDisplayName(m_spBindCtx, NULL, &pszTemp); 
                    if (!hr && pszTemp != NULL) 
                        ocscpy(m_pszURL, pszTemp); 
                    CoTaskMemFree(pszTemp); 
                    hr = spBindHost->MonikerBindToStorage(m_spMoniker, NULL, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), IID_IStream, (void**)&spStream); 
                    ATLTRACE(_T("Bound")); 
                } 
            } 
            return hr; 
        } 
 
        HRESULT StartAsyncDownload(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer, BOOL bRelative, 
                                   BOOL fReload) 
        { 
            m_pT = pT; 
            m_pFunc = pFunc; 
            m_fReload = fReload;        // force reload if TRUE 
            return  _StartAsyncDownload(bstrURL, pUnkContainer, bRelative); 
        } 
 
        static HRESULT Download(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE) 
        { 
            CComObject<CMyBindStatusCallback<T> > *pbsc; 
            HRESULT hRes = CComObject<CMyBindStatusCallback<T> >::CreateInstance(&pbsc); 
            if (FAILED(hRes)) 
                return hRes; 
            return pbsc->StartAsyncDownload(pT, pFunc, bstrURL, pUnkContainer, bRelative, FALSE); 
        } 
        CComPtr<IMoniker> m_spMoniker; 
        CComPtr<IBindCtx> m_spBindCtx; 
        CComPtr<IBinding> m_spBinding; 
        CComPtr<IStream> m_spStream; 
        CComPtr<IOleClientSite> m_spClientSite; 
        BOOL m_fReload; 
        OLECHAR m_pszURL[INTERNET_MAX_URL_LENGTH]; 
        T* m_pT; 
        ATL_PDATAAVAILABLE m_pFunc; 
        DWORD m_dwTotalRead; 
        DWORD m_dwAvailableToRead; 
}; 
 
////////////////////////////////////////////////////////////////////////////////////////////////// 
// CTimer 
template <class Derived, class T, const IID* piid> 
class CTimer 
{ 
public: 
 
    CTimer() 
    { 
        m_bTimerOn = FALSE; 
    } 
 
    HRESULT TimerOn(DWORD dwTimerInterval) 
    { 
        Derived* pDerived = ((Derived*)this); 
 
        m_dwTimerInterval = dwTimerInterval; 
        if (m_bTimerOn) // already on, just change interval 
            return S_OK; 
 
        m_bTimerOn = TRUE; 
        m_dwTimerInterval = dwTimerInterval; 
        m_pStream = NULL;  
 
        HRESULT hRes; 
 
        hRes = CoMarshalInterThreadInterfaceInStream(*piid, (T*)pDerived, &m_pStream); 
 
        // Create thread and pass the thread proc the this ptr 
        m_hThread = CreateThread(NULL, 0, &_Apartment, (void*)this, 0, &m_dwThreadID); 
 
        return S_OK; 
    } 
 
    void TimerOff() 
    { 
        if (m_bTimerOn) 
        { 
            m_bTimerOn = FALSE; 
            AtlWaitWithMessageLoop(m_hThread); 
        } 
    } 
 
 
// Implementation 
private: 
    static DWORD WINAPI _Apartment(void* pv) 
    { 
        CTimer<Derived, T, piid>* pThis = (CTimer<Derived, T, piid>*) pv; 
        pThis->Apartment(); 
        return 0; 
    } 
 
    DWORD Apartment() 
    { 
        CoInitialize(NULL); 
        HRESULT hRes; 
 
        m_spT.Release(); 
 
        if (m_pStream) 
        { 
            hRes = CoGetInterfaceAndReleaseStream(m_pStream, *piid, (void**)&m_spT); 
        } 
 
        while(m_bTimerOn) 
        { 
            Sleep(m_dwTimerInterval); 
            if (!m_bTimerOn) 
                break; 
 
            m_spT->_OnTimer(); 
        } 
        m_spT.Release(); 
 
        CoUninitialize(); 
        return 0; 
    } 
 
// Attributes 
public: 
    DWORD m_dwTimerInterval; 
 
// Implementation 
private: 
    HANDLE m_hThread; 
    DWORD m_dwThreadID; 
    LPSTREAM m_pStream; 
    CComPtr<T> m_spT; 
    BOOL m_bTimerOn; 
}; 
 
class CEventBroker; 
 
////////////////////////////////////////////////////////////////////////////// 
// CProxyITDCCtlEvents 
template <class T> 
class CProxyITDCCtlEvents : public IConnectionPointImpl<T, &IID_ITDCCtlEvents, CComDynamicUnkArray> 
{ 
//ITDCCtlEvents : IDispatch 
public: 
    void FireOnReadyStateChanged() 
    { 
        T* pT = (T*)this; 
        pT->Lock(); 
        IUnknown** pp = m_vec.begin(); 
        while (pp < m_vec.end()) 
        { 
            if (*pp != NULL) 
            { 
                DISPPARAMS dispParams; 
                dispParams.cArgs = 0; 
                dispParams.cNamedArgs = 0; 
                dispParams.rgvarg = NULL; 
                dispParams.rgdispidNamedArgs = NULL; 
                ITDCCtlEvents* pITDCCtlEvents = reinterpret_cast<ITDCCtlEvents*>(*pp); 
                pITDCCtlEvents->Invoke(DISPID_READYSTATECHANGE, IID_NULL, CP_ACP, DISPATCH_METHOD, &dispParams, 
                                       NULL, NULL, NULL); 
            } 
            pp++; 
        } 
        pT->Unlock(); 
        return; 
    } 
 
}; 
 
 
//------------------------------------------------------------------------ 
// 
//  Class:     CTDCCtl 
// 
//  Synopsis:  This is the TabularDataControl COM object. 
//             It creates a CTDCArr object to manage the control's data. 
// 
//------------------------------------------------------------------------ 
 
class CTDCCtl : 
    public CComObjectRoot, 
    public CComCoClass<CTDCCtl, &CLSID_CTDCCtl>, 
    public CComControl<CTDCCtl>, 
    public CStockPropImpl<CTDCCtl, ITDCCtl, &IID_ITDCCtl, &LIBID_TDCLib>, 
    public IProvideClassInfo2Impl<&CLSID_CTDCCtl, &IID_ITDCCtlEvents, &LIBID_TDCLib>, 
    public IPersistStreamInitImpl<CTDCCtl>, 
    public IOleControlImpl<CTDCCtl>, 
    public IOleObjectImpl<CTDCCtl>, 
    public IOleInPlaceActiveObjectImpl<CTDCCtl>, 
    public IViewObjectExImpl<CTDCCtl>, 
    public IOleInPlaceObjectWindowlessImpl<CTDCCtl>, 
    public IPersistPropertyBagImpl<CTDCCtl>, 
    public CTimer<CTDCCtl, ITDCCtl, &IID_ITDCCtl>, 
    public IRunnableObjectImpl<CTDCCtl>, 
    public IConnectionPointContainerImpl<CTDCCtl>, 
    public IPropertyNotifySinkCP<CTDCCtl>, 
    public CProxyITDCCtlEvents<CTDCCtl> 
{ 
public: 
    CTDCCtl(); 
    ~CTDCCtl(); 
 
DECLARE_REGISTRY_RESOURCEID(IDR_TDCCtl) 
 
DECLARE_NOT_AGGREGATABLE(CTDCCtl) 
 
BEGIN_COM_MAP(CTDCCtl)  
    COM_INTERFACE_ENTRY(IDispatch) 
    COM_INTERFACE_ENTRY(ITDCCtl) 
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject, IViewObjectEx) 
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IViewObject2, IViewObjectEx) 
    COM_INTERFACE_ENTRY_IMPL(IViewObjectEx) 
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleWindow, IOleInPlaceObjectWindowless) 
    COM_INTERFACE_ENTRY_IMPL_IID(IID_IOleInPlaceObject, IOleInPlaceObjectWindowless) 
    COM_INTERFACE_ENTRY_IMPL(IOleInPlaceObjectWindowless) 
    COM_INTERFACE_ENTRY_IMPL(IOleInPlaceActiveObject) 
    COM_INTERFACE_ENTRY_IMPL(IOleControl) 
    COM_INTERFACE_ENTRY_IMPL(IOleObject) 
    COM_INTERFACE_ENTRY_IMPL(IPersistStreamInit) 
    COM_INTERFACE_ENTRY(IProvideClassInfo) 
    COM_INTERFACE_ENTRY(IProvideClassInfo2) 
    COM_INTERFACE_ENTRY_IMPL(IPersistPropertyBag) 
    COM_INTERFACE_ENTRY_IMPL(IRunnableObject) 
    COM_INTERFACE_ENTRY_IMPL(IConnectionPointContainer) 
END_COM_MAP() 
 
BEGIN_PROPERTY_MAP(CTDCCtl) 
    PROP_ENTRY("RowDelim",      DISPID_ROWDELIM,    CLSID_CTDCCtl) 
    PROP_ENTRY("FieldDelim",    DISPID_FIELDDELIM,  CLSID_CTDCCtl) 
    PROP_ENTRY("TextQualifier", DISPID_TEXTQUALIFIER,   CLSID_CTDCCtl) 
    PROP_ENTRY("EscapeChar",    DISPID_ESCAPECHAR,  CLSID_CTDCCtl) 
    PROP_ENTRY("UseHeader",     DISPID_USEHEADER,   CLSID_CTDCCtl) 
    PROP_ENTRY("SortAscending", DISPID_SORTASCENDING,   CLSID_CTDCCtl) 
    PROP_ENTRY("SortColumn",    DISPID_SORTCOLUMN,  CLSID_CTDCCtl) 
    PROP_ENTRY("FilterValue",   DISPID_FILTERVALUE, CLSID_CTDCCtl) 
    PROP_ENTRY("FilterCriterion",   DISPID_FILTERCRITERION, CLSID_CTDCCtl) 
    PROP_ENTRY("FilterColumn",  DISPID_FILTERCOLUMN,CLSID_CTDCCtl) 
    PROP_ENTRY("CharSet",       DISPID_CHARSET,     CLSID_CTDCCtl) 
    PROP_ENTRY("Language",      DISPID_LANGUAGE,    CLSID_CTDCCtl) 
    PROP_ENTRY("CaseSensitive", DISPID_CASESENSITIVE, CLSID_CTDCCtl) 
    PROP_ENTRY("Sort",          DISPID_SORT,        CLSID_CTDCCtl) 
// ;begin_internal 
//  Doesn't work right yet. 
//    PROP_ENTRY("RefreshInterval",   DISPID_TIMER,      CLSID_CTDCCtl) 
// ;end_internal 
    PROP_ENTRY("Filter",        DISPID_FILTER,      CLSID_CTDCCtl) 
    PROP_ENTRY("AppendData",    DISPID_APPENDDATA,  CLSID_CTDCCtl) 
// ;begin_internal 
//  Trying to save this property causes OLEAUT to GP Fault trying 
//  to conver the IDispatch * to a BSTR! 
//    PROP_ENTRY("OSP",           DISPID_OSP,         CLSID_CTDCCtl) 
// ;end_internal 
    //  This will be removed when we learn more about the HTML 
    //  sub-tag "OBJECT" 
    PROP_ENTRY("DataURL",       DISPID_DATAURL,     CLSID_CTDCCtl) 
    PROP_ENTRY("ReadyState",    DISPID_READYSTATE,  CLSID_CTDCCtl) 
END_PROPERTY_MAP() 
 
BEGIN_CONNECTION_POINT_MAP(CTDCCtl) 
CONNECTION_POINT_ENTRY(IID_IPropertyNotifySink) 
CONNECTION_POINT_ENTRY(IID_ITDCCtlEvents) 
END_CONNECTION_POINT_MAP() 
 
BEGIN_MSG_MAP(CTDCCtl) 
    MESSAGE_HANDLER(WM_PAINT, OnPaint) 
    MESSAGE_HANDLER(WM_GETDLGCODE, OnGetDlgCode) 
    MESSAGE_HANDLER(WM_SETFOCUS, OnSetFocus) 
    MESSAGE_HANDLER(WM_KILLFOCUS, OnKillFocus) 
END_MSG_MAP() 
 
private: 
    CComBSTR     m_cbstrFieldDelim; 
    CComBSTR     m_cbstrRowDelim; 
    CComBSTR     m_cbstrQuoteChar; 
    CComBSTR     m_cbstrEscapeChar; 
    VARIANT_BOOL m_fUseHeader; 
    CComBSTR     m_cbstrSortColumn; 
    VARIANT_BOOL m_fSortAscending; 
    CComBSTR     m_cbstrFilterValue; 
    OSPCOMP      m_enumFilterCriterion; 
    CComBSTR     m_cbstrFilterColumn; 
    UINT         m_nCodePage; 
    CComBSTR     m_cbstrLanguage; 
    CComBSTR     m_cbstrDataURL; 
    LCID         m_lcidRead; 
    boolean      m_fDataURLChanged; 
    HRESULT      m_hrDownloadStatus; 
    LONG         m_lTimer; 
    CComBSTR     m_cbstrFilterExpr; 
    CComBSTR     m_cbstrSortExpr; 
    VARIANT_BOOL m_fAppendData; 
    VARIANT_BOOL m_fCaseSensitive; 
    boolean      m_fInReset; 
 
    OLEDBSimpleProvider *m_pSTD; 
    CTDCArr      *m_pArr; 
    IMultiLanguage  *m_pML; 
    BOOL         m_fSecurityChecked; 
 
// ;begin_internal 
    DATASRCListener *m_pDATASRCListener; 
// ;end_internal 
    DataSourceListener *m_pDataSourceListener; 
    CEventBroker *m_pEventBroker; 
 
 
    //  These member objects are used while parsing the input stream 
    // 
    CTDCUnify       *m_pUnify; 
    CComObject<CMyBindStatusCallback<CTDCCtl> > *m_pBSC; 
 
// These members and methods expose the ITDCCtl interface 
// 
public: 
 
    //  Control Properties 
    // 
    STDMETHOD(get_FieldDelim)(BSTR* pbstrFieldDelim); 
    STDMETHOD(put_FieldDelim)(BSTR bstrFieldDelim); 
    STDMETHOD(get_RowDelim)(BSTR* pbstrRowDelim); 
    STDMETHOD(put_RowDelim)(BSTR bstrRowDelim); 
    STDMETHOD(get_TextQualifier)(BSTR* pbstrTextQualifier); 
    STDMETHOD(put_TextQualifier)(BSTR bstrTextQualifier); 
    STDMETHOD(get_EscapeChar)(BSTR* pbstrEscapeChar); 
    STDMETHOD(put_EscapeChar)(BSTR bstrEscapeChar); 
    STDMETHOD(get_UseHeader)(VARIANT_BOOL* pfUseHeader); 
    STDMETHOD(put_UseHeader)(VARIANT_BOOL fUseHeader); 
    STDMETHOD(get_SortColumn)(BSTR* pbstrSortColumn); 
    STDMETHOD(put_SortColumn)(BSTR bstrSortColumn); 
    STDMETHOD(get_SortAscending)(VARIANT_BOOL* pfSortAscending); 
    STDMETHOD(put_SortAscending)(VARIANT_BOOL fSortAscending); 
    STDMETHOD(get_FilterValue)(BSTR* pbstrFilterValue); 
    STDMETHOD(put_FilterValue)(BSTR bstrFilterValue); 
    STDMETHOD(get_FilterCriterion)(BSTR* pbstrFilterCriterion); 
    STDMETHOD(put_FilterCriterion)(BSTR bstrFilterCriterion); 
    STDMETHOD(get_FilterColumn)(BSTR* pbstrFilterColumn); 
    STDMETHOD(put_FilterColumn)(BSTR bstrFilterColumn); 
    STDMETHOD(get_CharSet)(BSTR *pbstrCharSet); 
    STDMETHOD(put_CharSet)(BSTR bstrCharSet); 
    STDMETHOD(get_Language)(BSTR* pbstrLanguage); 
    STDMETHOD(put_Language_)(LPWCH pwchLanguage); 
    STDMETHOD(put_Language)(BSTR bstrLanguage); 
    STDMETHOD(get_CaseSensitive)(VARIANT_BOOL *pfCaseSensitive); 
    STDMETHOD(put_CaseSensitive)(VARIANT_BOOL fCaseSensitive); 
    STDMETHOD(get_DataURL)(BSTR* pbstrDataURL); //  
    STDMETHOD(put_DataURL)(BSTR bstrDataURL); 
// ;begin_internal 
//    STDMETHOD(get_RefreshInterval)(LONG* plTimer); 
//    STDMETHOD(put_RefreshInterval)(LONG lTimer); 
// ;end_internal 
    STDMETHOD(get_Filter)(BSTR* pbstrFilterExpr); 
    STDMETHOD(put_Filter)(BSTR bstrFilterExpr); 
    STDMETHOD(get_Sort)(BSTR* pbstrSortExpr); 
    STDMETHOD(put_Sort)(BSTR bstrSortExpr); 
    STDMETHOD(get_AppendData)(VARIANT_BOOL* pfAppendData); 
    STDMETHOD(put_AppendData)(VARIANT_BOOL fAppendData); 
    STDMETHOD(get_OSP)(OLEDBSimpleProviderX ** ppISTD); 
 
    STDMETHOD(get_ReadyState)(LONG *lReadyState); 
    STDMETHOD(put_ReadyState)(LONG lReadyState); 
 
    // Override IPersistPropertyBagImpl::Load 
    STDMETHOD(Load)(LPPROPERTYBAG pPropBag, LPERRORLOG pErrorLog); 
 
    void UpdateReadyState(LONG lReadyState); 
    //  Data source notification methods 
    STDMETHOD(msDataSourceObject)(BSTR qualifier, IUnknown **ppUnk); 
    STDMETHOD(addDataSourceListener)(IUnknown *pEvent); 
 
    //  Control Methods 
    // 
    STDMETHOD(Reset)(); 
    STDMETHOD(_OnTimer)(void); 
 
private: 
    STDMETHOD(CreateTDCArr)(boolean fAppend); 
    STDMETHOD(ReleaseTDCArr)(boolean fReplacing); 
    void LockBSC(); 
    void UnlockBSC(); 
    STDMETHOD(InitiateDataLoad)(boolean fAppend); 
    STDMETHOD(SecurityCheckDataURL)(LPOLESTR pszURL); 
    STDMETHOD(SecurityMatchAllowDomainList)(); 
    STDMETHOD(SecurityMatchProtocols)(LPOLESTR pszURL); 
    STDMETHOD(TerminateDataLoad)(); 
    BSTR bstrConstructSortExpr(); 
    BSTR bstrConstructFilterExpr(); 
 
protected: 
    void OnData(CMyBindStatusCallback<CTDCCtl> *pbsc, BYTE *pBytes, DWORD dwSize); 
};