WinInet多线程下载器编写历程(1)
昨天第一天开始编写使用Win32 API :WinInet 的多线程下载器,作为毕业设计,我准备把开发的过程记录下来。总结一下昨天遇到的问题吧:
(1)架构
我初步架构了一下:
1.每个下载任务,即一个CDingDownload的对象,对象成员包括此下载任务的各种配置信息:
源URL,目标文件名,线程数,块大小,Owner窗口句柄(发通知消息用)
2.CDingDownload中有一个主控线程,控制多线程下载
问题出现了: 线程的过程函数必须是类中的static函数,但是static是没有this指针的,只能访问静态变量
为了解决这个问题,我将this指针放在主控线程 过程函数的参数结构体里面,这样static线程也能区分不同的对象了,可以通过指针访问对象成员
struct MainControlParam
{
CDingHttpDownload * p_this;
};
VOID CDingHttpDownload::Start()
{
m_state = running;
m_mainctlparam.p_this = this;
m_thread_maincontrol = CreateThread(NULL,0,MainControlProc,&m_mainctlparam,0,NULL);
}
DWORD WINAPI CDingHttpDownload::MainControlProc(LPVOID lpParam)
{
MainControlParam *pParam = (MainControlParam *)lpParam;
CDingHttpDownload *pthis = pParam->p_this; //对象指针
HWND hwnd = pthis->m_hwnd; //主窗口句柄
PTCHAR URL = pthis->m_URL;
}
3.主控线程获取文件信息,开始分发任务下载(创建下载线程),并发送自定义消息通知窗口
关于自定义消息的使用:
首先定义消息,自定义消息都是从WM_USER往上的
#define WM_USER_THREAD_REQUEST (WM_USER + 0x101)
然后声明定义回调函数
afx_msg LRESULT OnThreadRequest(WPARAM wparam,LPARAM lparam);
LRESULT CDownloaderDlg::OnThreadRequest(WPARAM wparam,LPARAM lparam)
{
return 0;
}
最后加入消息映射
BEGIN_MESSAGE_MAP(CDownloaderDlg, CDialogEx)
ON_WM_SYSCOMMAND()
ON_WM_PAINT()
ON_WM_QUERYDRAGICON()
ON_BN_CLICKED(IDC_BUTTON1, &CDownloaderDlg::OnBnClickedButton1)
ON_MESSAGE(WM_USER_THREAD_REQUEST,OnThreadRequest)
END_MESSAGE_MAP()
4.主控线程维护一个信号量来控制线程数,开始不断创建任务
semaphore_threads = CreateSemaphore(NULL,pthis->m_threadnum,pthis->m_threadnum,NULL);
while循环,www.zzzyk.com获取信号量后创建下载线程
while (pthis->m_state == running)
{
WaitForSingleObject(semaphore_threads,INFINITE);
DownloadThreadParam *param = new DownloadThreadParam();
param->p_this = pthis;
param->mutex_progress = mutex_progress;
param->semaphore_threads = semaphore_threads;
param->range1 = 0;
param->range2 = 1024;
CreateThread(NULL,0,DownloadProc,param,0,NULL);
}
下载线程下载完成自己的任务块之后释放信号量:
DownloadProc尾部:
ReleaseSemaphore(pParm->semaphore_threads,1,NULL);
(2)遇到的问题
1.内存分配的问题:
DownloaderDlg.cpp中的按钮响应函数中构造一个下载类:
注意我注释掉的部分使用new来申请一个buffer然后传递给下载类的构造函数
//PTCHAR URL = new TCHAR(50);
PTCHAR URL = (PTCHAR)GlobalAlloc(0,50*sizeof(TCHAR));
_tcscpy(URL,_T("127.0.0.1"));
//PTCHAR filename = new TCHAR(50);
PTCHAR filename = (PTCHAR)GlobalAlloc(0,50*sizeof(TCHAR));
_tcscpy(filename,_T("ding.down.php"));
m_pdownload = new CDingHttpDownload(this->m_hWnd,URL,filename);
httpcore.cpp构造函数部分:
CDingHttpDownload::CDingHttpDownload(HWND handle,PTCHAR URL,PTCHAR filename,\
UINT threadnum,UINT blocksize,UINT cache)\
:m_hwnd(handle),m_threadnum(threadnum),m_blocksize(blocksize),m_cache(cache)
{
//m_URL = new TCHAR(50);
m_URL = (PTCHAR)GlobalAlloc(0,(_tcslen(URL)+1)*sizeof(TCHAR));
_tcscpy(m_URL,URL);
//m_filename = new TCHAR(50);
m_filename = (PTCHAR)GlobalAlloc(0,(_tcslen(filename)+1)*sizeof(TCHAR));
_tcscpy(m_filename,filename);
}
如果我使用注释掉的new方式来申请内存,会在运行过程中遇到错误,并提示"其原因可能是堆被损坏,这也说明程序中或它所加载的任何DLL 中有bug"
如果改成GlobalAlloc就没问题了,可是我的程序并没有使用DLL啊,应该还是一个堆呀?
难道是MFC和这段代码用的是不同的堆吗?
注:我使用的是静态链接MFC库的方式。
刚才调试了一下,发现在httpcore.cpp,构造函数里面new的地址和在DownloaderDlg.cpp中new地址相差很大。
摘自 New Day New Plan 。
补充:软件开发 , Vc ,