昨天第一天开始编写使用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循环,获取信号量后创建下载线程
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);
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); }
如果改成GlobalAlloc就没问题了,可是我的程序并没有使用DLL啊,应该还是一个堆呀?
难道是MFC和这段代码用的是不同的堆吗?
注:我使用的是静态链接MFC库的方式。
刚才调试了一下,发现在httpcore.cpp,构造函数里面new的地址和在DownloaderDlg.cpp中new地址相差很大。。