勇芳VFB的Miniblink组件实现文件下载

现在开发客户端程序,主流的开发语言中Java、.Net很容易破解,MFC开发效率低,Delphi7是巅峰,后面走了弯路,现在XE系列一言难尽。至于QT,哎。

VB6现在已经基本退出历史舞台,惋惜的话就不多说了。。

最近楼主看上了FreeBasic,其国内的版本叫VisualFreeBasic(下面简称vfb),开发者勇芳是一位技术大牛。

vfb自带一个浏览器组件,封装的是国内一个浏览器大咖龙泉扫地僧的Miniblink。作为一个浏览器组件,竟然没有提供文件下载的案例,为此我跟VFB的一位技术大牛驰骋乾坤准备为此做一个例子。

Miniblink的免费版本node.dll导出的是纯C接口, 也就是__cdecl,而不是常见的__stdcall,这个也给增加了一些麻烦。收费版本mb.dll则采用stdcall接口,但是我们秉着不改vfb的浏览器组件的原则写例子,等下面有时间,我们再写个vfb的控件。

Miniblink下载有两个事件,分别是wkeDownload和wkeDownload2

wkeDownload的声明区别如下:

void wkeOnDownload(wkeWebView webView, wkeDownloadCallback callback, void* param)

typedef bool(WKE_CALL_TYPE*wkeDownloadCallback)(wkeWebView webView, void* param, const char* url);

从上面的回调可以看出,wkeDownload只返回了一个url,对于没有身份认证、GET的请求才能下载,很鸡肋。

wkeDownload2的声明就复杂多了:

typedef void* wkeNetJob;

typedef enum _wkeLoadingResult {
    WKE_LOADING_SUCCEEDED,
    WKE_LOADING_FAILED,
    WKE_LOADING_CANCELED
} wkeLoadingResult;

typedef enum _wkeDownloadOpt {
    kWkeDownloadOptCancel,
    kWkeDownloadOptCacheData,
} wkeDownloadOpt;

typedef void(WKE_CALL_TYPE*wkeNetJobDataRecvCallback)(void* ptr, wkeNetJob job, const char* data, int length);
typedef void(WKE_CALL_TYPE*wkeNetJobDataFinishCallback)(void* ptr, wkeNetJob job, wkeLoadingResult result);

typedef struct _wkeNetJobDataBind {
    void* param;
    wkeNetJobDataRecvCallback recvCallback;
    wkeNetJobDataFinishCallback finishCallback;
}wkeNetJobDataBind;

typedef wkeDownloadOpt(WKE_CALL_TYPE*wkeDownload2Callback)(
   wkeWebView webView, 
   void* param, 
   size_t expectedContentLength, 
   const char* url, 
   const char* mime, 
   const char* disposition, 
   wkeNetJob job, 
   wkeNetJobDataBind* dataBind);

Type DownInfo 
   FileName As String
   Length As Integer
   Down As Integer  
End Type

这个确实完善多了,但是对接的难度也大,估计也是因为这个市面上没见过如何使用这个接口的例子。vfb封装了wkeDownload2的事件,但是具体怎么写没有例子。

首先,我们根据C++的文档,声明两个枚举和一个结构体wkeNetJobDataBind。要特别注意的是,wkeNetJobDataBind的两个回调,得声明是cdecl导出的,不然这个接口wkeNetJobDataRecvCallback只会被调用一次,然后程序就会平栈错误直接崩溃(这里特别感谢驰骋乾坤的指点,不然我找不出这个BUG)。

enum wkeLoadingResult
   WKE_LOADING_SUCCEEDED
   WKE_LOADING_FAILED
   WKE_LOADING_CANCELED
End enum

enum wkeDownloadOpt
   kWkeDownloadOptCancel
   kWkeDownloadOptCacheData
End enum

Type DownInfo 
   FileName As String
   Length As Integer
   Down As Integer  
End Type

Type wkeNetJobDataBind
   param          As Any Ptr
   recvCallback   As Sub cdecl(param As Any Ptr ,job As wkeNetJob ,dataIn As Any Ptr ,length As Integer)
   finishCallback As Sub cdecl(param As Any Ptr ,job As wkeNetJob ,result As wkeLoadingResult)
End Type

下面是两个回调的实现,当然也必须要说cdecl导出类型的:

Sub wkeNetJobDataRecvCallback cdecl(ByVal param As Any Ptr ,ByVal job As wkeNetJob ,ByVal dataIn As Any Ptr ,ByVal length As Integer)
   Dim d As DownInfo Ptr = param
   Dim dataOut(length) As Byte
   CopyMemory(Varptr(dataOut(0)) ,dataIn ,length)
   Dim f As Integer = FreeFile()
   Open d->FileName For Binary As #f
      Put #f, LOF(f),dataOut()
   Close #f
   
End Sub

Sub wkeNetJobDataFinishCallback cdecl(ByVal param As Any Ptr ,ByVal job As wkeNetJob ,ByVal result As wkeLoadingResult)
   Dim d As DownInfo Ptr = param
   
   If MsgBox("是否需要打开文件?" ,"下载完成" ,MB_YESNO) = 6 Then
      ShellExecute Me.hWnd, "Open", d->FileName, "", "", 0
   End If
   
   Delete d
End Sub

wkeNetJobDataRecvCallback负责文件字节流的接收;wkeNetJobDataFinishCallback负责下载完成的处理,例如打开。

wekDownload2事件的实现:

1、从disposition想办法获取下载的文件名字(不一定有)

2、如果没有,则从url的路径获取名字

3、引导用户选择保存的文件目录

4、如果本地存在同名文件,则自动删除

5、把两个回调的地址给接上去

6、事件函数返回1,表示开始接收

Function MainForm_Miniblink1_Download2(hWndForm As hWnd ,hWndControl As hWnd ,WebView As wkeWebView ,expectedContentLength As size_t ,url As CWSTR ,mime As CWSTR ,disposition As CWSTR ,job As wkeNetJob ,wkeNetJobDataBind As Any Ptr) As BOOL '页面下载事件。点击某些链接,触发下载会调用 
   Dim strFileName As String = ""
   If Len(disposition) > 0 Then
      strFileName = GetStrCenter(disposition ,"filename=""" ,"""")
   End If
   
   Dim nIndex As Integer
   If Len(strFileName) <= 0 Then
      nIndex = InStrRev(url ,"/")
      If nIndex > 0 Then
         Dim nQuery As Integer = InStr(nIndex ,url ,"?")
         If nQuery > 0 Then
            strFileName = Mid(url ,nIndex + 1 ,nQuery - nIndex - 1)
         Else
            strFileName = Mid(url, nIndex + 1)
         End If
      End If
   End If
   Dim strSaveFileName As String = FF_SaveFileDialog(hWndForm , "下载保存", strFileName, "", "所有的文件 (*.*)|*.*")
   If strSaveFileName = "" Then
      Function = 0
      Exit Function 
   End If
   
   nIndex = InStr(strSaveFileName ,"|")
   If nIndex > 0 Then
      strSaveFileName = Left(strSaveFileName, nIndex - 1)
   End If

   If FileExists(strSaveFileName) Then
      If MsgBox("文件已经存在,是否覆盖?" ,"文件覆盖提示" ,MB_YESNO) = 7 Then
         Function = 0 
         Exit Function
      End If
   End If
   
   Static d As DownInfo
   d.FileName = strSaveFileName
   d.Length = expectedContentLength
   d.Down = 0
   
   Static t As wkeNetJobDataBind
   t.param          = Varptr(d)
   t.finishCallback = @wkeNetJobDataFinishCallback
   t.recvCallback   = @wkeNetJobDataRecvCallback
   MoveMemory(wkeNetJobDataBind ,Varptr(t) ,Len(t))
   Function = 1
End Function

驰骋乾坤曰:

当然,vfb也可以不用动不动就MoveMemory,可以直接操作地址:

Dim t As wkeNetJobDataBind ptr = wkeNetJobDataBind
t->param          = Varptr(d)
t->finishCallback = @wkeNetJobDataFinishCallback
t->recvCallback   = @wkeNetJobDataRecvCallback

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值