C# 实现URLDownloadToFile回调
2015-12-01 11:44
489 查看
为了更快的切入主题,这里就不在回答
1.为什么要使用URLDownloadToFile?
2.使用URLDownloadToFile有多少好处,多少坏处?
3.C#有封装好的什么什么
.....................
等等之类的问题,这里只说! 使用C# 如何实现URLDownloadToFile的回调,能停止,能暂停,能看到进度的问题
说到低,实现URLDownloadToFile的回调,就是对接口IBindStatusCallback的实现,
微软MSN有代码:
还有另外个网站有完整系统的说明(需要翻墙才能看)
http://www.pinvoke.net/default.aspx/Interfaces/IBindStatusCallback.html
但很遗憾,我用纯C#无法很好实现这个接口,总是报内存无权访问...
无奈之下,不得另劈途径,是否可借助非托管DLL去实现接口IBindStatusCallback呢?
于是初步确定算法步骤如下
1.使用C++写好一个DLL,该DLL有如下功能
①.定义用于与C#交互的函数定义:
②.定义一个继承接口IBindStatusCallback的类MyCallback,除了实现相关函数为,多了一个属性和方法,分别是
属性
Progress_Ptr m_fun_ptr;//该函数指针指向C# 提供的委托
方法 SetPtr(Progress_Ptr);//该方法用于对属性的实现,就是将属性指向用户体提供的参数,一个C#的委托
③.MyCallback类除了OnProgress函数,其他都直接返回 S_OK;//0,而OnProgress函数则通过执行用户接口m_fun_ptr来返回,从而把
下载控制权留给了调用者
④.对外导出二个函数
MyCallback*
NewIBSClass(Progress_Ptr uproc);//该函数用于使用用户提供的函数,实例一个MyCallback类,并返回其实例指针
void DisIBSClass(MyCallback*
mcall);//销毁一个实例类
3.C#部分,
①.根据CDownIBind.dll的回调约定,定义一个委托,
②.将URLDownloadToFile声明为
其他就不多说了!,,上代码:
C++编写DLL部分,怎么写DLL..我就懒得说了,自己找资料,,
DLL 名称:CDownIBind.dll
C# 部分
关于如何实现,超时,,,就简单了,无需再说罢! 通过Stopwatch给每个下载线程一个活动戳,,一点超过多长时间 不活动,就意味着超时了,直接终止,下载线程既可
所有源代码,
下载地址,2点积分哦,最近积分吃紧,
http://download.csdn.net/detail/yangshengchuan/9316173
1.为什么要使用URLDownloadToFile?
2.使用URLDownloadToFile有多少好处,多少坏处?
3.C#有封装好的什么什么
.....................
等等之类的问题,这里只说! 使用C# 如何实现URLDownloadToFile的回调,能停止,能暂停,能看到进度的问题
说到低,实现URLDownloadToFile的回调,就是对接口IBindStatusCallback的实现,
微软MSN有代码:
[ComImport,Guid("79EAC9C1-BAF9-11CE-8C82-00AA004BA90B"),InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] public interface IBindStatusCallback{....}
还有另外个网站有完整系统的说明(需要翻墙才能看)
http://www.pinvoke.net/default.aspx/Interfaces/IBindStatusCallback.html
但很遗憾,我用纯C#无法很好实现这个接口,总是报内存无权访问...
无奈之下,不得另劈途径,是否可借助非托管DLL去实现接口IBindStatusCallback呢?
于是初步确定算法步骤如下
1.使用C++写好一个DLL,该DLL有如下功能
①.定义用于与C#交互的函数定义:
typedef LRESULT (CALLBACK* Progress_Ptr)(LONG lMsgType,//消息类型,0:接口函数调用; 1:下载进度消息 ULONG ulProgress,//进度消息时,表示下载完成数;调用消息时,永远描述被调用函数的序号 ULONG ulProgressMax,//进度消息时,表示总文件大小;调用消息时,保持为0 ULONG ulStatusCode//进度消息时,表示相关状态;调用消息时,保持为0 );//用于用户接口定义
②.定义一个继承接口IBindStatusCallback的类MyCallback,除了实现相关函数为,多了一个属性和方法,分别是
属性
Progress_Ptr m_fun_ptr;//该函数指针指向C# 提供的委托
方法 SetPtr(Progress_Ptr);//该方法用于对属性的实现,就是将属性指向用户体提供的参数,一个C#的委托
③.MyCallback类除了OnProgress函数,其他都直接返回 S_OK;//0,而OnProgress函数则通过执行用户接口m_fun_ptr来返回,从而把
下载控制权留给了调用者
④.对外导出二个函数
MyCallback*
NewIBSClass(Progress_Ptr uproc);//该函数用于使用用户提供的函数,实例一个MyCallback类,并返回其实例指针
void DisIBSClass(MyCallback*
mcall);//销毁一个实例类
3.C#部分,
①.根据CDownIBind.dll的回调约定,定义一个委托,
/// <summary> /// 一个用于接收来自回调的委托 /// </summary> /// <param name="msgType">消息类型 0:接口函数调用通知,1:下载进度通知</param> /// <param name="ulProgress"></param> /// <param name="ulProgressMax"></param> /// <param name="ulStatusCode"></param> /// <returns>继续下载返回S_OK=0,终止下载返回E_ABORT = 0x80004004</returns> private delegate uint OnProcDel(int msgtype,uint ulProgress, uint ulProgressMax, uint ulStatusCode);
②.将URLDownloadToFile声明为
/// <summary> /// 下载一个网络文件 /// </summary> /// <param name="pAxCaller">如果是一个Actix时使用</param> /// <param name="szURL">网络地址</param> /// <param name="szFileName">本地文件地址</param> /// <param name="dwReserved">保留:设为0</param> /// <param name="lpfnCB">一个执行回调的指针,是一个继承IBindStatusCallback的类示例</param> /// <returns>0表示成功,其他表示失败</returns> [DllImport("urlmon.dll")] public static extern uint URLDownloadToFile(IntPtr pAxCaller, string szURL, string szFileName, uint dwReserved, IntPtr lpfnCB);
其他就不多说了!,,上代码:
C++编写DLL部分,怎么写DLL..我就懒得说了,自己找资料,,
DLL 名称:CDownIBind.dll
#include "windows.h" #include <Urlmon.h> #pragma comment(lib, "Urlmon.lib") typedef LRESULT (CALLBACK* Progress_Ptr)(LONG lMsgType,//消息类型,下载进度消息1,接口函数调用通知0 ULONG ulProgress,//进度消息时,表示下载完成数;调用消息时,永远描述被调用函数的序号 ULONG ulProgressMax,//进度消息时,表示总文件大小;调用消息时,保持为0 ULONG ulStatusCode//进度消息时,表示相关状态;调用消息时,保持为0 );//用于用户接口定义 class MyCallback:public IBindStatusCallback { //一个用户接口 Progress_Ptr m_fun_ptr; public: MyCallback():m_fun_ptr(NULL){} virtual HRESULT STDMETHODCALLTYPE QueryInterface( /* [in] */ REFIID riid, /* [iid_is][out] */ v c9db oid __RPC_FAR *__RPC_FAR *ppvObject) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数1被调用 m_fun_ptr(0,1,0,0); return S_OK; } virtual ULONG STDMETHODCALLTYPE AddRef(void) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数2被调用 m_fun_ptr(0,2,0,0); return S_OK;//返回 S_OK=0 表示继续下载 //return 1; } virtual ULONG STDMETHODCALLTYPE Release( void) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数3被调用 m_fun_ptr(0,3,0,0); return 0; } virtual HRESULT STDMETHODCALLTYPE OnStartBinding( /* [in] */ DWORD dwReserved, /* [in] */ IBinding __RPC_FAR *pib) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数4被调用 m_fun_ptr(0,4,0,0); return S_OK; } virtual HRESULT STDMETHODCALLTYPE GetPriority( /* [out] */ LONG __RPC_FAR *pnPriority) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数5被调用 m_fun_ptr(0,5,0,0); return S_OK; } virtual HRESULT STDMETHODCALLTYPE OnLowResource( /* [in] */ DWORD reserved) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数5被调用 m_fun_ptr(0,6,0,0); return S_OK; } virtual HRESULT STDMETHODCALLTYPE OnStopBinding( /* [in] */ HRESULT hresult, /* [unique][in] */ LPCWSTR szError) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数7被调用 m_fun_ptr(0,7,0,0); return S_OK; } virtual /* [local] */ HRESULT STDMETHODCALLTYPE GetBindInfo( /* [out] */ DWORD __RPC_FAR *grfBINDF, /* [unique][out][in] */ BINDINFO __RPC_FAR *pbindinfo) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数8被调用 m_fun_ptr(0,8,0,0); return S_OK; } virtual /* [local] */ HRESULT STDMETHODCALLTYPE OnDataAvailable( /* [in] */ DWORD grfBSCF, /* [in] */ DWORD dwSize, /* [in] */ FORMATETC __RPC_FAR *pformatetc, /* [in] */ STGMEDIUM __RPC_FAR *pstgmed) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数9被调用 m_fun_ptr(0,9,0,0); return S_OK; } virtual HRESULT STDMETHODCALLTYPE OnObjectAvailable( /* [in] */ REFIID riid, /* [iid_is][in] */ IUnknown __RPC_FAR *punk) { if(NULL!=m_fun_ptr)//调用用户接口,表示函数10被调用 m_fun_ptr(0,10,0,0); return S_OK; } virtual HRESULT STDMETHODCALLTYPE OnProgress( /* [in] */ ULONG ulProgress, /* [in] */ ULONG ulProgressMax, /* [in] */ ULONG ulStatusCode, /* [in] */ LPCWSTR szStatusText)//这个函数是最重要的了,下载过程会一直调用该函数 { /* 如果用户接口存在,则把控制权转移到用户层, 这里不传递szStatusText参数, 是因为本人不想招惹麻烦,这个参数有时候会是 NULL 可能带来风险,但没试验 */ if(NULL!=m_fun_ptr) return m_fun_ptr(1,ulProgress,ulProgressMax,ulStatusCode); return S_OK; } //此方法将,类属性指向用户接口函数, //传入用户提供的接口 void SetPtr(Progress_Ptr fun_ptr) { m_fun_ptr = fun_ptr; } }; //一个导出函数,用户提供一个Progress_Ptr 函数来实现一个接口类,并返回类指针 extern "C" MyCallback* CALLBACK NewIBSClass(Progress_Ptr onprocc)//得到一个构造类 { MyCallback* temCall=new MyCallback; temCall->SetPtr(onprocc); return temCall; } //销毁实例类指针 extern "C" VOID CALLBACK DisIBSClass(MyCallback* temCall)//删除一个类指针 { if(NULL==temCall) return; delete temCall; } extern "C" VOID CALLBACK LoadFun()//首次查找这个函数,保证DLL 能正确使用 { } BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; }
C# 部分
public partial class Form1 : Form { /// <summary> /// 下载一个网络文件 /// </summary> /// <param name="pAxCaller">如果是一个Actix时使用</param> /// <param name="szURL">网络地址</param> /// <param name="szFileName">本地文件地址</param> /// <param name="dwReserved">保留:设为0</param> /// <param name="lpfnCB">一个执行回调的指针,是一个继承IBindStatusCallback的类示例</param> /// <returns>0表示成功,其他表示失败</returns> [DllImport("urlmon.dll")] private static extern uint URLDownloadToFile(IntPtr pAxCaller, string szURL, string szFileName, uint dwReserved, IntPtr lpfnCB); /// <summary> /// 实现一个IBindStatusCallback接口,并返回指针 /// </summary> /// <param name="delfun">一个OnProcDel委托的实例</param> /// <returns></returns> [DllImport("CDownIBind.dll")] private static extern IntPtr NewIBSClass(OnProcDel delfun); /// <summary> /// 销毁一个实现的IBindStatusCallback接口 /// </summary> /// <param name="ibis"></param> /// <returns></returns> [DllImport("CDownIBind.dll")] private static extern int DisIBSClass(IntPtr ibis); /// <summary> /// 一个用于接收来自回调的委托 /// </summary> /// <param name="msgType">消息类型 0:接口函数调用通知,1:下载进度通知</param> /// <param name="ulProgress"></param> /// <param name="ulProgressMax"></param> /// <param name="ulStatusCode"></param> /// <returns>继续下载返回S_OK=0,终止下载返回E_ABORT = 0x80004004</returns> private delegate uint OnProcDel(int msgtype, uint ulProgress, uint ulProgressMax, uint ulStatusCode); /// <summary> /// IBindStatusCallback类示例的指针 /// </summary> IntPtr ibClas = IntPtr.Zero; OnProcDel thisProc, //表示指向本地的委托指针 apiProc;//用于接收来自DLL的通知 System.Threading.Thread DownThrObj; /// <summary> /// 下载状态 0:没有动作,1:下载中,2:暂停中,3:等待终止 /// </summary> byte bDownStats = 0; string sUrl = "", sLocfile = "", sDownCap = ""; public Form1() { InitializeComponent(); apiProc += OnProecc;//挂载函数 thisProc += OnProecc;//挂载函数 ibClas = NewIBSClass(apiProc);//实现本得到指针 label1.Text = label2.Text = ""; CMD_控制.Enabled = false; //注册时间 CMD_控制.Click += CMDClick; CMD_下载.Click += CMDClick; } protected override void OnClosed(EventArgs e) { if (bDownStats != 0) { bDownStats = 3; DownThrObj.Join(); } if (ibClas != IntPtr.Zero) DisIBSClass(ibClas); base.OnClosed(e); } private void CMDClick(object send, EventArgs e) { if (CMD_下载 == send) { #region switch (bDownStats) { case 0://没有动作 label1.Text = ""; PB_进度条.Value = 0; if (textBox1.Text.Length == 0) { label2.Text = "提供的网络地址无效"; return; } if (ibClas == IntPtr.Zero) { try { ibClas = NewIBSClass(apiProc); } catch (Exception ex) { Win32Exception we = new Win32Exception(Marshal.GetLastWin32Error(), ex.Message); label2.Text = "实现接口失败:\r" + we.Message; return; } } if (ibClas == IntPtr.Zero) { label2.Text = "接口未实现"; return; } listBox1.Items.Clear(); textBox1.Enabled = false; CMD_控制.Enabled = true; CMD_控制.Text = "暂停"; CMD_下载.Text = "终止"; bDownStats = 1; sUrl = textBox1.Text + "?ux=" + new Random().NextDouble().ToString("0.0000000000000000");//为了防止从缓存下载,使用一个随机参数 sLocfile = "d:\\15.exe";//本地文件可以自行修改 DownThrObj = new System.Threading.Thread(DownThrFun); DownThrObj.IsBackground = true; label2.Text = "下载:" + sLocfile; DownThrObj.Start();//开始下载 break; case 1://下载中 case 2://暂停中 bDownStats = 3; break; case 3://等待终止 break; default: label2.Text = "状态未知"; break; } #endregion return; } if (CMD_控制 == send) { #region switch (bDownStats) { case 1://下载中 bDownStats = 2; break; case 2://暂停中 bDownStats = 1; break; } #endregion return; } } /// <summary> /// 用于下载线程工作的函数 /// </summary> private void DownThrFun() { int iva1 = 0; try { iva1 = (int)URLDownloadToFile(IntPtr.Zero, sUrl, sLocfile, 0, ibClas); } catch (Exception ex) { if (bDownStats == 1) { Win32Exception we = new Win32Exception(Marshal.GetLastWin32Error(), ex.Message); sDownCap = "下载失败:" + we.Message; bDownStats = 0; OnProecc(3, 0, 0, 0); return; } } if (iva1 != 0) { Win32Exception we = new Win32Exception(Marshal.GetLastWin32Error(), "失败"); sDownCap = "下载失败:" + iva1.ToString(); bDownStats = 0; OnProecc(3, 0, 0, 0); return; } if (bDownStats == 3) { bDownStats = 0; sDownCap = "用户终止"; OnProecc(3, 0, 0, 0); return; } bDownStats = 0; OnProecc(3, 1, 0, 0); } /// <summary> /// 接口函数,本窗口也是 /// </summary> /// <param name="msgtype"></param> /// <param name="ulProgress"></param> /// <param name="ulProgressMax"></param> /// <param name="ulStatusCode"></param> /// <returns></returns> private uint OnProecc(int msgtype, uint ulProgress, uint ulProgressMax, uint ulStatusCode) { if (bDownStats != 1 && bDownStats != 2 && msgtype != 3 && msgtype != 13) return 0x80004004;// S_OK=0,继续,E_ABORT = 0x80004004 终止 if (msgtype < 10)//小于10,有可能来自其他线程,所以需要进行投递成本地消息 { ZtKey: if (bDownStats == 2) { System.Threading.Thread.Sleep(10); goto ZtKey; } msgtype = msgtype + 10; this.BeginInvoke(thisProc, msgtype, ulProgress, ulProgressMax, ulStatusCode);//投递到目标消息队列 return 0; } if (ulProgress > 0) { if (ulProgressMax > 0) { float fva1 = ulProgress * 1.0f / ulProgressMax * 100.0f; label1.Text = fva1.ToString("0.0"); PB_进度条.Value = (int)fva1; } } else listBox1.Items.Add(msgtype.ToString() + ":" + ulProgress.ToString() + "|" + ulProgressMax.ToString()); if (msgtype == 13) { label1.Text = ""; textBox1.Enabled = true; CMD_下载.Text = "开始"; CMD_控制.Text = "暂停"; CMD_控制.Enabled = false; PB_进度条.Value = 0; if (ulProgress == 0) label2.Text = "失败:" + sDownCap; else label2.Text = sLocfile; } return 0; } }
关于如何实现,超时,,,就简单了,无需再说罢! 通过Stopwatch给每个下载线程一个活动戳,,一点超过多长时间 不活动,就意味着超时了,直接终止,下载线程既可
所有源代码,
下载地址,2点积分哦,最近积分吃紧,
http://download.csdn.net/detail/yangshengchuan/9316173
相关文章推荐
- Scrapy的架构介绍
- 发布一个自己做的jsp博客系统
- c#调用COM组件
- 提供个 全免杀海洋2006asp木马 下载
- 高手写的Tracer-Flash代码调试类代码下载
- Vista 防火墙 Vista Firewall Control v1.0.11 下载
- C#实现把指定数据写入串口
- C#动态创建button的方法
- 国外Lightbox v2.03.3 最新版 下载
- C#中抽象方法与虚拟方法的区别
- c#中虚函数的相关使用方法
- C#使用加边法计算行列式的值
- C#实现多线程的同步方法实例分析
- C#中尾递归的使用、优化及编译器优化
- 火影漫画下载助手 下载
- USBkill U盘防火墙 v3.0 下载
- 腾讯 Tencent Traveler v3.4 下载
- 查杀软件 360安全卫士 v3.2.1.1001 下载
- Kaspersky 6.0.2.666 MP2 nct Release+汉化补丁 下载
- Symantec Norton Ghost v12.0 Retail ISO 多国语言版 下载