老问题了
Abstract:Hooking the IWebBrowser and DHTML controls to properly handle keystrokes
 The problem: If you import the Internet Explorer or Microsoft DHTML ActiveX controls into Delphi or C++Builder, the control does not respond to certain keystrokes (Tab, in the case of IE, and delete, in the case of DHTML).
The reasons for the problem: The problem occurs because of a combination of peculiar behavior on the part of the controls, and peculiar behavior on the part of the Delphi and C++Builder IDEs. 
How the control is strange: The control believes that the keys in question should be treated as accelerators and wishes to handle them as such, rather than as Windows key down messages. 
What Delphi and C++Builder are doing: TApplication catches all windows messages. As part of the processing of such messages, it allows windows contained within the application to respond to key messages themselves rather than use the default processing To allow VCL-wrapped windows to distinguish between messages passed to them by TApplication in the middle of message processing from messages which have been passed on after TApplication has given up on processing them, the VCL modifies the message and passes the modified message to the window via SendMessage. (The relevant code is in forms.pas: Application.IsKeyMsg).When an ActiveX control is imported, it is wrapped by a descendant class of TOleControl (olectrls.pas). TOleControl hooks the window procedure; the hook catches the messages thrown by Application, modifies them back to their original state, and calls IOleInPlaceActiveObject.TranslateAccelerator, allowing the code to respond to accelerators.With most controls, this works. Unfortunately, the dynamic nature of the IE controls causes a problem, in that the window which is recieving the messages is either a child or a grandchild of the window which has been subclassed. Their window procedures don't understand the messages they are recieving from TApplication (and so ignore them); the accelerators never get translated; and the message that was intended by pressing tab or delete gets lost. How Delphi 5 solves the problem: The problem was solved in Delphi 5 by modifying the behavior of TApplication.IsKeyMsg to walk the parent chain of a non-VCL window until a VCL window is encountered, and then throw the modified message at it. This allows the TOleControl wrapper to ask the control to translate the accelerator. 
How you can solve the problem in C++Builder 4 or Delphi 4: The problem can be solved on an ad-hoc basis by dynamically subclassing the window procedures of the controls in question. (An earlier fix to the problem in Delphi5 involved modifying TOleControl to subclass the windows of all children created by the embedded Ole control; the problem with this approach was that, while the controls notify you verify their event interfaces when their children are destroyed and recreated, they do not pass such a notification through a standard Ole interface, so it was difficult to find a general-case mechanism for the container to know when to hook the child windows).
When to hook the window procedure: In the case of the WebBrowser control, a new child window is created whenever a new web page is accessed (and so the window procedure should be hooked in the OnNavigatComplete2 or the OnNewWindow2 event handlers). In the case of the DHTML control, there is usually only one child window whose creation is delayed until well past the creation of the control itself; manually hooking its window procedure in the form's OnActivate method should work).
How to get the handle for the window: The handle for the window that you want to subclass can be retrieved in one of two ways (one is generic, the other is not). You can get the handle for the window which was subclassed at the creation of the control by accessing the Handle method of the TOleControl descendant, using code that looks more or less like the following pascal code:
{these are members of Form1} WebBrowser1 : TWebBrowser;
FFrameWndProcInstance: Pointer;
FFrameHwnd : HWND;
FFrameDefWndProc : Pointer;function Form1.SubClassFrame;
var
Child : HWND;
begin
  Child := GetWindow(WebBrowser1.Handle, GW_CHILD);
  if Child <> 0 then
  begin
     Child := GetWindow(Child, GW_CHILD);
     if (Child <>0) and (Child <> FFrameHwnd) then
     { don't bother subclassing if you've already got it subclassed }  
     begin
         if FFrameWndProcInstance <> nil then
             UnSubClassFrame;
         FFrameHwnd := Child;
         FFrameWndProcInstance := MakeObjectInstance(FrameWndProc);
         FFrameDefWndProc := Pointer(GetWindowLong(FFrameHwnd, GWL_WNDPROC));
         SetWindowLong(FFrameHwnd, GWL_WNDPROC, LongInt(FFrameWndProcInstance));
    end;
  end;
end;function Form1.UnSubClassFrame;
begin
   if (FFrameWndProcInstance <> nil and (FDefFrameWndProc)  <> nil and (FFrameHwnd <> 0) then
   begin
       SetWindowLong(FFrameHwnd, GWL_WNDPROC, LongInt(FDefFrameWndProc));
       FFrameWndProcInstance := nil;
       FFrameHwnd := nil;
       { require the caller to set FDefFrameWndProc to nil so you can unsubclass and then call the default
          proc when responding to WM_DESTROY events }
   end;
end;
Alternately, you can ask the control to return to you an IOleInPlaceActiveObject interface, and query it for its window handle: 
function Form1.SubClassFrame;
var
ActiveObject : IOleInPlaceActiveObject;
Child : HWND;
begin
   if (WebBrowser1.DefaultDispatch.QueryInterface(IOleInPlaceActiveObject, ActiveObject) = S_OK) then
   try 
     Child := ActiveObject.GetWindow;
     if (Child <> nil) and (Child <> FFrameHwnd) then
     begin
         if FFrameWndProcInstance <> nil then
             UnSubClassFrame;
         FFrameHwnd := Child;
         FFrameWndProcInstance := MakeObjectInstance(FrameWndProc);
         FFrameDefWndProc := Pointer(GetWindowLong(FFrameHwnd, GWL_WNDPROC));
         SetWindowLong(FFrameHwnd, GWL_WNDPROC, LongInt(FFrameWndProcInstance));
    end;
  finally
    ActiveObject.Release;
  end;
end;function Form1.UnSubClassFrame;
var
ActiveObject: IOleInPlaceActiveObject;
begin
  if (WebBrowser1.DefaultDispatch.QueryInterface(IOleInPlaceActiveObject, ActiveObject) = S_OK) then
    try
    if (FFrameWndProcInstance <> nil and (FDefFrameWndProc)  <> nil and (FFrameHwnd <> 0) then
    begin
       SetWindowLong(FFrameHwnd, GWL_WNDPROC, LongInt(FDefFrameWndProc));
       FFrameWndProcInstance := nil;
       FFrameHwnd := nil;
       { require the caller to set FDefFrameWndProc to nil so you can unsubclass and then call the default
          proc when responding to WM_DESTROY events }
   end;
   finally
     ActiveObject.Release;
   end;
end;
What the window procedure should do. The window procedure hook you install should, at a minimum, pass the key messages on to the ole control's accelerator treanslator, properly unhook itself in response to a WM_DESTROY message,and pass all other messages along to the default window procedure, as per the following example: 
procedure Form1.FrameWndProc(var Message: TMessage);
var
  WinMsg: TMsg;
  ActiveObject : IOleInPlaceActiveObject;
begin
  if (Message.Msg >= CN_BASE + WM_KEYFIRST) 
     and (Message.Msg <= CN_BASE + WM_KEYLAST) then
  begin
      WinMsg.HWnd := WebBrowser1.Handle;
      WinMsg.Message := Message.Msg - CN_BASE;
      WinMsg.WParam := Message.WParam;
      WinMsg.LParam := Message.LParam;
      WinMsg.Time := GetMessagTime;
      WinMsg.Pt.X := $115DE1F1;
      WinMsg.Pt.Y= $115DE1F1;
      if (WebBrowser1.DefaultDispatch.QueryInterface(IOleInPlaceActiveObject, ActiveObject) = S_OK) then
      try
        if ActiveObject.TranslateAccelerator(WinMsg) = S_OK then
        begin
           Message.Result := 1;
           WinMsg.Pt.X := $5ACC355;
        end;
     finally
        ActiveObject.Release;
     end;
     if WinMsg.Pt.X = $5ACC355 then
         Exit;
  end;
  with Message do
  begin
     case Msg of
        WM_DESTROY:
         begin
            UnSubClassFrame;
            CallWindowProc(FDefFrameWndProc, FFrameHwnd, Message.Msg, Message.WParam, Message.LParam);
             FDefFrameWndProc := nil;
         end;
    end;
    Result := CallWindowProc(FDefFrameWndProc, FFrameHwnd, Msg, WParam, LParam);
  end;
end;

解决方案 »

  1.   

    在tWebBrowser中,如果navigate到的页面上有textarea框,我发现就是不能在里面用回车换行,一定要用alt+13才行。另外不能ctrl+c。
    是不是该控件的bug,还是哪里设置不对? 
    回复贴子: 
     回复人:Daiver(Daiver) (2000-11-30 19:09:00)  得0分 
    是Delphi的BUG来的,在VB中没有这种情况(老BILl)还是对自己好啊!)在程序员大本营中有解决的办法,是DELphi中的技巧2,我只是看了,没有去试过,你可以去试试 啊,有什么结果记得E_mail给我([email protected])  
     回复人:easypaper() (2000-11-30 19:24:00)  得0分 
    关于复制的问题,不是DELPHI的BUG,是因为你的程序里面少了一点东西。
    在初始化函数里面,增加
    OleInitialize(nil);
    在推出程序之前,增加:
    OleUninitialize();
    解可以解决这个问题。至于回车的问题,可以算是vcl的bug(其实是封装机制问题,vcl将回车给吃掉了)。
     
     回复人:menxin() (2000-12-4 20:31:00)  得0分 
    没办法彻底解决,你应该用www.intelligo.com/iedelphi 上的Embedded.  
     回复人:pengpenghu(碰碰和) (2000-12-9 10:10:00)  得0分 
    我无法打开这个网站www.intelligo.com/iedelphi ,你有没有现成的?  
     回复人:Nicrosoft(奈软) (2000-12-9 10:27:00)  得0分 
    这个控件不是vcl的,是IE自带的ocx,所以不可能是delphi的bug  
     回复人:alaclp(陈石) (2001-2-22 8:21:00)  得0分 
    在主窗体中添加TappliationEvent,在其事件中写如下代码,
    //uses Activex
    procedure TMainFrm.ApplicationEvents1Message(var Msg: tagMSG;
      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 }
    var
    WebBrowser1: TWebBrowser;
    begin
    if Pages.PageCount = 0 then Exit;
    WebBrowser1 := GetCurrentWeb;
    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(WebBrowser1.Handle, hWnd) then
          { handles all browser related messages }
          begin
            with WebBrowser1.Application as IOleInPlaceActiveObject do
              Handled := TranslateAccelerator(Msg) = S_OK;
            if not Handled then
            begin
              Handled := True;
              TranslateMessage(Msg);
              DispatchMessage(Msg);
            end;
          end;
        except end;
    end; // IEMessageHandler另外,easypaper所说的oleini..., oleunini...,不只写在那里,有何作用?
     
     回复人:aton() (2001-2-28 19:33:00)  得0分 
    先要使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