Delphi的TWebBrowser编程综述


DelphiTWebBrowser编程综述



Delphi3

开始有了
TWebBrowser
构件,不过那时是以 ActiveX 控件的形式出现的,而且需要自己引入,在其后的 4.0 5.0 中,它就在封装好
shdocvw.dll
之后作为 Internet 构件组之一出现在构件面板上了。常常听到有人骂 Delphi 的帮助做得极差,这次的
TWebBrowser
又是 Microsoft 的东东,自然不会好到哪里去,虽说 MSDN 上什么都有,可是内容太过庞杂,如果没有入口点更是件烦人的事,
查找起来给人的感觉大概可以用一句话来形容:非常复杂、复杂非常。
   这里有平时我自己用 TWebBrowser 做程序的一些心得和上网收集到的部分例子和资料,整理了一下,希望能给有兴趣用 TWebBrowser 编程的朋友带来些帮助。
 
  
   1、初始化和终止化( Initialization & Finalization
    大家在执行 TWebBrowser 的某个方法以进行期望的操作,如 ExecWB 等的时候可能都碰到过 试图激活未注册的丢失目标 “OLE 对象
未注册 等错误,或者并没有出错但是得不到希望的结果,比如不能将选中的网页内容复制到剪贴板等。以前用它编程的时候,我发现 ExecWB 有时侯起作用但
有时侯又不行,在 Delphi 生成的缺省工程主窗口上加入 TWebBrowser ,运行时并不会出现 “OLE 对象未注册 的错误。同样是一个偶然的机会,
我才知道 OLE 对象需要初始化和终止化(懂得的东东实在太少了)。
    我用我的前一篇文章《 Delphi 程序窗口动画 & 正常排列平铺的解决》所说的方法编程,运行时出了上面所说的错误,我便猜想应该有
OleInitialize
之类的语句,于是,找到并加上了下面几句话,终于搞定!究其原因,我想大概是由于 TWebBrowser 是一个嵌入的 OLE
象而不算是用 Delphi 编写的 VCL 吧。
      initialization
       OleInitialize(nil);
      finalization
       try
        OleUninitialize;
       except
       end;
     这几句话放在主窗口所有语句之后, “end.” 之前。
   -----------------------------------------------------------------------------------
   2、 EmptyParam
     在 Delphi 5 TWebBrowser Navigate 方法被多次重载:
      procedure Navigate(const URL: WideString); overload;
      procedure Navigate(const URL: WideString; var Flags:
   OleVariant); overload;
      procedure Navigate(const URL: WideString; var Flags:
   OleVariant; var TargetFrameName:      OleVariant); overload;
      procedure Navigate(const URL: WideString; var Flags:
   OleVariant; var TargetFrameName:      OleVariant; var PostData:
   OleVariant); overload;
      procedure Navigate(const URL: WideString; var Flags:
   OleVariant; var TargetFrameName:      OleVariant; var PostData:
   OleVariant; var Headers: OleVariant); overload;
     而在实际应用中,使用后几种方法调用时,由于我们很少用到后面几个参数,但函数声明又要求是变量参数,一般的做法如下:
      var
       t:OleVariant;
      begin
       webbrowser1.Navigate(edit1.text,t,t,t,t);
      end;
     需要定义变量 t (还有很多地方要用到它),很麻烦。其实我们可以用 EmptyParam 来代替( EmptyParam 是一个公用的 Variant 空变量,不要对它赋值),只需一句话就可以了:
      webbrowser1.Navigate(edit1.text,EmptyParam,EmptyParam,EmptyParam,EmptyParam);
     虽然长一点,但比每次都定义变量方便得多。当然,也可以使用第一种方式。
      webbrowser1.Navigate(edit1.text)
   -----------------------------------------------------------------------------------
   3、命令操作   常用的命令操作用 ExecWB 方法即可完成, ExecWB 同样多次被重载:
      procedure ExecWB(cmdID: OLECMDID; cmdexecopt:
   OLECMDEXECOPT); overload;
      procedure ExecWB(cmdID: OLECMDID; cmdexecopt: OLECMDEXECOPT;
   var pvaIn:
        OleVariant); overload;
      procedure ExecWB(cmdID: rOLECMDID; cmdexecopt:
   OLECMDEXECOPT; var pvaIn:
        OleVariant; var pvaOut: OleVariant); overload;
     打开: 弹出 打开 Internet 地址 对话框, CommandID OLECMDID_OPEN (若浏览器版本为 IE5.0
         则此命令不可用)。
     另存为:调用 另存为 对话框。
          ExecWB(OLECMDID_SAVEAS,OLECMDEXECOPT_DODEFAULT,
   EmptyParam,
               EmptyParam);
  
  
     打印、打印预览和页面设置: 调用 打印 打印预览 页面设置 对话框( IE5.5 及以上版本才支持打
                   印预览,故实现应该检查此命令是否可用)。
          ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_DODEFAULT,
   EmptyParam,
               EmptyParam);
          if QueryStatusWB(OLECMDID_PRINTPREVIEW)=3 then
           ExecWB(OLECMDID_PRINTPREVIEW, OLECMDEXECOPT_DODEFAULT,
               EmptyParam,EmptyParam);
          ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_DODEFAULT,
   EmptyParam,
               EmptyParam);
  
  
     剪切、复制、粘贴、全选: 功能无须多说,需要注意的是:剪切和粘贴不仅对编辑框文字,而且对网页上的非编
                  辑框文字同样有效,用得好的话,也许可以做出功能特殊的东东。获得其命令使能状
                  态和执行命令的方法有两种(以复制为例,剪切、粘贴和全选分别将各自的关键字替
                  换即可,分别为 CUT,PASTE SELECTALL ):
       A 、用 TWebBrowser QueryStatusWB 方法。
         if(QueryStatusWB(OLECMDID_COPY)=OLECMDF_ENABLED) or
          OLECMDF_SUPPORTED) then
          ExecWB(OLECMDID_COPY, OLECMDEXECOPT_DODEFAULT,
   EmptyParam,
               EmptyParam);
       B 、用 IHTMLDocument2 QueryCommandEnabled 方法。
         var
          Doc: IHTMLDocument2;
         begin
          Doc :=WebBrowser1.Document as IHTMLDocument2;
          if Doc.QueryCommandEnabled('Copy') then
           Doc.ExecCommand('Copy',false,EmptyParam);
         end;
     查找: 参考第九条 查找 功能。
   -----------------------------------------------------------------------------------
   4、字体大小
     类似 字体 菜单上的从 最大 最小 五项(对应整数 0 4 Largest 等假设为五个菜单项的名字, Tag
   属性分别设为 0 4 )。
       A 、读取当前页面字体大小。
         var
          t: OleVariant;
         Begin
          WebBrowser1.ExecWB(OLECMDID_ZOOM,
   OLECMDEXECOPT_DONTPROMPTUSER,
           EmptyParam,t);
          case t of
          4: Largest.Checked :=true;
          3: Larger.Checked :=true;
          2: Middle.Checked :=true;
          1: Small.Checked :=true;
          0: Smallest.Checked :=true;
          end;
         end;
       B 、设置页面字体大小。
         Largest.Checked :=false;
         Larger.Checked :=false;
         Middle.Checked :=false;
         Small.Checked :=false;
         Smallest.Checked :=false;
         TMenuItem(Sender).Checked :=true;
         t :=TMenuItem(Sender).Tag;
         WebBrowser1.ExecWB(OLECMDID_ZOOM,
   OLECMDEXECOPT_DONTPROMPTUSER,
          t,t);
   -----------------------------------------------------------------------------------
   5、添加到收藏夹和整理收藏夹
        const
         CLSID_ShellUIHelper: TGUID =
   '{64AB4BB7-111E-11D1-8F79-00C04FC2FBE1}';
        var
         p:procedure(Handle: THandle; Path: PChar); stdcall;
        procedure TForm1.OrganizeFavorite(Sender: Tobject);
        var
         H: HWnd;
        begin
         H := LoadLibrary(PChar('shdocvw.dll'));
         if H <> 0 then
         begin
        p := GetProcAddress(H, PChar('DoOrganizeFavDlg'));
          if Assigned(p) then p(Application.Handle,
   PChar(FavFolder));
         end;
         FreeLibrary(h);
        end;
       
        procedure TForm1.AddFavorite(Sender: TObject);
        var
         ShellUIHelper: ISHellUIHelper;
         url, title: Olevariant;
        begin
         Title := Webbrowser1.LocationName;
         Url := Webbrowser1.LocationUrl;
         if Url <> '' then
         begin
          ShellUIHelper := CreateComObject(CLSID_SHELLUIHELPER) as
   IShellUIHelper;
          ShellUIHelper.AddFavorite(url, title);
         end;
        end;
    用上面的通过 ISHellUIHelper 接口来打开 添加到收藏夹 对话框的方法比较简单,但是有个缺陷,就是打开的窗口不是模式窗口,而是独
立于应用程序的。可以想象,如果使用与 OrganizeFavorite 过程同样的方法来打开对话框,由于可以指定父窗口的句柄,自然可以实现模式窗口
(效果与在资源管理器和 IE 中打开 添加到收藏夹 对话框相同)。问题显然是这样的,上面两个过程的作者当时只知道 shdocvw.dll
DoOrganizeFavDlg
的原型而不知道 DoAddToFavDlg 的原型,所以只好用 ISHellUIHelper 接口来实现(或许是他不够严
谨,认为是否是模式窗口无所谓?)。
    下面的过程就告诉你 DoAddToFavDlg 的函数原型。需要注意的是,这样打开的对话框并不执行 添加到收藏夹 的操作,它只是告诉应用程序
用户是否选择了 确定 ,同时在 DoAddToFavDlg 的第二个参数中返回用户希望放置 Internet 快捷方式的路径,建立 .Url 文件的工作由应
用程序自己来完成。
        procedure TForm1.AddFavorite(IE: TEmbeddedWB);
         procedure CreateUrl(AUrlPath, AUrl: PChar);
         var
          URLfile: TIniFile;
         begin
          URLfile := TIniFile.Create(String(AUrlPath));
         RLfile.WriteString('InternetShortcut', 'URL',
   String(AUrl));
         RLfile.Free;
         end;
        var
         AddFav: function(Handle: THandle;
          UrlPath: PChar; UrlPathSize: Cardinal;
          Title: PChar; TitleSize: Cardinal;
          FavIDLIST: pItemIDList): Bool; stdcall;
         FDoc: IHTMLDocument2;
         UrlPath, url, title: array[0..MAX_PATH] of char;
         H: HWnd;
         pidl: pItemIDList;
         FRetOK: Bool;
        begin
         FDoc := IHTMLDocument2(IE.Document);
         if FDoc = nil then exit;
         StrPCopy(Title, FDoc.Get_title);
         StrPCopy(url, FDoc.Get_url);
         if Url <> '' then
         begin
          H := LoadLibrary(PChar('shdocvw.dll'));
          if H <> 0 then
          begin
           SHGetSpecialFolderLocation(0, CSIDL_FAVORITES, pidl);
           AddFav := GetProcAddress(H, PChar('DoAddToFavDlg'));
           if Assigned(AddFav) then
            FRetOK :=AddFav(Handle, UrlPath, Sizeof(UrlPath),
   Title, Sizeof(Title), pidl)
          end;
          FreeLibrary(h);
          if FRetOK then
           CreateUrl(UrlPath, Url);
         end
        end;
  
  
   -----------------------------------------------------------------------------------
   6、使 WebBrowser 获得焦点
      TWebBrowser 非常特殊,它从 TWinControl 继承来的 SetFocus 方法并不能使得它所包含的文档获得焦点,从而不能立即使用 Internet
   Explorer 本身具有得快捷键,解决方法如下: <
      procedure TForm1.SetFocusToDoc;
      begin
       if WebBrowser1.Document <> nil then
        with WebBrowser1.Application as IOleobject do
         DoVerb(OLEIVERB_UIACTIVATE, nil, WebBrowser1, 0, Handle,
   GetClientRect);
      end;
     除此之外,我还找到一种更简单的方法,这里一并列出:
      if WebBrowser1.Document <> nil then
       IHTMLWindow2(IHTMLDocument2(WebBrowser1.Document).ParentWindow).focus
  
  
     刚找到了更简单的方法,也许是最简单的:
      if WebBrowser1.Document <> nil then
       IHTMLWindow4(WebBrowser1.Document).focus
     还有,需要判断文档是否获得焦点这样来做:
      if IHTMLWindow4(WebBrowser1.Document).hasfocus then
   -----------------------------------------------------------------------------------
   7、点击 提交 按钮
    如同程序里每个窗体上有一个 缺省 按钮一样, Web 页面上的每个 Form 也有一个 缺省 按钮 —— 即属性为 “Submit” 的按钮,当用户按下
回车键时就相当于鼠标单击了 “Submit” 。但是 TWebBrowser 似乎并不响应回车键,并且,即使把包含 TWebBrowser 的窗体的
KeyPreview
设为 True ,在窗体的 KeyPress 事件里还是不能截获用户向 TWebBrowser 发出的按键。
    我的解决办法是用 ApplicatinEvents 构件或者自己编写 TApplication 对象的 OnMessage 事件,在其中判断消息类
型,对键盘消息做出响应。至于点击 提交 按钮,可以通过分析网页源代码的方法来实现,不过我找到了更为简单快捷的方法,有两种,第一种是我自己想出来
的,另一种是别人写的代码,这里都提供给大家,以做参考。
      A 、用 SendKeys 函数向 WebBrowser 发送回车键
       Delphi
   5 光盘上的 Info/Extras/SendKeys 目录下有一个 SndKey32.pas 文件,其中包含了两个函数 SendKeys
AppActivate
,我们可以用 SendKeys 函数来向 WebBrowser 发送回车键,我现在用的就是这个方法,使用很简单,在
WebBrowser
获得焦点的情况下(不要求 WebBrowser 所包含的文档获得焦点),用一条语句即可:
       Sendkeys('~',true);// press RETURN key
       SendKeys 函数的详细参数说明等,均包含在 SndKey32.pas 文件中。
      B 、在 OnMessage 事件中将接受到的键盘消息传递给 WebBrowser
       procedure TForm1.ApplicationEvents1Message(var Msg: TMsg;
   var Handled: Boolean);
       {fixes the malfunction of some keys within webbrowser
   control}
       const
        StdKeys = [VK_TAB, VK_RETURN]; { standard keys }
        ExtKeys = [VK_DELETE, VK_BACK, VK_LEFT, VK_RIGHT]; {
   extended keys }
        fExtended = $01000000; { extended key flag }
       begin
        Handled := False;
        with Msg do
        if ((Message >= WM_KEYFIRST) and (Message <= WM_KEYLAST))
   and
         ((wParam in StdKeys) or
         {$IFDEF VER120}(GetKeyState(VK_CONTROL) < 0) or {$ENDIF}
         (wParam in ExtKeys) and
         ((lParam and fExtended) = fExtended)) then
        try
         if IsChild(Handle, hWnd) then { handles all browser
   related messages }
         begin
          with {$IFDEF
   VER120}Application_{$ELSE}Application{$ENDIF} as
            IOleInPlaceActiveObject do
           Handled := TranslateAccelerator(Msg) = S_OK;
           if not Handled then
           begin
            Handled := True;
            TranslateMessage(Msg);
            DispatchMessage(Msg);
           end;
           end;
        except
        end;
       end; // MessageHandler
     (此方法来自 EmbeddedWB.pas
   -----------------------------------------------------------------------------------
   8、直接从 TWebBrowser 得到网页源码及 Html
    下面先介绍一种极其简单的得到 TWebBrowser 正在访问的网页源码的方法。一般方法是利用 TWebBrowser 控件中的 Document
对象提供的 IPersistStreamInit 接口来实现,具体就是:先检查 WebBrowser.Document 对象是否有效,无效则退出;然后取
IPersistStreamInit 接口,接着取得 HTML 源码的大小,分配全局堆内存块,建立流,再将 HTML 文本写到流中。程序虽然不算复杂,但是
有更简单的方法,所以实现代码不再给出。其实基本上所有 IE 的功能 TWebBrowser 都应该有较为简单的方法来实现,获取网页源码也是一样。下面的代
码将网页源码显示在 Memo1 中。
       Memo1.Lines.Add(IHtmlDocument2(WebBrowser1.Document).Body.OuterHtml);
  
  
     同时,在用 TWebBrowser 浏览 HTML 文件的时候要将其保存为文本文件就很简单了,不需要任何的语法解析工具,因为 TWebBrowser 也完成了,如下:
       Memo1.Lines.Add(IHtmlDocument2(WebBrowser1.Document).Body.OuterText);
  
  
   -----------------------------------------------------------------------------------
   9、 查找 功能
    查找对话框可以在文档获得焦点的时候通过按键 Ctrl-F 来调出,程序中则调用 IOleCommandTarget 对象的成员函数 Exec 执行
OLECMDID_FIND
操作来调用,下面给出的方法是如何在程序中用代码来做出文字选择,即你可以自己设计查找对话框。
       var
        Doc: IHtmlDocument2;
        TxtRange: IHtmlTxtRange;
       begin
        Doc :=WebBrowser1.Document as IHtmlDocument2;
        Doc.SelectAll;      // 此处为简写,选择全部文档的方法请参见第三条命令操作
                    // 这句话尤为重要,因为 IHtmlTxtRange 对象的方法能够操作的前提是
                    //Document 已经有一个文字选择区域。由于接着执行下面的语句,所以不会
                    // 看到文档全选的过程。
        TxtRange :=Doc.Selection.CreateRange as IHtmlTxtRange;
        TxtRange.FindText('Text to be searched',0.0);
        TxtRange.Select;
       end;
     还有,从 Txt.Get_text 可以得到当前选中的文字内容,某些时候是有用的。
   -----------------------------------------------------------------------------------
   10、提取网页中所有链接
     这个方法来自大富翁论坛 hopfield 朋友的对一个问题的回答,我本想自己试验,但总是没成功。
      var
       doc:IHTMLDocument2;
       all:IHTMLElementCollection;
       len,i:integer;
       item:OleVariant;
      begin
       doc:=WebBrowser1 .Document as IHTMLDocument2;
       all:=doc.Get_links;               //doc.Links 亦可
       len:=all.length;
       for i:=0 to len-1 do begin
        item:=all.item(i,varempty);          //EmpryParam 亦可
        memo1.lines.add(item.href);
       end;
      end;
   -----------------------------------------------------------------------------------
   11、设置 TWebBrowser 的编码
     为什么我总是错过很多机会?其实早就该想到的,但是一念之差,便即天壤之别。当时我要是肯再多考虑一下,多试验一下,这就不会排到第11条了。下面给出一个函数,搞定,难以想象的简单。
      procedure SetCharSet(AWebBrowser: TWebBrowser; ACharSet:
   String);
      var
       RefreshLevel: OleVariant;
      Begin
       IHTMLDocument2(AWebBrowser.Document).Set_CharSet(ACharSet);
       RefreshLevel :=7;                // 这个 7 应该从注册表来,帮助有 Bug
       AWebBrowser.Refresh2(RefreshLevel);
      End;  

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值