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