[How To] Implement Tabstop for ActiveX > Shell.Explorer

Helpful script writing tricks and HowTo's
User avatar
SKAN
Posts: 333
Joined: 29 Sep 2013, 16:58

[How To] Implement Tabstop for ActiveX > Shell.Explorer

04 Dec 2013, 18:17

Greetings :)

When we create a GUI with Edit, Button and ActiveX Shell.Explorer control, the TAB navigation is restricted to Edit & Button controls. We have to manually mouse click the WebControl before we can perform any key strokes on it

The following is ClassNN list for a above like GUI:

Code: Select all

Edit1  ( WS_TABSTOP )
Button1  ( WS_TABSTOP )
AtlAxWin1 
  Shell Embedding1  ( WS_TABSTOP )
The "Shell Embedding" is styled WS_TABSTOP, but being child of "AtlAxWin", it is unable to participate in overall GUI TAB navigation.
Any attempt to style AtlAxWin with WS_TABSTOP will result in appcrash.

The correct way of enabling "Shell Embedding" to participate in TAB navigation is to style its parent AtlAxWin with WS_EX_CONTROLPARENT
WS_EX_CONTROLPARENT 0x00010000L ( E0x10000 ) MSDN: goo.gl/8TsQaK
The window itself contains child windows that should take part in dialog box navigation.
If this style is specified, the dialog manager recurses into children of this window when
performing navigation operations such as handling the TAB key, an arrow key, or a keyboard
mnemonic.
Once an URL is loaded into Shell.Explorer object, additional child controls are created and ClassNN list will be:

Code: Select all

Edit1  ( WS_TABSTOP )
Button1  ( WS_TABSTOP )
AtlAxWin1
  Shell Embedding1
    Shell DocObject View1  ( WS_TABSTOP )
      Internet Explorer_Server1
From above we see that Tabstop now belongs to "Shell DocObject View".
Attempts to transfer Tabstop to "Internet Explorer_Server1" does not enable it to receive focus.
Also, I checked IE 10. the above lying "Internet Explorer_Server1" is not styled WS_TABSTOP

The workaround I am suggesting/using is to detect when "Shell DocObject View" receives the focus, and transfer it immediately ( with ControlFocus ) to it child "Internet Explorer_Server"

We already have to handle WM_KEYDOWN & WM_KEYUP message for calling IOleInPlaceActiveObject::TranslateAccelerator method.
Placing the following code along, inside the OnMessage handler should be sufficient.

Code: Select all

  If ( ClassName = "Shell DocObject View" && wParam = 0x09 ) {
    WinGet, hIES, ControlListHwnd, ahk_id %hWnd% ; List children of 'Shell DocObject View'
    ControlFocus,, ahk_id %hIES%
    Return 0
  }
The above snippet detects if "Shell DocObject View" receives TAB key and sets the focus to its only child, which will be "Internet Explorer_Server"

That should be it, and should serve most of the needs... but,
If you open a webpage like http://ip.ahk4.me/ in any browser, you can immediately use Ctrl+A to select all text.
But the suggested implementation is not enough. The control will have focus but not any elements to focus on. Depending on needs one could use ControlClick instead of ControlFocus, but it will clear any selection already been made in the web content.

The correct way to handle these, would be to handle it in DocumentComplete() event. I do know more on the subject but it will be like testing all frames and whether it has elements (enabled & visible) and set focus accordingly. If it does not have any, as the case with http://ip.ahk4.me/, one may use WB.Document.Body.Focus() to ready the document for Select All (Ctrl+A).

Example code ( adapted from Doc and rest of forum ):

Code: Select all

/*
     __    __  __          __ __       __    __                 _       __                   
    / /_  / /_/ /_____  _ / // /____ _/ /_  / /________________(_)___  / /_ ____  _______
   / __ \/ __/ __/ __ \(_) // // __ '/ __ \/ //_/ ___/ ___/ __/ / __ \/ __// __ \/ __/ _ \
  / / / / /_/ /_/ /_/ / / // // /_/ / / / / ,< (__  ) /__/ / / / /_/ / /__/ /_/ / / / // / 
 /_/ /_/\__/\__/ .___(_) // / \__,_/_/ /_/_/|_/____/\___/_/ /_/ .___/\__(_)____/_/  \__ /  
              /_/     /_//_/                                 /_/                   (___/   
              
  Subject :  [How To] Implement Tabstop for ActiveX > Shell.Explorer
  Author  :  SKAN, for the technique described on topic.
  Topic   :  http://ahkscript.org/boards/viewtopic.php?f=7&t=879
  
*/

#NoEnv  
#SingleInstance, Force
SetWorkingDir %A_ScriptDir% 


Gui Add, Edit, w930 r1 vURL, http://www.google.com/
Gui Add, Button, x+6 yp w44 Default, Go
Gui Add, ActiveX, xm w980 h640 E0x10000 vWB, Shell.Explorer
ComObjConnect(WB, WB_events)

IOleInPlaceActiveObject_Interface := "{00000117-0000-0000-C000-000000000046}"
pipa := ComObjQuery( WB, IOleInPlaceActiveObject_Interface )
TranslateAccelerator := NumGet( NumGet( pipa+0 ) + 20 )
    
OnMessage( 0x0100, "WM_KeyPress" ) ; WM_KEYDOWN 
OnMessage( 0x0101, "WM_KeyPress" ) ; WM_KEYUP   
OnExit, GuiClose

Gui Show


ButtonGo:
  Gui Submit, NoHide
  WB.Navigate(URL)
Return


class WB_events
{
    NavigateComplete2(wb, NewURL)
    {
        GuiControl,, URL, %NewURL%  ; Update the URL edit control.
    }

    DocumentComplete( WB, NewURL ) 
    {
        ; check all frames and elements for input focus
        ; WB.Document.Body.Focus()
    }     
}


GuiClose:
  ObjRelease( pipa )
  OnExit
  ExitApp
Return  



WM_KeyPress( wParam, lParam, nMsg, hWnd ) {

Global WB, pipa, TranslateAccelerator
Static Vars := "hWnd | nMsg | wParam | lParam | A_EventInfo | A_GuiX | A_GuiY" 

  WinGetClass, ClassName, ahk_id %hWnd%

  If ( ClassName = "Shell DocObject View" && wParam = 0x09 ) {
    WinGet, hIES, ControlListHwnd, ahk_id %hWnd% ; Find child of 'Shell DocObject View'
    ControlFocus,, ahk_id %hIES%
    Return 0
  }

  If ( ClassName = "Internet Explorer_Server" ) {

    VarSetCapacity( MSG, 28, 0 )                   ; MSG STructure    http://goo.gl/4bHD9Z
    Loop, Parse, Vars, |, %A_Space%
      NumPut( %A_LoopField%, MSG, ( A_Index-1 ) * 4 )

    Loop 2  ; IOleInPlaceActiveObject::TranslateAccelerator method    http://goo.gl/XkGZYt
      r := DllCall( TranslateAccelerator, UInt,pipa, UInt,&MSG )
    Until wParam != 9 || WB.document.activeElement != ""

    IfEqual, R, 0, Return, 0         ; S_OK: the message was translated to an accelerator.

  }
}
Experts may please edit this topic as they seem fit.
User avatar
tank
Posts: 2412
Joined: 28 Sep 2013, 22:15
Facebook: charlie.simmons.7334
Google: ttnnkkrr
GitHub: ttnnkkrr
Location: Louisville KY
Contact:

Re: [How To] Implement Tabstop for ActiveX > Shell.Explorer

04 Dec 2013, 18:40

Love the tut and I learned from it. But I love the ascii art
We are troubled on every side‚ yet not distressed; we are perplexed‚
but not in despair; Persecuted‚ but not forsaken; cast down‚ but not destroyed;
https://www.facebook.com/ahkscript.org
If you have forum suggestions please submit a pull request
User avatar
SKAN
Posts: 333
Joined: 29 Sep 2013, 16:58

Re: [How To] Implement Tabstop for ActiveX > Shell.Explorer

04 Dec 2013, 19:11

tank wrote:I love the ascii art
Me too.. and looks even better when displayed non-italicized

Code: Select all

Thanks to tank for
     __    __  __          __ __       __    __                 _       __                   
    / /_  / /_/ /_____  _ / // /____ _/ /_  / /________________(_)___  / /_ ____  _______
   / __ \/ __/ __/ __ \(_) // // __ '/ __ \/ //_/ ___/ ___/ __/ / __ \/ __// __ \/ __/ _ \
  / / / / /_/ /_/ /_/ / / // // /_/ / / / / ,< (__  ) /__/ / / / /_/ / /__/ /_/ / / / // / 
 /_/ /_/\__/\__/ .___(_) // / \__,_/_/ /_/_/|_/____/\___/_/ /_/ .___/\__(_)____/_/  \__ /  
              /_/     /_//_/                                 /_/                   (___/   

"the freedom site"
:)
tmplinshi
Posts: 1281
Joined: 01 Oct 2013, 14:57

Re: [How To] Implement Tabstop for ActiveX > Shell.Explorer

03 Jan 2014, 12:46

Thank you, very helpful.
User avatar
jethrow
Posts: 187
Joined: 30 Sep 2013, 19:52
Location: Iowa

Re: [How To] Implement Tabstop for ActiveX > Shell.Explorer

25 Feb 2014, 02:14

Please confirm - the following is all that would need updated to make this 32bit/64bit compatible:

Code: Select all

;~ Original:
TranslateAccelerator := NumGet( NumGet( pipa+0 ) + 20 )

;~ Updated:
TranslateAccelerator := NumGet( NumGet( pipa+0 ) + 5*A_PtrSize )

Code: Select all

;~ Original
    VarSetCapacity( MSG, 28, 0 )                   ; MSG STructure    http://goo.gl/4bHD9Z
    Loop, Parse, Vars, |, %A_Space%
      NumPut( %A_LoopField%, MSG, ( A_Index-1 ) * 4 )

;~ Updated:
    VarSetCapacity( MSG, 48, 0 )                   ; MSG STructure    http://goo.gl/4bHD9Z
    Loop, Parse, Vars, |, %A_Space%
      NumPut( %A_LoopField%, MSG, ( A_Index-1 ) * A_PtrSize )
jballi
Posts: 521
Joined: 29 Sep 2013, 17:34

Re: [How To] Implement Tabstop for ActiveX > Shell.Explorer

24 Oct 2015, 00:14

Old thread but this is as good of a place as any to post this.

Based upon the research posted in this and other threads, I created a stand-alone method of giving input focus to a web browser control. Results appear to be consistent after the web browser control has been rendered, i.e. the parent window is showing, and after the web browser control is loaded, i.e. the ReadyState method returns a READYSTATE_LOADED value. Before that... not so much.

Code: Select all

;-- The web browser control is comprised of multiple windows.  The
;   structure looks something like this (courtesy paulwarr circa 2008):
;
;       Web Browser control, aka AtlAxWin
;       --> Shell Embedding
;       ----> Shell DocObject View
;       ------> Internet Explorer_Server
;
;   The objective is to identify and set input focus on the
;   "Internet Explorer_Server" window.
;
;   Credit to SKAN for the research and some code
;
GW_CHILD:=5
hChild:=hWB  ;-- hWB is the handle to the web browser control
Loop 3
   hChild:=DllCall("GetWindow","Ptr",hChild,"UInt",GW_CHILD)

;-- Set focus
ControlFocus,,ahk_id %hChild%
I haven't done much testing but it's working so far. If you find any problems or if you have any improvements, post it!

Return to “Tutorials”

Who is online

Users browsing this forum: Google [Bot], JnLlnd and 8 guests