You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
752 lines
18 KiB
752 lines
18 KiB
#include "inspch.h"
|
|
#include "util2.h"
|
|
#include "download.h"
|
|
|
|
#define BUFFERSIZE 4096
|
|
char g_szBuffer[BUFFERSIZE];
|
|
|
|
#define TIMEOUT_PERIOD 120
|
|
#define PATCHWIN9xKEY "SOFTWARE\\Microsoft\\Advanced INF Setup"
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
CDownloader::CDownloader() : CTimeTracker(0)
|
|
{
|
|
_pCb = NULL;
|
|
_pBnd = NULL;
|
|
_cRef = 1;
|
|
_pStm = NULL;
|
|
_pMkr = NULL;
|
|
_uFlags = NULL;
|
|
_hDL = CreateEvent(NULL, TRUE, FALSE, NULL);
|
|
DllAddRef();
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
CDownloader::~CDownloader()
|
|
{
|
|
if(_hDL)
|
|
CloseHandle(_hDL);
|
|
|
|
DllRelease();
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::QueryInterface(const GUID &riid,void **ppv )
|
|
{
|
|
*ppv = NULL ;
|
|
if( IsEqualGUID(riid,IID_IUnknown) ) {
|
|
*ppv = (IUnknown *) (IBindStatusCallback *)this;
|
|
} else if (IsEqualGUID(riid,IID_IBindStatusCallback) ) {
|
|
*ppv = (IBindStatusCallback *) this;
|
|
} else if (IsEqualGUID(riid, IID_IAuthenticate))
|
|
*ppv = (IAuthenticate *) this;
|
|
|
|
if (*ppv)
|
|
{
|
|
// increment our reference count before we hand out our interface
|
|
((LPUNKNOWN)*ppv)->AddRef();
|
|
return(NOERROR);
|
|
}
|
|
|
|
return( E_NOINTERFACE );
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP_(ULONG) CDownloader::AddRef()
|
|
{
|
|
return(++_cRef);
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP_(ULONG) CDownloader::Release()
|
|
{
|
|
if(!--_cRef)
|
|
{
|
|
delete this;
|
|
return(0);
|
|
}
|
|
return( _cRef );
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::GetBindInfo( DWORD *grfBINDF, BINDINFO *pbindInfo)
|
|
{
|
|
// clear BINDINFO but keep its size
|
|
DWORD cbSize = pbindInfo->cbSize;
|
|
ZeroMemory( pbindInfo, cbSize );
|
|
pbindInfo->cbSize = cbSize;
|
|
|
|
*grfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE | BINDF_PULLDATA | BINDF_RESYNCHRONIZE | BINDF_PREFERDEFAULTHANDLER;
|
|
pbindInfo->dwBindVerb = BINDVERB_GET;
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::OnStartBinding(DWORD /*grfBSCOption*/,IBinding *p)
|
|
{
|
|
// BUGBUG: should check to see options are what we think they are
|
|
EnterCriticalSection(&g_cs);
|
|
_pBnd = p;
|
|
_pBnd->AddRef();
|
|
LeaveCriticalSection(&g_cs);
|
|
return(NOERROR);
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::GetPriority(LONG *pnPriority)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pwzStatusText)
|
|
{
|
|
IndicateWinsockActivity();
|
|
return NOERROR;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pFmtetc, STGMEDIUM *pstgmed)
|
|
{
|
|
// bring in major changes here
|
|
HRESULT hr = NOERROR;
|
|
|
|
DWORD dwRead = 0;
|
|
DWORD dwReadThisCall = 0;
|
|
DWORD dwWritten = 0;
|
|
|
|
if(!_pStm)
|
|
{
|
|
_pStm = pstgmed->pstm;
|
|
_pStm->AddRef();
|
|
}
|
|
|
|
|
|
// should ignore WAIT_TIMEOUT while getting bytes from urlmon
|
|
_fTimeoutValid = FALSE;
|
|
|
|
do
|
|
{
|
|
hr = _pStm->Read(g_szBuffer, BUFFERSIZE, &dwRead);
|
|
if( SUCCEEDED(hr) || ( (hr == E_PENDING) && (dwRead > 0) ) )
|
|
{
|
|
if(_hFile)
|
|
if(WriteFile(_hFile, g_szBuffer, dwRead, &dwWritten, NULL))
|
|
{
|
|
_uBytesSoFar += dwRead;
|
|
dwReadThisCall += dwRead;
|
|
if(_pCb)
|
|
_pCb->OnProgress(_uBytesSoFar >> 10, NULL);
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
Abort();
|
|
}
|
|
}
|
|
} while (hr == NOERROR);
|
|
// SetInstallBytes
|
|
SetBytes(dwReadThisCall, TRUE);
|
|
|
|
_uTickCount = 0;
|
|
_fTimeoutValid = TRUE; // should increment dwTickCount if WAIT_TIMEOUT occurs now
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
|
|
STDMETHODIMP CDownloader::OnObjectAvailable(REFIID riid, IUnknown *punk)
|
|
{
|
|
return(E_NOTIMPL);
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::OnLowResource(DWORD reserved)
|
|
{
|
|
// BUGBUG: really should have this kind of harsh policy on this ...
|
|
_pBnd->Abort();
|
|
return(S_OK);
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
STDMETHODIMP CDownloader::OnStopBinding(HRESULT hrError, LPCWSTR szError)
|
|
{
|
|
_fTimeoutValid = FALSE;
|
|
StopClock();
|
|
|
|
if((hrError == E_ABORT) && _fTimeout)
|
|
{
|
|
// This is the timeout case
|
|
_hDLResult = INET_E_CONNECTION_TIMEOUT;
|
|
}
|
|
else
|
|
{
|
|
// this is all other cases
|
|
_hDLResult = hrError;
|
|
}
|
|
|
|
SetEvent(_hDL);
|
|
return(NOERROR);
|
|
}
|
|
|
|
/* IAuthenticate::Authenticate
|
|
*/
|
|
|
|
STDMETHODIMP CDownloader::Authenticate(HWND *phwnd,
|
|
LPWSTR *pszUserName, LPWSTR *pszPassword)
|
|
{
|
|
if (!phwnd || !pszUserName || !pszPassword)
|
|
return E_POINTER;
|
|
|
|
*pszUserName = NULL;
|
|
*pszPassword = NULL;
|
|
|
|
// BUGBUG: Need to have our own window! NULL does not work!
|
|
// *phwnd = NULL;
|
|
*phwnd = GetDesktopWindow() ;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
HRESULT GetAMoniker( LPOLESTR url, IMoniker ** ppmkr )
|
|
{
|
|
// FUTURE: This really should be a call to MkParseDisplayNameEx!!!
|
|
HRESULT hr = CreateURLMoniker(0,url,ppmkr);
|
|
// hr = ::MkParseDisplayNameEx(0, url, 0, ppmkr);
|
|
return( hr );
|
|
}
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
|
|
HRESULT CDownloader::SetupDownload(LPCSTR pszUrl, IMyDownloadCallback *pcb, DWORD dwFlags, LPCSTR pszFilenameToUse)
|
|
{
|
|
LPOLESTR pwszUrl;
|
|
LPSTR pszFilename;
|
|
|
|
if(!pszUrl) return E_INVALIDARG;
|
|
|
|
lstrcpyn(_szURL, pszUrl, INTERNET_MAX_URL_LENGTH);
|
|
|
|
pwszUrl = OLESTRFROMANSI(pszUrl);
|
|
if(!pwszUrl) return E_OUTOFMEMORY;
|
|
|
|
IMoniker *ptmpmkr;
|
|
|
|
HRESULT hr = GetAMoniker( pwszUrl, &ptmpmkr );
|
|
|
|
IBindCtx * pBindCtx = 0;
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
if(SUCCEEDED(::CreateBindCtx(0,&pBindCtx)))
|
|
hr = ::RegisterBindStatusCallback(pBindCtx, (IBindStatusCallback *) this, 0, 0) ;
|
|
}
|
|
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
AddRef();
|
|
|
|
// setup path for download
|
|
if(FAILED( CreateTempDirOnMaxDrive(_szDest, sizeof(_szDest))))
|
|
goto GetOut;
|
|
if(pszFilenameToUse)
|
|
{
|
|
SafeAddPath(_szDest, pszFilenameToUse, sizeof(_szDest));
|
|
}
|
|
else
|
|
{
|
|
pszFilename = ParseURLA(pszUrl);
|
|
SafeAddPath(_szDest, pszFilename, sizeof(_szDest));
|
|
}
|
|
|
|
_pMkr = ptmpmkr;
|
|
_pCb = pcb;
|
|
_uFlags = dwFlags;
|
|
_pBndContext = pBindCtx;
|
|
_fTimeout = FALSE;
|
|
_fTimeoutValid = TRUE;
|
|
_uBytesSoFar = 0;
|
|
_uTickCount = 0;
|
|
_pStm = 0;
|
|
}
|
|
|
|
GetOut:
|
|
if(pwszUrl)
|
|
CoTaskMemFree(pwszUrl);
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDownloader::DoDownload(LPSTR pszPath, DWORD dwBufSize)
|
|
{
|
|
HRESULT hr = NOERROR;
|
|
BOOL fQuit = FALSE;
|
|
DWORD dwRet;
|
|
|
|
if(!_pMkr)
|
|
return E_UNEXPECTED;
|
|
|
|
pszPath[0] = 0;
|
|
|
|
StartClock();
|
|
|
|
// Create the file
|
|
_hFile = CreateFile(_szDest, GENERIC_READ | GENERIC_WRITE, 0, NULL,
|
|
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
if(_hFile == INVALID_HANDLE_VALUE)
|
|
hr = E_FAIL;
|
|
|
|
|
|
if( SUCCEEDED(hr) )
|
|
hr = _pMkr->BindToStorage( _pBndContext, 0, IID_IStream, (void**)&_pStm );
|
|
|
|
// we need this here because it synchronus *FAIL* case,
|
|
// we Set the event in onstopbinding, but we skip the loop below so it
|
|
// never gets reset.
|
|
// If BindToStorage fails without even sending onstopbinding, we are resetting
|
|
// an unsignalled event, which is OK.
|
|
if(FAILED(hr))
|
|
ResetEvent(_hDL);
|
|
|
|
_pBndContext->Release();
|
|
_pBndContext = 0;
|
|
|
|
// here we wait for Bind to complete
|
|
//Wait for download event or abort
|
|
while(SUCCEEDED(hr) && !fQuit)
|
|
{
|
|
dwRet = MsgWaitForMultipleObjects(1, &_hDL, FALSE, 1000, QS_ALLINPUT);
|
|
if(dwRet == WAIT_OBJECT_0)
|
|
{
|
|
// Download is finished
|
|
hr = _hDLResult;
|
|
ResetEvent(_hDL);
|
|
break;
|
|
}
|
|
else if(dwRet == WAIT_TIMEOUT) // our wait has expired
|
|
{
|
|
if(_fTimeoutValid)
|
|
_uTickCount++;
|
|
|
|
// if our tick count is past threshold, abort the download
|
|
// BUGBUG: What about synch. case? We can't time out
|
|
if(_uTickCount >= TIMEOUT_PERIOD)
|
|
{
|
|
_fTimeout = TRUE;
|
|
Abort();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MSG msg;
|
|
// read all of the messages in this next loop
|
|
// removing each message as we read it
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
// if it's a quit message we're out of here
|
|
if (msg.message == WM_QUIT)
|
|
fQuit = TRUE;
|
|
else
|
|
{
|
|
// otherwise dispatch it
|
|
DispatchMessage(&msg);
|
|
} // end of PeekMessage while loop
|
|
}
|
|
}
|
|
}
|
|
|
|
// clean up all our stuff
|
|
if(_hFile != INVALID_HANDLE_VALUE)
|
|
CloseHandle(_hFile);
|
|
_hFile = INVALID_HANDLE_VALUE;
|
|
|
|
// if we are not using cache and download succeeded, delete from cache
|
|
if(SUCCEEDED(hr) && !(_uFlags & DOWNLOADFLAGS_USEWRITECACHE))
|
|
DeleteUrlCacheEntry(_szURL);
|
|
|
|
|
|
if(FAILED(hr))
|
|
{
|
|
GetParentDir(_szDest);
|
|
DelNode(_szDest,0);
|
|
}
|
|
|
|
EnterCriticalSection(&g_cs);
|
|
if(_pBnd)
|
|
{
|
|
_pBnd->Release();
|
|
_pBnd = 0;
|
|
}
|
|
LeaveCriticalSection(&g_cs);
|
|
|
|
_pCb = 0;
|
|
|
|
if(_pStm)
|
|
{
|
|
_pStm->Release();
|
|
_pStm = 0;
|
|
}
|
|
|
|
if(SUCCEEDED(hr))
|
|
lstrcpyn(pszPath, _szDest, dwBufSize);
|
|
|
|
_szDest[0] = 0;
|
|
_szURL[0] = 0;
|
|
Release();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CDownloader::Suspend()
|
|
{
|
|
|
|
// in theory, we could call _pBnd->Suspend here
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
HRESULT CDownloader::Resume()
|
|
{
|
|
|
|
// in theory, we could call _pBnd->Resume here
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
HRESULT CDownloader::Abort()
|
|
{
|
|
EnterCriticalSection(&g_cs);
|
|
if(_pBnd)
|
|
{
|
|
_pBnd->Abort();
|
|
}
|
|
LeaveCriticalSection(&g_cs);
|
|
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
//=--------------------------------------------------------------------------=
|
|
// Function name here
|
|
//=--------------------------------------------------------------------------=
|
|
// Function description
|
|
//
|
|
// Parameters:
|
|
//
|
|
// Returns:
|
|
//
|
|
// Notes:
|
|
//
|
|
/*
|
|
HRESULT GetAMoniker( LPOLESTR url, IMoniker ** ppmkr )
|
|
{
|
|
// FUTURE: This really should be a call to MkParseDisplayNameEx!!!
|
|
HRESULT hr = CreateURLMoniker(0,url,ppmkr);
|
|
// hr = ::MkParseDisplayNameEx(0, url, 0, ppmkr);
|
|
return( hr );
|
|
}
|
|
*/
|
|
|
|
CPatchDownloader::CPatchDownloader(BOOL fEnable=FALSE) : CTimeTracker(0), _fEnable(fEnable)
|
|
{
|
|
}
|
|
|
|
CPatchDownloader::~CPatchDownloader()
|
|
{
|
|
;
|
|
}
|
|
|
|
HRESULT CPatchDownloader::SetupDownload(DWORD dwFullTotalSize, UINT uPatchCount, IMyDownloadCallback *pcb, LPCSTR pszDLDir)
|
|
{
|
|
_dwFullTotalSize = dwFullTotalSize;
|
|
_pCb = pcb;
|
|
_uNumDownloads = uPatchCount;
|
|
if (pszDLDir)
|
|
lstrcpyn(_szPath, pszDLDir, sizeof(_szPath));
|
|
else
|
|
lstrcpy(_szPath, "");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT CPatchDownloader::DoDownload(LPCTSTR szFile)
|
|
{
|
|
HINF hInf = NULL;
|
|
HRESULT hr = S_OK;
|
|
|
|
// We shouldn't be called if patching isn't available.
|
|
if (!IsEnabled())
|
|
return E_FAIL;
|
|
|
|
// TODO: Advpext currently behaves as a synchronous call, so
|
|
// right now we can't do timeouts and progress bar ticks.
|
|
StartClock();
|
|
|
|
if(!IsNT())
|
|
{
|
|
DWORD fWin9x = 1;
|
|
HKEY hKey;
|
|
|
|
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, PATCHWIN9xKEY,
|
|
0, KEY_READ | KEY_WRITE, &hKey) == ERROR_SUCCESS)
|
|
{
|
|
RegSetValueEx(hKey, "Usewin9xDirectory", 0, REG_DWORD,
|
|
(LPBYTE)&fWin9x, sizeof(DWORD));
|
|
RegCloseKey(hKey);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (SUCCEEDED(hr = OpenINFEngine(szFile, NULL, 0, &hInf, NULL)))
|
|
{
|
|
hr = g_pfnProcessFileSection(hInf, NULL, TRUE, "DefaultInstall", _szPath, CPatchDownloader::Callback, (LPVOID) this);
|
|
CloseINFEngine(hInf);
|
|
}
|
|
|
|
_pCb = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
BOOL CPatchDownloader::Callback(PATCH_DOWNLOAD_REASON Reason, PVOID lpvInfo, PVOID pvContext)
|
|
{
|
|
if (!pvContext)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
CPatchDownloader *pPatchInst = (CPatchDownloader *) pvContext;
|
|
|
|
switch (Reason)
|
|
{
|
|
case PATCH_DOWNLOAD_ENDDOWNLOADINGDATA:
|
|
break;
|
|
|
|
case PATCH_DOWNLOAD_CONNECTING:
|
|
case PATCH_DOWNLOAD_FINDINGSITE:
|
|
case PATCH_DOWNLOAD_DOWNLOADINGDATA:
|
|
// Not interesting here...
|
|
break;
|
|
|
|
case PATCH_DOWNLOAD_PROGRESS:
|
|
{
|
|
PDOWNLOAD_INFO ProgressInfo = (PDOWNLOAD_INFO)lpvInfo;
|
|
DWORD dwBytesDownloaded = ProgressInfo->dwBytesToDownload - ProgressInfo->dwBytesRemaining;
|
|
|
|
// Convert to KB
|
|
dwBytesDownloaded = dwBytesDownloaded >> 10;
|
|
|
|
// Adjust because the progress needs to be reflected as if
|
|
// it was a full download.
|
|
dwBytesDownloaded *= pPatchInst->GetFullDownloadSize();
|
|
if (ProgressInfo->dwBytesToDownload != 0)
|
|
dwBytesDownloaded /= ProgressInfo->dwBytesToDownload >> 10;
|
|
|
|
// BUGBUG: We have to handle more than 1 patching INF.
|
|
// This hack divides up the progress across
|
|
// multiple downloads.
|
|
if (pPatchInst->GetDownloadCount() > 0)
|
|
dwBytesDownloaded /= pPatchInst->GetDownloadCount();
|
|
|
|
pPatchInst->GetCallback()->OnProgress(dwBytesDownloaded, NULL);
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
case PATCH_DOWNLOAD_FILE_COMPLETED: // AdditionalInfo is Source file downloaded
|
|
{
|
|
TCHAR szDstFile[MAX_PATH+1];
|
|
|
|
lstrcpyn(szDstFile, pPatchInst->GetPath(), MAX_PATH);
|
|
SafeAddPath(szDstFile, ParseURLA((LPCTSTR) lpvInfo), sizeof(szDstFile));
|
|
|
|
// advpext cleans up for us when it's finished downloading all the files.
|
|
CopyFile((LPCTSTR)lpvInfo, szDstFile, FALSE);
|
|
}
|
|
|
|
break;
|
|
case PATCH_DOWNLOAD_FILE_FAILED:
|
|
// advpext automatically retries failures 3 times
|
|
return PATCH_DOWNLOAD_FLAG_RETRY;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|