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);
};