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 =======================================================================