PROGRESS.CPP

// =========================================================================== 
// File: P R O G R E S S . C P P
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Description:
//
// This sample demonstrates using a URL moniker to download information.
// The key routines include the implementation of IBindStatusCallback
// and the CDownload::DoDownload routine, which creates and binds to the
// URL moniker.
//
// Instructions:
//
// To use this sample:
// * build it using the NMAKE command. NMAKE will create PROGRESS.EXE.
// * run PROGRESS.EXE. specify the resource to download by passing an
// URL on the command-line. use no command-line argument to default to
// downloading "http://www.msn.com".
// * The program displays a dialog box containing information about the
// download:
// - a status message, describing the current status of the download
// - a progress message, describing the amount of information that
// has been downloaded.
// - a text box, which displays chunks of the download information as
// it arrives.
// * Press the "GO" button to begin the download.
//
// Sample update:-
// * New feature include Progress Bar to indicated progress of the download
// * Edit box replaces the old text box. Also the whole file can be viewed.
// If the file exceeds 32KB then only the first 32 KB from the last data
// pull will be displayed. If the pull exceeds 32 KB, then only the first
// 32 KB of the last Read will be displayed.
//
///
// File updated by Jason Strayer 27-Aug-1997
// File updated by Jobi George 19-June-1996
// File updated by Ramesha Gopalakrishna 28-June-1996
// File updated by Oliver Wallace 9-July-1996
// Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
// ===========================================================================
#include "urlmon.h"
#include "wininet.h"
#include "resource.h"
#include "commctrl.h"

#define EDIT_BOX_LIMIT 0x7FFF // The Edit box limit

// %%Classes: ----------------------------------------------------------------
class CDownload {
public:
CDownload(LPCWSTR szURL);
~CDownload();
HRESULT DoDownload(HWND,HWND,HWND,HWND);
LPCWSTR m_url;

private:
IMoniker* m_pmk;
IBindCtx* m_pbc;
IBindStatusCallback* m_pbsc;
};

class CBindStatusCallback : public IBindStatusCallback {
public:
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid,void ** ppv);
STDMETHODIMP_(ULONG) AddRef() { return m_cRef++; }
STDMETHODIMP_(ULONG) Release() { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; }

// IBindStatusCallback methods
STDMETHODIMP OnStartBinding(DWORD dwReserved, IBinding* pbinding);
STDMETHODIMP GetPriority(LONG* pnPriority);
STDMETHODIMP OnLowResource(DWORD dwReserved);
STDMETHODIMP OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode,
LPCWSTR pwzStatusText);
STDMETHODIMP OnStopBinding(HRESULT hrResult, LPCWSTR szError);
STDMETHODIMP GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindinfo);
STDMETHODIMP OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc,
STGMEDIUM* pstgmed);
STDMETHODIMP OnObjectAvailable(REFIID riid, IUnknown* punk);

// constructors/destructors
CBindStatusCallback(HWND hwndStatus, HWND hwndProgress, HWND hwndText, HWND hwndProgressBar);
~CBindStatusCallback();

inline void SetStatus(LPCWSTR szStatus) { SetWndText(m_hwndStatus, szStatus); }
void SetProgress(LPCWSTR szProgress) { SetWndText(m_hwndProgress, szProgress); }
void SetProgressBar(ULONG cProgress, ULONG maxProgress)
{
// Set the range and increment of the progress bar.
SendMessage(m_hwndProgressBar, PBM_SETRANGE, 0, 100);
SendMessage(m_hwndProgressBar, PBM_SETPOS, (WPARAM) (maxProgress ? cProgress * 100 / maxProgress : 0), 0);
}
void SetWndText(HWND hwnd, LPCWSTR szText);

// data members
DWORD m_cRef;
IBinding* m_pbinding;
IStream* m_pstm;
HWND m_hwndStatus;
HWND m_hwndProgress;
HWND m_hwndText;
HWNDm_hwndProgressBar;
DWORD m_cbOld;
};


// ===========================================================================
// CBindStatusCallback Implementation
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::CBindStatusCallback(HWND hwndStatus, HWND hwndProgress, HWND hwndText, HWND hwndProgressBar)
{
m_hwndStatus = hwndStatus;
m_hwndProgress = hwndProgress;
m_hwndText = hwndText;
m_hwndProgressBar = hwndProgressBar;
m_pbinding = NULL;
m_pstm = NULL;
m_cRef = 1;
m_cbOld = 0;
} // CBindStatusCallback

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::~CBindStatusCallback
// ---------------------------------------------------------------------------
CBindStatusCallback::~CBindStatusCallback()
{
} // ~CBindStatusCallback

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::SetWndText
// ---------------------------------------------------------------------------
inline void
CBindStatusCallback::SetWndText(HWND hwnd,LPCWSTR szText)
{
if (IsWindow(hwnd))
{
char rgchBuf[INTERNET_MAX_PATH_LENGTH];
WideCharToMultiByte(CP_ACP, 0, szText, -1, rgchBuf, INTERNET_MAX_PATH_LENGTH, 0, 0);
SetWindowText(hwnd, rgchBuf);
}
} // CBindStatusCallback::SetWndText

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::QueryInterface
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::QueryInterface(REFIID riid, void** ppv)
{
*ppv = NULL;

if (riid==IID_IUnknown || riid==IID_IBindStatusCallback)
{
*ppv = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
} // CBindStatusCallback::QueryInterface

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStartBinding
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnStartBinding(DWORD dwReserved, IBinding* pbinding)
{
if (m_pbinding != NULL)
m_pbinding->Release();
m_pbinding = pbinding;
if (m_pbinding != NULL)
{
m_pbinding->AddRef();
SetStatus(L"Status: Starting to bind...");
}
return S_OK;
} // CBindStatusCallback::OnStartBinding

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetPriority
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetPriority(LONG* pnPriority)
{
return E_NOTIMPL;
} // CBindStatusCallback::GetPriority

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnLowResource
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnLowResource(DWORD dwReserved)
{
return E_NOTIMPL;
} // CBindStatusCallback::OnLowResource

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnProgress
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR szStatusText)
{
char sz[255];

if(szStatusText!=NULL)
WideCharToMultiByte(CP_ACP, 0, szStatusText,-1, sz, 255, 0,0);

char msg[100];
WCHAR out[200];

wsprintf(msg,"Progress: %s %d of %d ", sz, ulProgress, (ulProgress>ulProgressMax)?ulProgress:ulProgressMax);
MultiByteToWideChar(CP_ACP, 0, msg, -1, out, sizeof(out));

SetProgress(out);
SetProgressBar(ulProgress, ulProgressMax);
return(NOERROR);
} // CBindStatusCallback::OnProgress

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnStopBinding
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnStopBinding(HRESULT hrStatus, LPCWSTR pszError)
{
if (hrStatus)
SetStatus(L"Status: File download Failed.");

if (m_pbinding)
{
m_pbinding->Release();
m_pbinding = NULL;
}

return S_OK;
} // CBindStatusCallback::OnStopBinding

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::GetBindInfo
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo)
{
if (!pbindInfo || !pbindInfo->cbSize || !pgrfBINDF)
return E_INVALIDARG;

*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA |
BINDF_GETNEWESTVERSION | BINDF_NOWRITECACHE;

// remember incoming cbSize
ULONG cbSize = pbindInfo->cbSize;
// zero out structure
memset(pbindInfo, 0, cbSize);

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

return S_OK;
} // CBindStatusCallback::GetBindInfo

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnDataAvailable
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC* pfmtetc, STGMEDIUM* pstgmed)
{
HRESULT hr=S_OK;
DWORD dStrlength=0;

// Get the Stream passed
if (BSCF_FIRSTDATANOTIFICATION & grfBSCF)
{
if (!m_pstm && pstgmed->tymed == TYMED_ISTREAM)
{
m_pstm = pstgmed->pstm;
if (m_pstm)
m_pstm->AddRef();
}
}

// If there is some data to be read then go ahead and read them
if (m_pstm && dwSize > m_cbOld)
{
DWORD dwRead = dwSize - m_cbOld; // Minimum amount available that hasn't been read
DWORD dwActuallyRead = 0; // Placeholder for amount read during this pull

if (dwRead > 0)
do
{
char * pNewstr = new char[dwRead + 1];
if (pNewstr==NULL)
return S_FALSE;
hr=m_pstm->Read(pNewstr,dwRead,&dwActuallyRead);
pNewstr[dwActuallyRead] = 0;
// If we really read something then lets add it to the Edit box
if (dwActuallyRead>0)
{
dStrlength=GetWindowTextLength(m_hwndText);

char *pOldstr=NULL;
if (dwActuallyRead>=EDIT_BOX_LIMIT) // Read exceeded Edit box size
{
pOldstr= new char[EDIT_BOX_LIMIT];
if (pOldstr==NULL)
{
delete[] pNewstr;
return S_FALSE;
}
lstrcpyn(pOldstr,pNewstr,EDIT_BOX_LIMIT-1);
}
else if (dStrlength + dwActuallyRead >= EDIT_BOX_LIMIT)
{
// This pull exceeded the edit box size. Only copy the last read.
pOldstr = new char[dwActuallyRead+1];
if (pOldstr==NULL)
{
delete[] pNewstr;
return S_FALSE;
}
lstrcpyn(pOldstr,pNewstr,dwActuallyRead);
}
else
{
pOldstr= new char[dStrlength+dwActuallyRead+1];
if (pOldstr==NULL)
{
delete[] pNewstr;
return S_FALSE;
}
GetWindowText(m_hwndText,pOldstr,(dStrlength+dwActuallyRead+1));
lstrcat(pOldstr,pNewstr);
}
SetWindowText(m_hwndText,pOldstr);
m_cbOld += dwActuallyRead;
delete[] pOldstr;
}
delete[] pNewstr;

} while (!(hr == E_PENDING || hr == S_FALSE) && SUCCEEDED(hr));
}// if (m_pstm && dwSize > m_cbOld)

if (BSCF_LASTDATANOTIFICATION & grfBSCF)
{
if (m_pstm)
m_pstm->Release();
hr=S_OK; // If it was the last data then we should return S_OK as we just finished reading everything
SetStatus(L"Status: File downloaded.");
}

return hr;
} // CBindStatusCallback::OnDataAvailable

// ---------------------------------------------------------------------------
// %%Function: CBindStatusCallback::OnObjectAvailable
// ---------------------------------------------------------------------------
STDMETHODIMP
CBindStatusCallback::OnObjectAvailable(REFIID riid, IUnknown* punk)
{
return E_NOTIMPL;
} // CBindStatusCallback::OnObjectAvailable


// ===========================================================================
// CDownload Implementation
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: CDownload::CDownload
// ---------------------------------------------------------------------------
CDownload::CDownload(LPCWSTR szURL)
{
m_url = szURL;
m_pmk = 0;
m_pbc = 0;
m_pbsc = 0;
} // CDownload

// ---------------------------------------------------------------------------
// %%Function: CDownload::~CDownload
// ---------------------------------------------------------------------------
CDownload::~CDownload()
{
if (m_pmk)
m_pmk->Release();
if (m_pbc)
m_pbc->Release();
if (m_pbsc)
m_pbsc->Release();
} // ~CDownload

// ---------------------------------------------------------------------------
// %%Function: CDownload::DoDownload
// ---------------------------------------------------------------------------
HRESULT
CDownload::DoDownload(HWND hwndStatus, HWND hwndProgress, HWND hwndText, HWND hwndProgressBar)
{
IStream* pstm = NULL;
HRESULT hr;

hr = CreateURLMoniker(NULL, m_url, &m_pmk);
if (FAILED(hr))
goto LErrExit;

m_pbsc = new CBindStatusCallback(hwndStatus, hwndProgress, hwndText, hwndProgressBar);
if (m_pbsc == NULL)
{
hr = E_OUTOFMEMORY;
goto LErrExit;
}

hr = CreateBindCtx(0, &m_pbc);
if (FAILED(hr))
goto LErrExit;

hr = RegisterBindStatusCallback(m_pbc,
m_pbsc,
0,
0L);
if (FAILED(hr))
goto LErrExit;

hr = m_pmk->BindToStorage(m_pbc, 0, IID_IStream, (void**)&pstm);
if (FAILED(hr))
goto LErrExit;

return hr;

LErrExit:
if (m_pbc != NULL)
{
m_pbc->Release();
m_pbc = NULL;
}
if (m_pbsc != NULL)
{
m_pbsc->Release();
m_pbsc = NULL;
}
if (m_pmk != NULL)
{
m_pmk->Release();
m_pmk = NULL;
}
if (pstm)
{
pstm->Release();
pstm = NULL;
}
return hr;
} // CDownload::DoDownload


// ===========================================================================
// User Interface and Initialization Routines
// ===========================================================================

// ---------------------------------------------------------------------------
// %%Function: DialogProc
// ---------------------------------------------------------------------------
BOOL CALLBACK
DialogProc(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
static CDownload* pcdl = NULL;

switch(message)
{
case WM_INITDIALOG:
EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE);
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), TRUE);
pcdl = (CDownload*)(LPVOID)(lParam);
break;

case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDOK:
{
char rgchBuf[INTERNET_MAX_PATH_LENGTH];
HWND hwndStatus = GetDlgItem(hwndDlg, IDC_DISPLAY);
HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESS);
HWND hwndText = GetDlgItem(hwndDlg, IDC_EDIT);
HWND hwndProgressBar = GetDlgItem(hwndDlg, IDC_PROGRESSBAR);

EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), FALSE);

WideCharToMultiByte(CP_ACP, 0, pcdl->m_url, -1, rgchBuf, MAX_PATH, 0, 0);
SetWindowText(hwndDlg, rgchBuf);

SetWindowText(hwndStatus, TEXT("Status: Initiating Bind..."));
pcdl->DoDownload(hwndStatus, hwndProgress, hwndText, hwndProgressBar);
EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), TRUE);
break;
}

case IDCANCEL:
EndDialog(hwndDlg,0);
return 1;
}
break;
}
}

return 0;
} // DialogProc

// ---------------------------------------------------------------------------
// %%Function: WinMain
// ---------------------------------------------------------------------------
int WINAPI
WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR szCmdLine, int nCmdShow)
{
static WCHAR szDefaultURL[] = L"http://www.msn.com";
WCHAR rgwchCmdLine[MAX_PATH];
HRESULT hr;

InitCommonControls();

hr = CoInitialize(NULL);
if (FAILED(hr))
return hr;

MultiByteToWideChar(CP_ACP, 0, szCmdLine, -1, rgwchCmdLine, MAX_PATH);

CDownload download(rgwchCmdLine[0] ? rgwchCmdLine : szDefaultURL);

int x = DialogBoxParam(hinst, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP,
DialogProc, (LPARAM)(LPVOID)&download);

CoUninitialize();

return 0;
} // WinMain

// EOF =======================================================================