如何禁用 TWebBrowser 上下文菜单?

Posted

技术标签:

【中文标题】如何禁用 TWebBrowser 上下文菜单?【英文标题】:How to disable TWebBrowser context menu? 【发布时间】:2021-09-27 19:13:00 【问题描述】:

我有一个包含TWebBrowser 组件并被我的一些应用程序使用的框架,我需要禁用TWebBrowser 的默认弹出菜单。

我找到了一个适用于应用程序级别的解决方案,通过这种方式使用TApplicationEvents 组件及其OnMessage 事件处理程序:

procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
  var Handled: Boolean);
begin
  if (Msg.Message = WM_RBUTTONDOWN) or (Msg.Message = WM_RBUTTONDBLCLK) then
  begin
    if IsChild(WebBrowser1.Handle, Msg.hwnd) then
    begin
      Handled := True;
    end;
  end;
end;

我正在寻找一种可以在框架/TWebBrowser 级别工作的解决方案,而无需在应用程序级别添加代码。

我已尝试分配TWebBrowserTPopupMenu 属性,但它仅在将页面加载到WebBrowser 之前有效。

我已尝试分配TWebBrowserWindowProc,但在WebBrowser 中加载页面后,不再执行代码。

  private
    FPrevBrowWindowProc : TWndMethod;
    procedure BrowWindowProc(var AMessage: TMessage);

...

procedure TFrame1.BrowWindowProc(var AMessage: TMessage);
begin
  if(AMessage.Msg = WM_RBUTTONDOWN) or (AMessage.Msg = WM_RBUTTONDBLCLK) then 
    Exit;

  if(Assigned(FPrevBrowWindowProc))
  then FPrevBrowWindowProc(AMessage);
end;

constructor TFrame1.Create(AOwner : TComponent);
begin
  inherited;

  FPrevBrowWindowProc := WebBrowser1.WindowProc;
  VS_Brow.WindowProc := BrowWindowProc;
end;

【问题讨论】:

“它只在网页浏览器上加载页面之前有效” 那么也许你应该在页面加载之后分配它? @Oliver 加载网页不应该影响使用TPopupMenu 属性的场景。 @Fabrizio 我们在这里谈论的是 WebBrowser 上下文菜单还是 WebPage 上下文菜单。网页可以注册并显示他们自己的自定义上下文菜单,而不是默认的浏览器上下文菜单。 @SilverWarior:我想禁用所有上下文菜单。基本上,我不想让用户右键单击。我通过添加用户右键单击时当前出现的弹出菜单的图片来更新问题 这是彼得·约翰逊的描述表格:How to cusomise teh TWebBrowser user interface (part 4 of 6) 【参考方案1】:

这是使用 IE 时的解决方案。也许有人会为我提供如何使用 Edge TEdgeBrowser Popup menu 的解决方案!

P D Johnson 的以下单元,http://www.delphidabbler.com/articles?article=22 是实施所必需的。我不知道新的 URL 地址,抱歉。

    
    This demo application accompanies the article
    "How to call Delphi code from scripts running in a TWebBrowser" at
    http://www.delphidabbler.com/articles?article=22.

    This unit provides a do-nothing implementation of a web browser OLE container
    object

    This code is copyright (c) P D Johnson (www.delphidabbler.com), 2005-2006.

    v1.0 of 2005/05/09 - original version named UBaseUIHandler.pas
    v2.0 of 2006/02/11 - total rewrite based on unit of same name from article at
                         http://www.delphidabbler.com/articles?article=22
  


  $A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O+,P+,Q-,R-,S-,T-,U-,V+,W-,X+,Y+,Z1
  $WARN UNSAFE_TYPE OFF


  unit UContainerBasis;

  interface

  uses
    Winapi.Windows, Winapi.ActiveX, Winapi.Mshtmhst, SHDocVw;

  type
    TContainerBasis = class(TObject,
      IUnknown, IOleClientSite, IDocHostUIHandler)
    private
      fHostedBrowser: TWebBrowser;
      // Registration method
      procedure SetBrowserOleClientSite(const Site: IOleClientSite);
    protected
       IUnknown 
      function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
      function _AddRef: Integer; stdcall;
      function _Release: Integer; stdcall;
       IOleClientSite 
      function SaveObject: HResult; stdcall;
      function GetMoniker(dwAssign: Longint;
        dwWhichMoniker: Longint;
        out mk: IMoniker): HResult; stdcall;
      function GetContainer(
        out container: IOleContainer): HResult; stdcall;
      function ShowObject: HResult; stdcall;
      function OnShowWindow(fShow: BOOL): HResult; stdcall;
      function RequestNewObjectLayout: HResult; stdcall;
       IDocHostUIHandler 
      function ShowContextMenu(const dwID: DWORD; const ppt: PPOINT;
        const pcmdtReserved: IUnknown; const pdispReserved: IDispatch): HResult;
        stdcall;
      function GetHostInfo(var pInfo: TDocHostUIInfo): HResult; stdcall;
      function ShowUI(const dwID: DWORD;
        const pActiveObject: IOleInPlaceActiveObject;
        const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame;
        const pDoc: IOleInPlaceUIWindow): HResult; stdcall;
      function HideUI: HResult; stdcall;
      function UpdateUI: HResult; stdcall;
      function EnableModeless(const fEnable: BOOL): HResult; stdcall;
      function OnDocWindowActivate(const fActivate: BOOL): HResult; stdcall;
      function OnFrameWindowActivate(const fActivate: BOOL): HResult; stdcall;
      function ResizeBorder(const prcBorder: PRECT;
        const pUIWindow: IOleInPlaceUIWindow; const fFrameWindow: BOOL): HResult;
        stdcall;
      function TranslateAccelerator(const lpMsg: PMSG; const pguidCmdGroup: PGUID;
        const nCmdID: DWORD): HResult; stdcall;
      function GetOptionKeyPath(var pchKey: POLESTR; const dw: DWORD ): HResult;
        stdcall;
      function GetDropTarget(const pDropTarget: IDropTarget;
        out ppDropTarget: IDropTarget): HResult; stdcall;
      function GetExternal(out ppDispatch: IDispatch): HResult; stdcall;
      function TranslateUrl(const dwTranslate: DWORD; const pchURLIn: POLESTR;
        var ppchURLOut: POLESTR): HResult; stdcall;
      function FilterDataObject(const pDO: IDataObject;
        out ppDORet: IDataObject): HResult; stdcall;
    public
      constructor Create(const HostedBrowser: TWebBrowser);
      destructor Destroy; override;
      property HostedBrowser: TWebBrowser read fHostedBrowser;
    end;


  implementation

  uses
    System.SysUtils;

   TNulWBContainer 

  constructor TContainerBasis.Create(const HostedBrowser: TWebBrowser);
  begin
    Assert(Assigned(HostedBrowser));
    inherited Create;
    fHostedBrowser := HostedBrowser;
    SetBrowserOleClientSite(Self as IOleClientSite);
  end;

  destructor TContainerBasis.Destroy;
  begin
    SetBrowserOleClientSite(nil);
    inherited;
  end;

  function TContainerBasis.EnableModeless(const fEnable: BOOL): HResult;
  begin
     Return S_OK to indicate we handled (ignored) OK 
    Result := S_OK;
  end;

  function TContainerBasis.FilterDataObject(const pDO: IDataObject;
    out ppDORet: IDataObject): HResult;
  begin
     Return S_FALSE to show no data object supplied.
      We *must* also set ppDORet to nil 
    ppDORet := nil;
    Result := S_FALSE;
  end;

  function TContainerBasis.GetContainer(
    out container: IOleContainer): HResult;
    Returns a pointer to the container's IOleContainer
    interface
  begin
     We do not support IOleContainer.
      However we *must* set container to nil 
    container := nil;
    Result := E_NOINTERFACE;
  end;

  function TContainerBasis.GetDropTarget(const pDropTarget: IDropTarget;
    out ppDropTarget: IDropTarget): HResult;
  begin
     Return E_FAIL since no alternative drop target supplied.
      We *must* also set ppDropTarget to nil 
    ppDropTarget := nil;
    Result := E_FAIL;
  end;

  function TContainerBasis.GetExternal(out ppDispatch: IDispatch): HResult;
  begin
     Return E_FAIL to indicate we failed to supply external object.
      We *must* also set ppDispatch to nil 
    ppDispatch := nil;
    Result := E_FAIL;
  end;

  function TContainerBasis.GetHostInfo(var pInfo: TDocHostUIInfo): HResult;
  begin
     Return S_OK to indicate UI is OK without changes 
    Result := S_OK;
  end;

  function TContainerBasis.GetMoniker(dwAssign, dwWhichMoniker: Integer;
    out mk: IMoniker): HResult;
    Returns a moniker to an object's client site
  begin
     We don't support monikers.
      However we *must* set mk to nil 
    mk := nil;
    Result := E_NOTIMPL;
  end;

  function TContainerBasis.GetOptionKeyPath(var pchKey: POLESTR;
    const dw: DWORD): HResult;
  begin
     Return E_FAIL to indicate we failed to override
      default registry settings 
    Result := E_FAIL;
  end;

  function TContainerBasis.HideUI: HResult;
  begin
     Return S_OK to indicate we handled (ignored) OK 
    Result := S_OK;
  end;

  function TContainerBasis.OnDocWindowActivate(
    const fActivate: BOOL): HResult;
  begin
     Return S_OK to indicate we handled (ignored) OK 
    Result := S_OK;
  end;

  function TContainerBasis.OnFrameWindowActivate(
    const fActivate: BOOL): HResult;
  begin
     Return S_OK to indicate we handled (ignored) OK 
    Result := S_OK;
  end;

  function TContainerBasis.OnShowWindow(fShow: BOOL): HResult;
    Notifies a container when an embedded object's window
    is about to become visible or invisible
  begin
     Return S_OK to pretend we've responded to this 
    Result := S_OK;
  end;

  function TContainerBasis.QueryInterface(const IID: TGUID; out Obj): HResult;
  begin
    if GetInterface(IID, Obj) then
      Result := S_OK
    else
      Result := E_NOINTERFACE;
  end;

  function TContainerBasis.RequestNewObjectLayout: HResult;
    Asks container to allocate more or less space for
    displaying an embedded object
  begin
     We don't support requests for a new layout 
    Result := E_NOTIMPL;
  end;

  function TContainerBasis.ResizeBorder(const prcBorder: PRECT;
    const pUIWindow: IOleInPlaceUIWindow; const fFrameWindow: BOOL): HResult;
  begin
     Return S_FALSE to indicate we did nothing in response 
    Result := S_FALSE;
  end;

  function TContainerBasis.SaveObject: HResult;
    Saves the object associated with the client site
  begin
     Return S_OK to pretend we've done this 
    Result := S_OK;
  end;

  procedure TContainerBasis.SetBrowserOleClientSite(
    const Site: IOleClientSite);
  var
    OleObj: IOleObject;
  begin
    Assert((Site = Self as IOleClientSite) or (Site = nil));
    if not Supports(
      fHostedBrowser.DefaultInterface, IOleObject, OleObj
    ) then
      raise Exception.Create(
        'Browser''s Default interface does not support IOleObject'
      );
    OleObj.SetClientSite(Site);
  end;

  function TContainerBasis.ShowContextMenu(const dwID: DWORD;
    const ppt: PPOINT; const pcmdtReserved: IInterface;
    const pdispReserved: IDispatch): HResult;
  begin
     Return S_FALSE to notify we didn't display a menu and to
    let browser display its own menu 
    Result := S_FALSE
  end;

  function TContainerBasis.ShowObject: HResult;
    Tells the container to position the object so it is
    visible to the user
  begin
     Return S_OK to pretend we've done this 
    Result := S_OK;
  end;

  function TContainerBasis.ShowUI(const dwID: DWORD;
    const pActiveObject: IOleInPlaceActiveObject;
    const pCommandTarget: IOleCommandTarget; const pFrame: IOleInPlaceFrame;
    const pDoc: IOleInPlaceUIWindow): HResult;
  begin
     Return S_OK to say we displayed own UI 
    Result := S_OK;
  end;

  function TContainerBasis.TranslateAccelerator(const lpMsg: PMSG;
    const pguidCmdGroup: PGUID; const nCmdID: DWORD): HResult;
  begin
     Return S_FALSE to indicate no accelerators are translated 
    Result := S_FALSE;
  end;

  function TContainerBasis.TranslateUrl(const dwTranslate: DWORD;
    const pchURLIn: POLESTR; var ppchURLOut: POLESTR): HResult;
  begin
     Return E_FAIL to indicate that no translations took place 
    Result := E_FAIL;
  end;

  function TContainerBasis.UpdateUI: HResult;
  begin
     Return S_OK to indicate we handled (ignored) OK 
    Result := S_OK;
  end;

  function TContainerBasis._AddRef: Integer;
  begin
    Result := -1;
  end;

  function TContainerBasis._Release: Integer;
  begin
    Result := -1;
  end;

  end.

这是实际的程序: UMain.pas

        unit UMain;

    interface

    uses
      Winapi.Windows, Winapi.Messages, Winapi.ActiveX,  Winapi.Mshtmhst,
      System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.OleCtrls, Vcl.Edge, SHDocVw,
      Vcl.Menus, UContainerBasis, Vcl.StdCtrls;

    const
      html= '<!DOCTYPE html><html lang="de"><head><title>Hallo Welt</title><style type="text/css">' +
            '.verlauffont-size:27px;-webkit-background-clip: text;-webkit-text-fill-color: transparent;' +
            'background-color: #ba254c;background-image: linear-gradient(to right,#ba254c 30%,#392ea4 70%);' +
            'background-size: cover;background-position: center center;</style>' +
            '</head><body><b class="verlauf">Hallöchen - Welt!</b></body></html>';

    type

      TWBContainer = class(TContainerBasis, IDocHostUIHandler, IOleClientSite)
      private
        FbUserPopUp: boolean;
      protected
        function ShowContextMenu(const AiID: DWORD; const ApptPos: PPOINT;
                                 const AptrCmdtReserved: IUnknown;
                                 const AptrDispReserved: IDispatch): HResult; stdcall;
      public
         property bUserPopUp: Boolean  read  FbUserPopUp
                                       write FbUserPopUp   default False;

      end;

      TForm1 = class(TForm)
        WebIE: TWebBrowser;
        Splitter1: TSplitter;
        WebEdge: TWebBrowser;
        mnp: TPopupMenu;
        Eins1: TMenuItem;
        Zwei1: TMenuItem;
        Drei1: TMenuItem;
        Panel1: TPanel;
        chkIE: TCheckBox;
        chkEdge: TCheckBox;
        procedure FormActivate(Sender: TObject);
        procedure chkIEClick(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        FEdge  : TEdgeBrowser;
        FWbIe  : TWBContainer;
        FWbEdge: TWBContainer;
      public
         Public-Deklarationen 
      end;

    var
      Form1: TForm1;

    implementation

    uses
      System.Rtti;


    $R *.dfm

    function TWBContainer.ShowContextMenu(const AiID: DWORD; const ApptPos: PPOINT;
                                          const AptrCmdtReserved: IUnknown;
                                          const AptrDispReserved: IDispatch): HResult; stdcall;
    begin
      if bUserPopUp then
      begin
        Result := S_OK; // Ok. I do it myself.
        if Assigned(HostedBrowser.PopupMenu) then
          HostedBrowser.PopupMenu.Popup(ApptPos.X, ApptPos.Y); //Show own Popup
      end
      else
        Result := S_FALSE; // Orign Popup. You do it
    end;

    procedure TForm1.chkIEClick(Sender: TObject);
    begin
      if Sender = chkIE then
        FWbIe.bUserPopUp := chkIE.Checked
      else
        FWbEdge.bUserPopUp := chkEdge.Checked
    end;

    procedure TForm1.FormActivate(Sender: TObject);
    var
      doc: variant;
      LcT: string;
      rtC: TRttiContext;
      rtT: TRttiType;
      rtF: TRttiField;
    begin
      OnActivate := nil;
      FWbIe   := nil;
      FWbEdge := nil;

      Top    := 50;
      Height := 600;
      Width  := 600;

      WebIE.Height := 270;
      WebIE.PopupMenu := mnp;

      FWbIe  := TWBContainer.Create(WebIE);
      FWbIe.bUserPopUp := chkIE.Checked;

      WebIE.Navigate('about:blank');
      doc := WebIE.Document;
      doc.clear;
      doc.write(HTML);
      doc.close;


      LcT := ExtractFilePath(ParamStr(0));
      LcT := LcT + 'WebView2Loader.dll';
      if not FileExists(LcT) then
        raise Exception.Create('WebView2Loader.dll not found!');

      WebEdge.PopupMenu := mnp;
      try
        FWbEdge := TWBContainer.Create(WebEdge);
        FWbEdge.bUserPopUp := chkEdge.Checked;
        chkEdge.Enabled := true;
      except
        on E: Exception do
          ShowMessage(Format('Error %s; %s', [E.Message, E.ClassName]));
      end;


      //to trigger CreateWebView
      WebEdge.Navigate('about:blank');
      //doc := WebEdge.Document;  //0 !!!
      //WebEdge.Navigate(HTML);
      //Exit;

      //Psalm 130, 1
      //  Out of the depths I cry to you, Lord.
      //     https://www.youtube.com/watch?v=lm84E2At9Zk
      rtc := TRttiContext.Create;
      try
        rtt := rtc.GetType(TWebBrowser);
        rtF := rtt.GetField('FEdge');
        FEdge := rtF.GetValue(WebEdge).AsObject as TEdgeBrowser;
      finally
        rtF.Free;
        rtt.Free;
      end;

      while FEdge.BrowserControlState = TCustomEdgeBrowser.TBrowserControlState.Creating do
      begin
        Application.ProcessMessages;
      end;

      FEdge.NavigateToString(HTML);
    end;

    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      FreeAndNil(FWbIe);
      FreeAndNil(FWbEdge);
    end;

    end.

UMain.dfm:

object Form1: TForm1
   Left = 0
   Top = 0
   Caption = 'Form1'
   ClientHeight = 289
   ClientWidth = 554
   Color = clBtnFace
   Font.Charset = DEFAULT_CHARSET
   Font.Color = clWindowText
   Font.Height = -11
   Font.Name = 'Tahoma'
   Font.Style = []
   OldCreateOrder = False
   OnActivate = FormActivate
   OnDestroy = FormDestroy
   PixelsPerInch = 96
   TextHeight = 13
   object Splitter1: TSplitter
     Left = 0
     Top = 185
     Width = 554
     Height = 3
     Cursor = crVSplit
     Align = alTop
     ExplicitTop = 150
     ExplicitWidth = 139
   end
   object Panel1: TPanel
     Left = 0
     Top = 0
     Width = 554
     Height = 35
     Align = alTop
     TabOrder = 2
     object chkIE: TCheckBox
       Left = 19
       Top = 9
       Width = 97
       Height = 17
       Caption = 'IE PopUp'
       Checked = True
       State = cbChecked
       TabOrder = 0
       OnClick = chkIEClick
     end
     object chkEdge: TCheckBox
       Left = 114
       Top = 10
       Width = 97
       Height = 17
       Caption = 'Edge PopUp'
       Enabled = False
       TabOrder = 1
       OnClick = chkIEClick
     end
   end
   object WebIE: TWebBrowser
     Left = 0
     Top = 35
     Width = 554
     Height = 150
     Align = alTop
     PopupMenu = mnp
     TabOrder = 0
     ExplicitLeft = 144
     ExplicitTop = 40
     ExplicitWidth = 300
     ControlData = 
       4C00000042390000810F00000000000000000000000000000000000000000000
       000000004C000000000000000000000001000000E0D057007335CF11AE690800
       2B2E126209000000000000004C0000000114020000000000C000000000000046
       8000000000000000000000000000000000000000000000000000000000000000
       00000000000000000100000000000000000000000000000000000000
   end
   object WebEdge: TWebBrowser
     Left = 0
     Top = 188
     Width = 554
     Height = 101
     Align = alClient
     PopupMenu = mnp
     TabOrder = 1
     SelectedEngine = EdgeOnly
     ExplicitLeft = 168
     ExplicitTop = 156
     ExplicitWidth = 300
     ExplicitHeight = 150
     ControlData = 
       4C00000042390000700A00000000000000000000000000000000000000000000
       000000004C000000000000000000000001000000E0D057007335CF11AE690800
       2B2E126209000000000000004C0000000114020000000000C000000000000046
       8000000000000000000000000000000000000000000000000000000000000000
       00000000000000000100000000000000000000000000000000000000
   end
   object mnp: TPopupMenu
     Left = 432
     Top = 40
     object Eins1: TMenuItem
       Caption = 'Eins'
     end
     object Zwei1: TMenuItem
       Caption = 'Zwei'
     end
     object Drei1: TMenuItem
       Caption = 'Drei'
     end
   end
 end

【讨论】:

以上是关于如何禁用 TWebBrowser 上下文菜单?的主要内容,如果未能解决你的问题,请参考以下文章

如何禁用 WebView 的上下文菜单?

VBA:如何从右键单击上下文菜单中禁用某些选项

如何禁用右键单击事件或如何隐藏 Autodesk Forge 查看器上的上下文菜单

WindowChrome - 如何修改或禁用标题栏中的上下文菜单?

如何在 Chrome 的信息亭模式下禁用右键单击/长按的上下文菜单?

在不禁用上下文菜单的情况下管理 UITextfiled 上的长按?