BSCBCK.H

// CBindStatusCallback2 -- we want a little more info than it is giving us, such as  
// C&P'ed from ATL -- see ADDED below for additons, CHANGED for changes...

// PreBindMoniker() & OnBindingFailure() must be implemented in using class T

template <class T>
class ATL_NO_VTABLE CBindStatusCallback2 :
public CComObjectRootEx<T::_ThreadModel::ThreadModelNoCS>,
public IBindStatusCallbackImpl<T>
{
// CHANGED from CBindStatusCallback
typedef void (T::*ATL_PDATAAVAILABLE)(CBindStatusCallback2<T>* pbsc, BYTE* pBytes, DWORD dwSize,
DWORD grfBSCF, FORMATETC *pformatetc, STGMEDIUM *pstgmed);
// -------

public:

BEGIN_COM_MAP(CBindStatusCallback2<T>)
COM_INTERFACE_ENTRY_IID(IID_IBindStatusCallback, IBindStatusCallbackImpl<T>)
END_COM_MAP()


CBindStatusCallback2()
{
m_pT = NULL;
m_pFunc = NULL;
}
~CBindStatusCallback2()
{
ATLTRACE(_T("~CBindStatusCallback2\n"));
}

STDMETHOD(OnStartBinding)(DWORD dwReserved, IBinding *pBinding)
{
ATLTRACE(_T("CBindStatusCallback2::OnStartBinding\n"));
m_spBinding = pBinding;
return S_OK;
}

STDMETHOD(GetPriority)(LONG *pnPriority)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback2::GetPriority"));
}

STDMETHOD(OnLowResource)(DWORD reserved)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback2::OnLowResource"));
}

STDMETHOD(OnProgress)(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
//ATLTRACENOTIMPL(_T("CBindStatusCallback2::OnProgress"));
ATLTRACE(_T("CBindStatusCallback2::OnProgress : progress=%d / %d - status=%x\n"), ulProgress, ulProgressMax, ulStatusCode);
_bstr_t strStatusText = szStatusText;
ATLTRACE(_T(" status-text=%s\n"),(LPCTSTR)strStatusText);
return E_NOTIMPL;
}

STDMETHOD(OnStopBinding)(HRESULT hresult, LPCWSTR szError)
{
ATLTRACE(_T("CBindStatusCallback2::OnStopBinding\n"));

if (FAILED(hresult))
m_pT->OnBindingFailure(hresult, szError);

m_spBinding.Release();// ADDED
m_spBindCtx.Release();
m_spMoniker.Release();

return S_OK;
}

STDMETHOD(GetBindInfo)(DWORD *pgrfBINDF, BINDINFO *pbindInfo)
{
ATLTRACE(_T("CBindStatusCallback2::GetBindInfo\n"));
if (!pbindInfo || !pbindInfo->cbSize || !pgrfBINDF)
return E_INVALIDARG;
*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE |
BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE;

// NOTE: BINDINFO has changed sizes between IE3 and IE4
// Relying on a compiled sizeof(BINDINFO) can cause trouble.
// The following changes will propagate to later versions of
// ATL.

// Remember incoming cbSize
ULONG cbSize = pbindInfo->cbSize;

memset(pbindInfo, 0, cbSize);

// Restore cbSize
pbindInfo->cbSize = cbSize;
pbindInfo->dwBindVerb = BINDVERB_GET;

return S_OK;
}

STDMETHOD(OnDataAvailable)(DWORD grfBSCF, DWORD dwSize, FORMATETC *pformatetc, STGMEDIUM *pstgmed)
{
ATLTRACE(_T("CBindStatusCallback2::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;
/*if (m_spStream)
m_spStream->AddRef();*/ // CHANGED
}
}

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)
{
// CHANGED
(m_pT->*m_pFunc)(this, pBytes, dwActuallyRead, grfBSCF, pformatetc, pstgmed);
// -------
m_dwTotalRead += dwActuallyRead;
}
}
delete[] pBytes;
}
}

if (BSCF_LASTDATANOTIFICATION & grfBSCF)
m_spStream.Release();
return hr;
}

STDMETHOD(OnObjectAvailable)(REFIID riid, IUnknown *punk)
{
ATLTRACENOTIMPL(_T("CBindStatusCallback2::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;
if (spServiceProvider)
spServiceProvider->QueryService(SID_IBindHost, IID_IBindHost, (void**)&spBindHost);

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

// ADDED to CBindStatusCallback
if (SUCCEEDED(hr))
hr = m_pT->PreBindMoniker(m_spBindCtx, m_spMoniker);
// -----

if (SUCCEEDED(hr))
{
IStream* pStream;
hr = m_spMoniker->BindToStorage(m_spBindCtx, 0, IID_IStream, (void**)&pStream);
if (pStream != NULL) // ADDED
pStream->Release();
}
}
else
{
hr = CreateBindCtx(0, &m_spBindCtx);

/*if (SUCCEEDED(hr))
hr = RegisterBindStatusCallback(m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), 0, 0L);*/
// If we're working through the IBindHost interface, we should specify the
// IBSC on MonikerBindToStorage, not registered on the IBindCtx

if (SUCCEEDED(hr))
{
if (bRelative)
hr = spBindHost->CreateMoniker(bstrURL, m_spBindCtx, &m_spMoniker, 0);
else
hr = CreateURLMoniker(NULL, bstrURL, &m_spMoniker);
}

// ADDED
if (SUCCEEDED(hr))
hr = m_pT->PreBindMoniker(m_spBindCtx, m_spMoniker);
// -----

if (SUCCEEDED(hr))
{
IStream* pStream;
hr = spBindHost->MonikerBindToStorage(m_spMoniker, m_spBindCtx, reinterpret_cast<IBindStatusCallback*>(static_cast<IBindStatusCallbackImpl<T>*>(this)), IID_IStream, (void**)&pStream);
if (pStream != NULL) // ADDED
pStream->Release();
ATLTRACE(_T("Bound"));
}
}
return hr;
}

HRESULT StartAsyncDownload(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
{
m_pT = pT;
m_pFunc = pFunc;
return _StartAsyncDownload(bstrURL, pUnkContainer, bRelative);
}

static HRESULT Download(T* pT, ATL_PDATAAVAILABLE pFunc, BSTR bstrURL, IUnknown* pUnkContainer = NULL, BOOL bRelative = FALSE)
{
CComObject<CBindStatusCallback2<T> > *pbsc;
HRESULT hRes = CComObject<CBindStatusCallback2<T> >::CreateInstance(&pbsc);
if (FAILED(hRes))
return hRes;
return pbsc->StartAsyncDownload(pT, pFunc, bstrURL, pUnkContainer, bRelative);
}

CComPtr<IMoniker> m_spMoniker;
CComPtr<IBindCtx> m_spBindCtx;
CComPtr<IBinding> m_spBinding;
CComPtr<IStream> m_spStream;
T* m_pT;
ATL_PDATAAVAILABLE m_pFunc;
DWORD m_dwTotalRead;
DWORD m_dwAvailableToRead;
};