最近,做一個類似於下載器的程序:
我用了2種方法,加上迅雷提供的插件是3種:我主要寫下微軟提供的API,通過MSDN都可以查到。
第一種:CInternetSession類
第二個:URLDownloadToFile函數
先說第一個,我使用第一個直接通過URL獲取服務器的數據(.txt文件),比如獲取網絡遊戲的最新版本號,是不需要下載的。如果遊戲需要更新需要再從服務器讀取更新列表
<版本號 下載URL>我是通過第二個方法URLDownloadToFile函數,下載到本地的,然後讀取.txt文件每行數據保存的vector中,然後刪除下載的列表文件,依據vector列表更新遊
戲數據。在下載遊戲更新包的我用的是URLDownloadToFile函數,可以實時瞭解下載文件的狀況,不支持斷點續傳;不過,要向支持可以找迅雷公開的接口API,支持斷點續傳。
CInternetSession類的應用:
const int dwBufSize = 1024;
CInternetSession * Session = new CInternetSession;
CHttpFile* pHttpFile = NULL;
CStdioFile pLocalFile;
DWORD dwlen;
try
{
LPBYTE lpBuf = new byte[dwBufSize];
pLocalFile.Open( "XXX.txt",
CFile::modeCreate |
CFile::modeWrite |
CFile::typeBinary );
pHttpFile =(CHttpFile*)Session->OpenURL(g_NewVersionURL,
1,
INTERNET_FLAG_TRANSFER_BINARY |
INTERNET_FLAG_RELOAD |
INTERNET_FLAG_DONT_CACHE,
NULL,
0);
//可以調用BOOL QueryInfo( DWORD dwInfoLevel, LPVOID lpvBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex = NULL ) const;
//查詢網絡狀態網址:http://baike.baidu.com/view/2911740.htm
while(dwlen = pHttpFile-> Read(lpBuf, dwBufSize-1 ))
{
m_lsState = LS_LOADING;
pLocalFile.Write(lpBuf,dwlen);
}
pLocalFile.Close();
pHttpFile->Close();
delete pHttpFile; //一定要刪除,不然內存泄露
pHttpFile=NULL;
delete[] lpBuf;
lpBuf = NULL;
}
catch(CInternetException eInt)
{
eInt.Delete();
}
catch(CMemoryException eMem)
{
eMem.Delete();
}
catch(CFileException eFile)
{
eFile.Delete();
}
m_lsState = LS_FINISHED;
return 0;
}
下面說一下URLDownloadToFile函數:
我在一個網站上找的:http://www.codeproject.com/KB/IP/urldownload.aspx
===================callback.h
#pragma once
#endif // _MSC_VER > 1000
#include "URLDownloadDlg.h"
#pragma warning(disable:4100) // disable warnings about unreferenced params
class CCallback : public IBindStatusCallback
{
public:
CCallback();
~CCallback();
// Pointer to the download progress dialog.
CURLDownloadDlg* m_pDlg;
// The time when the download should timeout.
BOOL m_bUseTimeout;
CTime m_timeToStop;
// IBindStatusCallback methods. Note that the only method called by IE
// is OnProgress(), so the others just return E_NOTIMPL.
STDMETHOD(OnStartBinding)(
/* [in] */ DWORD dwReserved,
/* [in] */ IBinding __RPC_FAR *pib)
{ return E_NOTIMPL; }
STDMETHOD(GetPriority)(
/* [out] */ LONG __RPC_FAR *pnPriority)
{ return E_NOTIMPL; }
STDMETHOD(OnLowResource)(
/* [in] */ DWORD reserved)
{ return E_NOTIMPL; }
STDMETHOD(OnProgress)(
/* [in] */ ULONG ulProgress,
/* [in] */ ULONG ulProgressMax,
/* [in] */ ULONG ulStatusCode,
/* [in] */ LPCWSTR wszStatusText);
STDMETHOD(OnStopBinding)(
/* [in] */ HRESULT hresult,
/* [unique][in] */ LPCWSTR szError)
{ return E_NOTIMPL; }
STDMETHOD(GetBindInfo)(
/* [out] */ DWORD __RPC_FAR *grfBINDF,
/* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo)
{ return E_NOTIMPL; }
STDMETHOD(OnDataAvailable)(
/* [in] */ DWORD grfBSCF,
/* [in] */ DWORD dwSize,
/* [in] */ FORMATETC __RPC_FAR *pformatetc,
/* [in] */ STGMEDIUM __RPC_FAR *pstgmed)
{ return E_NOTIMPL; }
STDMETHOD(OnObjectAvailable)(
/* [in] */ REFIID riid,
/* [iid_is][in] */ IUnknown __RPC_FAR *punk)
{ return E_NOTIMPL; }
// IUnknown methods. Note that IE never calls any of these methods, since
// the caller owns the IBindStatusCallback interface, so the methods all
// return zero/E_NOTIMPL.
STDMETHOD_(ULONG,AddRef)()
{ return 0; }
STDMETHOD_(ULONG,Release)()
{ return 0; }
STDMETHOD(QueryInterface)(
/* [in] */ REFIID riid,
/* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{ return E_NOTIMPL; }
};
#pragma warning(default:4100)
#endif
===================callback.cpp
#include "stdafx.h"
#include "URLDownload.h"
#include <shlwapi.h> // for StrFormatByteSize()
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CCallback::CCallback() : m_bUseTimeout(FALSE), m_pDlg(NULL)
{
}
CCallback::~CCallback()
{
}
HRESULT CCallback::OnProgress ( ULONG ulProgress, ULONG ulProgressMax,
ULONG ulStatusCode, LPCWSTR wszStatusText )
{
// Local variables are declared static so they don't have to be reallocated on
// the stack every time. This is safe in this app since I know I'll only have
// one thread downloading.
static CString sIEStatusMsg;
static TCHAR szCustomStatusMsg [256];
static TCHAR szAmtDownloaded [256], szTotalSize [256];
UNREFERENCED_PARAMETER(ulStatusCode);
// Did the user hit the Stop button?
if ( 0 != g_fAbortDownload )
return E_ABORT;
// Has the timeout period elapsed?
if ( m_bUseTimeout && CTime::GetCurrentTime() > m_timeToStop )
return E_ABORT;
// Use CString to convert IE's status message to a TCHAR string.
if ( NULL != wszStatusText )
{
sIEStatusMsg = wszStatusText;
}
else
{
sIEStatusMsg.Empty();
}
// Make our own progress message - we'll show the amount downloaded and
// the total file size (if known).
StrFormatByteSize ( ulProgress, szAmtDownloaded, 256 );
StrFormatByteSize ( ulProgressMax, szTotalSize, 256 );
if ( 0 != ulProgressMax )
{
wsprintf ( szCustomStatusMsg, _T("Downloaded %s of %s"), szAmtDownloaded, szTotalSize );
}
else
{
wsprintf ( szCustomStatusMsg, _T("Downloaded %s (total size unknown)"), szAmtDownloaded );
}
// Report the progress back to the main window.
if ( 0 != ulProgressMax )
{
m_pDlg->ProgressUpdate ( sIEStatusMsg, szCustomStatusMsg, int( 100.0 * ulProgress / ulProgressMax) );
}
else
{
m_pDlg->ProgressUpdate ( sIEStatusMsg, szCustomStatusMsg, 0 );
}
return S_OK;
}
======================函數的使用
#include "stdafx.h"
#include "URLDownload.h"
#include "URLDownloadDlg.h"
#include "BindStatusCallback.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
static UINT gThreadProc ( void* pv );
/////////////////////////////////////////////////////////////////////////////
// CAboutDlg dialog used for App About
class CAboutDlg : public CDialog
{
public:
CAboutDlg();
// Dialog Data
//{{AFX_DATA(CAboutDlg)
enum { IDD = IDD_ABOUTBOX };
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CAboutDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX);
// DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
//{{AFX_MSG(CAboutDlg)
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
//{{AFX_DATA_INIT(CAboutDlg)
//}}AFX_DATA_INIT
}
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CAboutDlg)
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
//{{AFX_MSG_MAP(CAboutDlg)
// No message handlers
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
// CURLDownloadDlg dialog
CURLDownloadDlg::CURLDownloadDlg(CWnd* pParent /*=NULL*/)
: CDialog(CURLDownloadDlg::IDD, pParent)
{
//{{AFX_DATA_INIT(CURLDownloadDlg)
m_uTimeout = 0;
//}}AFX_DATA_INIT
// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}
void CURLDownloadDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CURLDownloadDlg)
DDX_Control(pDX, IDC_TIMEOUT_SECS, m_editTimeout);
DDX_Control(pDX, IDC_TIMEOUT, m_btnTimeout);
DDX_Control(pDX, IDC_SPIN, m_spinner);
DDX_Control(pDX, IDC_FILE, m_editFile);
DDX_Control(pDX, IDC_ABOUT, m_btnAbout);
DDX_Control(pDX, IDC_URL, m_editURL);
DDX_Control(pDX, IDCANCEL, m_btnExit);
DDX_Control(pDX, IDC_STOP, m_btnStop);
DDX_Control(pDX, IDC_START, m_btnStart);
DDX_Control(pDX, IDC_PROGRESS, m_progress);
DDX_Control(pDX, IDC_IE_STATUS, m_stIEMsg);
DDX_Control(pDX, IDC_CUSTOM_STATUS, m_stCustomMsg);
DDX_Text(pDX, IDC_TIMEOUT_SECS, m_uTimeout);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CURLDownloadDlg, CDialog)
//{{AFX_MSG_MAP(CURLDownloadDlg
ON_WM_SYSCOMMAND()
ON_BN_CLICKED(IDC_START, OnStart)
ON_BN_CLICKED(IDC_STOP, OnStop)
ON_BN_CLICKED(IDC_ABOUT, OnAbout)
ON_BN_CLICKED(IDC_TIMEOUT, OnClickedTimeout)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CURLDownloadDlg message handlers
BOOL CURLDownloadDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
ASSERT(IDM_ABOUTBOX < 0xF000);
CMenu* pSysMenu = GetSystemMenu(FALSE);
if (pSysMenu != NULL)
{
CString strAboutMenu;
strAboutMenu.LoadString(IDS_ABOUTBOX);
if (!strAboutMenu.IsEmpty())
{
pSysMenu->AppendMenu(MF_SEPARATOR);
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
}
}
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// Init & pre-fill some of the dlg controls.
m_spinner.SetRange ( 1, 120 );
m_progress.SetRange ( 0, 100 );
// Fill the URL editbox with "http://"
m_editURL.SetWindowText ( _T("http://") );
// Fill the file editbox with the user's MyDocs directory.
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
TCHAR szDocsDir [MAX_PATH];
if ( SUCCEEDED( SHGetSpecialFolderLocation ( NULL, CSIDL_PERSONAL, &pidl )))
{
if ( SHGetPathFromIDList ( pidl, szDocsDir ))
{
lstrcat ( szDocsDir, _T("\\") );
m_editFile.SetWindowText ( szDocsDir );
}
if ( SUCCEEDED( SHGetMalloc ( &pMalloc )))
{
pMalloc->Free ( pidl );
pMalloc->Release();
}
}
GotoDlgCtrl ( &m_editURL );
m_editURL.SetSel ( -1, -1 );
return FALSE; // return TRUE unless you set the focus to a control
}
void CURLDownloadDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
else
{
CDialog::OnSysCommand(nID, lParam);
}
}
void CURLDownloadDlg::OnOK()
{
}
void CURLDownloadDlg::OnStart()
{
CWinThread* pWorkerThread;
UpdateData();
if ( m_editURL.GetWindowTextLength() == 0 )
{
AfxMessageBox ( _T("Please enter a URL.") );
return;
}
if ( m_editURL.GetWindowTextLength() == 0 )
{
AfxMessageBox ( _T("Please enter a filename.") );
return;
}
pWorkerThread = AfxBeginThread ( gThreadProc, this,
THREAD_PRIORITY_NORMAL, 0,
CREATE_SUSPENDED );
// Note: Yeah I know that it's BAD to pass a CWnd object between threads.
// I forgot about that rule when I wrote the above call, and it turns out
// that I got lucky and it works. All the stuff the worker thread does
// involves just sending messages to window handles. Since accessing the
// data member CWnd::m_hWnd is safe to do across threads, nothing ever
// asserted to alert me of this mistake.
// Since I have finished this sample app and just now noticed this mistake,
// I;m gonna be lazy and not fix it. :D
if ( NULL != pWorkerThread )
{
g_fAbortDownload = 0;
m_editURL. EnableWindow ( FALSE );
m_editFile. EnableWindow ( FALSE );
m_editTimeout.EnableWindow ( FALSE );
m_btnStart. EnableWindow ( FALSE );
m_btnStop. EnableWindow ();
m_btnExit. EnableWindow ( FALSE );
m_btnAbout. EnableWindow ( FALSE );
m_btnTimeout. EnableWindow ( FALSE );
m_spinner. EnableWindow ( FALSE );
GotoDlgCtrl ( &m_btnStop );
// Kick off the download!
pWorkerThread->ResumeThread();
}
else
{
AfxMessageBox ( _T("Couldn't create worker thread!"), MB_ICONERROR );
}
}
void CURLDownloadDlg::OnStop()
{
InterlockedExchange ( &g_fAbortDownload, 1 );
}
void CURLDownloadDlg::OnAbout()
{
CAboutDlg dlgAbout;
dlgAbout.DoModal();
}
void CURLDownloadDlg::OnClickedTimeout()
{
UpdateTimeoutCtrls();
}
void CURLDownloadDlg::UpdateTimeoutCtrls()
{
BOOL bEnable = m_btnTimeout.GetCheck();
m_editTimeout.EnableWindow ( bEnable );
m_spinner.EnableWindow ( bEnable );
}
UINT gThreadProc ( void* pv )
{
CURLDownloadDlg* pDlg = (CURLDownloadDlg*) pv;
pDlg->WorkerThreadProc();
return 0;
}
void CURLDownloadDlg::WorkerThreadProc()
{
CCallback callback;
HRESULT hr;
CString sURL, sFile;
callback.m_pDlg = this;
m_editURL.GetWindowText ( sURL );
m_editFile.GetWindowText ( sFile );
if ( m_btnTimeout.GetCheck() )
{
callback.m_bUseTimeout = TRUE;
callback.m_timeToStop = CTime::GetCurrentTime() + CTimeSpan( 0, 0, 0, m_uTimeout );
}
hr = URLDownloadToFile ( NULL, // ptr to ActiveX container
sURL, // URL to get
sFile, // file to store data in
0, // reserved
&callback // ptr to IBindStatusCallback
);
if ( SUCCEEDED(hr) )
{
AfxMessageBox ( _T("Download completed successfully!"),
MB_ICONINFORMATION );
}
else
{
LPTSTR lpszErrorMessage;
CString sMsg;
if ( FormatMessage ( FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, hr,
MAKELANGID ( LANG_NEUTRAL, SUBLANG_DEFAULT ),
(LPTSTR) &lpszErrorMessage, 0, NULL ))
{
sMsg.Format ( _T("Download failed. Error = 0x%08lX\n\n%s"),
(DWORD) hr, lpszErrorMessage );
LocalFree ( lpszErrorMessage );
}
else
{
sMsg.Format ( _T("Download failed. Error = 0x%08lX\n\nNo message available."), (DWORD) hr );
}
AfxMessageBox ( sMsg );
}
m_editURL. EnableWindow();
m_editFile. EnableWindow();
m_btnStart. EnableWindow();
m_btnStop. EnableWindow ( FALSE );
m_btnExit. EnableWindow();
m_btnAbout. EnableWindow();
m_btnTimeout.EnableWindow();
UpdateTimeoutCtrls();
GotoDlgCtrl ( &m_editURL );
}
void CURLDownloadDlg::ProgressUpdate ( LPCTSTR szIEMsg,
LPCTSTR szCustomMsg,
const int nPercentDone )
{
ASSERT ( AfxIsValidString ( szIEMsg ));
ASSERT ( AfxIsValidString ( szCustomMsg ));
ASSERT ( nPercentDone >= 0 && nPercentDone <= 100 );
m_stIEMsg.SetWindowText ( szIEMsg );
m_stCustomMsg.SetWindowText ( szCustomMsg );
m_progress.SetPos ( nPercentDone );
}
///////////////////////////////////////////////////////////////////////////////////////////
#pragma once
#endif // _MSC_VER > 1000
/////////////////////////////////////////////////////////////////////////////
// CURLDownloadDlg dialog
class CURLDownloadDlg : public CDialog
{
// Construction
public:
CURLDownloadDlg(CWnd* pParent = NULL);
// standard constructor
// Dialog Data
//{{AFX_DATA(CURLDownloadDlg)
enum { IDD = IDD_URLDOWNLOAD_DIALOG };
CEdit m_editTimeout;
CButton m_btnTimeout;
CSpinButtonCtrl m_spinner;
CEdit m_editFile;
CButton m_btnAbout;
CEdit m_editURL;
CButton m_btnExit;
CButton m_btnStop;
CButton m_btnStart;
CProgressCtrl m_progress;
CStatic m_stIEMsg;
CStatic m_stCustomMsg;
UINT m_uTimeout;
//}}AFX_DATA
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CURLDownloadDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
// Implementation
protected:
HICON m_hIcon;
void UpdateTimeoutCtrls();
public:
void WorkerThreadProc();
void ProgressUpdate ( LPCTSTR szIEMsg, LPCTSTR szCustomMsg, const int nPercentDone );
// Generated message map functions
//{{AFX_MSG(CURLDownloadDlg)
virtual BOOL OnInitDialog();
afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
virtual void OnOK();
afx_msg void OnStart();
afx_msg void OnStop();
afx_msg void OnAbout();
afx_msg void OnClickedTimeout();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
#endif