C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[14721] Re:[질문]WebBrowser로 강제로 포커스 주기
I.MagicPotato [magicptt] 2196 읽음    2002-01-19 01:11

대충 읽어보니.. 원하시는 내용과, 대충 해결했을때 발생할 문제까지 해결한듯한..
문서가 있습니다.

대충 보니 target이나 shift+click에 대한 새창에 대한 내용도 있는거 같네요..

출처는 볼랜드 커뮤니티 입니다.  전 영어 못함 -_-;



Lost keys in the IE5 ActiveX controls - by


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;



김은령 님이 쓰신 글 :
: IE 를 Import하여 사용하고 있습니다.
:
: Form에 다른 콤포넌트에 현재 Focus가 있는 상태에서
:  내부적으로 강제로 Browser 콤퍼넌트로 Focus를 이동할려고 하는데 잘 안됩니다.
:
: 좋은 의견 부탁합니다.
:
:
:

+ -

관련 글 리스트
14721 Re:[질문]WebBrowser로 강제로 포커스 주기 I.MagicPotato 2196 2002/01/19
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.