By
jackchen
OK,I've made my "Chrome_KeepLastTab.au3" work.This script adds some features to Chrome browser:
1. Double click on a tab to close the tab.
2. Keep last tab:This script monitors your mouse clicks and hotkeys,if you are about to close the last tab within Chrome(click close button on the last tab, middle click/double click on the last tab or press Ctrl + w or Ctrl + {F4}), a new tab will be open and then the old tab be closed.
#include <WindowsConstants.au3>
#include <WinAPI.au3>
#include "CUIAutomation2.au3"
#AutoIt3Wrapper_UseX64=Y ;Should be used for stuff like tagpoint having right struct etc. when running on a 64 bits os
ConsoleWrite("@OSArch: " & @OSArch & ", @AutoItX64: " & @AutoItX64 & @CRLF)
Global $DoubleClickTime = 500
Global $UIA_oUIAutomation ; The main library core CUI automation reference
Global $hMouseEvent, $hMouseHook
Global $aMouseEvent[2]
Global $KeepLastTab = True ; settings from ini file
If $KeepLastTab Then
;The main object with acces to the windows automation api 3.0
$UIA_oUIAutomation = ObjCreateInterface($sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation)
If IsObj($UIA_oUIAutomation) Then
HotKeySet("^w", "Hotkey_Event") ; Ctrl + w to close tab
HotKeySet("^{F4}", "Hotkey_Event") ; Ctrl + {F4} to close tab
$DoubleClickTime = DllCall("user32.dll", "uint", "GetDoubleClickTime")[0]
OnAutoItExitRegister("UnhookMouse")
; Register mouse events callback
$hMouseEvent = DllCallbackRegister("Mouse_Event", "int", "int;ptr;ptr")
$hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0))
EndIf
EndIf
While 1
Sleep(100)
WEnd
ToolTip("")
; https://www.autoitscript.com/forum/topic/103362-monitoring-mouse-events/
Func Mouse_Event($nCode, $wParam, $lParam)
Local $info, $mouseData, $time, $timeDiff
If $nCode < 0 Then
Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing
EndIf
$tPoint = _WinAPI_GetMousePos()
$hWnd = _WinAPI_WindowFromPoint($tPoint)
; if mouse is on the widget window(class: Chrome_RenderWidgetHostHWND),
; use $hWnd = _WinAPI_GetParent($hWnd) to get the parent Chrome window
If Not StringInStr(_WinAPI_GetClassName($hWnd), "Chrome_WidgetWin_") Then
; Chrome_WidgetWin_1: Chrome window
; Chrome_WidgetWin_2: Chrome menu
; ignore non Chrome window
Return _WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam)
EndIf
ToolTip($hWnd & " - " & _WinAPI_GetClassName($hWnd))
;$tagPOINT = "struct;long X;long Y;endstruct"
Local Const $MSLLHOOKSTRUCT = $tagPOINT & ";dword mouseData;dword flags;dword time;ulong_ptr dwExtraInfo"
$info = DllStructCreate($MSLLHOOKSTRUCT, $lParam)
$mouseData = DllStructGetData($info, 3)
$time = DllStructGetData($info, 5)
$timeDiff = $time - $aMouseEvent[1]
Local $block
Switch $wParam
Case $WM_LBUTTONUP
$aMouseEvent[1] = $time
If $aMouseEvent[0] = "LClick" And ($timeDiff) < $DoubleClickTime Then
$aMouseEvent[0] = "LDClick"
Else
$aMouseEvent[0] = "LClick"
EndIf
$block = KeepLastTab($hWnd, $aMouseEvent[0])
Case $WM_MBUTTONUP
$aMouseEvent[1] = $time
If $aMouseEvent[0] = "MClick" And ($timeDiff) < $DoubleClickTime Then
$aMouseEvent[0] = "MDClick"
Else
$aMouseEvent[0] = "MClick"
$block = KeepLastTab($hWnd, $aMouseEvent[0])
EndIf
EndSwitch
If Not $block Then
_WinAPI_CallNextHookEx($hMouseHook, $nCode, $wParam, $lParam) ; Continue processing
EndIf
EndFunc ;==>Mouse_Event
Func UnhookMouse()
_WinAPI_UnhookWindowsHookEx($hMouseHook)
$hMouseHook = 0
DllCallbackFree($hMouseEvent)
$hMouseEvent = 0
EndFunc ;==>UnhookMouse
Func Hotkey_Event()
Local $block
Local $hWnd = WinGetHandle(WinGetTitle("[ACTIVE]"))
If StringInStr(_WinAPI_GetClassName($hWnd), "Chrome_WidgetWin_1") Then ; Chrome_WidgetWin_1, Chrome window
$block = KeepLastTab($hWnd, "Hotkey")
EndIf
If Not $block Then
HotKeySet("^w")
Send(@HotKeyPressed)
HotKeySet("^w", "Hotkey_Event")
EndIf
EndFunc
Func KeepLastTab($hWnd, $action = "LClick")
; Possible $action value: LClick, LDClick, MClick, Hotkey
ConsoleWrite(@CRLF & $action & " on a Chrome window: " & $hWnd & @CRLF)
Local $aMousePos = MouseGetPos()
Local $pChrome, $oChrome
$UIA_oUIAutomation.ElementFromHandle($hWnd, $pChrome) ; Window
$oChrome = ObjCreateInterface($pChrome, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
If Not IsObj($oChrome) Then
ConsoleWrite("Failed to get Chrome object from hWnd." & @CRLF)
Return
EndIf
;$UIA_ControlTypePropertyId = 30003
$oChromeTabs = UIA_getFirstElement($oChrome, $UIA_ControlTypePropertyId, $UIA_TabControlTypeId, $treescope_subtree)
If Not IsObj($oChromeTabs) Then
ConsoleWrite("Failed to get Chrome tab bar object." & @CRLF)
Return
EndIf
Local $t
$oChromeTabs.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t)
If UBound($t) < 4 Then Return
If $action <> "Hotkey" And ($aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+$t[1]) Then
; Mouse not on tabs bar
ConsoleWrite("Mouse is not on the tab bar. Ignore and return..." & @CRLF)
Return
EndIf
Local $pTrueCondition, $pElements, $iLength, $oAutomationElementArray
$UIA_oUIAutomation.CreateTrueCondition($pTrueCondition)
$oCondition = ObjCreateInterface($pTrueCondition, $sIID_IUIAutomationCondition, $dtagIUIAutomationCondition)
If Not IsObj($oCondition) Then Return
$oChromeTabs.FindAll($treescope_children, $oCondition, $pElements)
$oAutomationElementArray = ObjCreateInterface($pElements, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray)
If Not IsObj($oAutomationElementArray) Then
ConsoleWrite("Failed to find all Chrome tabs. " & @CRLF)
Return
EndIf
$oAutomationElementArray.Length($iLength)
Local $UIA_pUIElement, $oTab2
Local $iTabs = $iLength - 1
If $iTabs > 1 Then ; more than one tab
If $action = "LDClick" Then
ConsoleWrite("There are " & $iTabs & " tabs within Chrome window. " & @CRLF)
For $i = 1 To $iTabs
$oAutomationElementArray.GetElement($i, $UIA_pUIElement)
$oTab2 = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
If Not IsObj($oTab2) Then ContinueLoop
$oTab2.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t)
If UBound($t) < 4 Then ContinueLoop
If $aMousePos[0] >= $t[0] And $aMousePos[0] <= $t[2]+$t[0] And $aMousePos[1] >= $t[1] And $aMousePos[1] <= $t[3]+$t[1] Then
ConsoleWrite("You double clicked on one of " & $iTabs & " tabs. Close the tab and return..." & @CRLF)
HotKeySet("^w")
Send("^w")
HotKeySet("^w", "Hotkey_Event")
Return True
EndIf
Next
EndIf
Return
EndIf
ConsoleWrite("There is ONLY one tab within Chrome window. " & @CRLF)
$oAutomationElementArray.GetElement(1, $UIA_pUIElement)
$oTab = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
If Not IsObj($oTab) Then
ConsoleWrite("Failed to get the last tab object." & @CRLF)
Return
EndIf
Local $rtTab
$oTab.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $rtTab)
If UBound($rtTab) < 4 Then Return
If $action <> "Hotkey" And ($aMousePos[0] < $rtTab[0] Or $aMousePos[0] > $rtTab[2]+$rtTab[0] Or $aMousePos[1] < $rtTab[1] Or $aMousePos[1] > $rtTab[3]+$rtTab[1]) Then
; Mouse not on the last tab
ConsoleWrite("Mouse is not on the last tab. Ignore and return..." & @CRLF)
Return
EndIf
If $action = "LClick" Then
$oTabClose = UIA_getFirstElement($oTab, $UIA_ControlTypePropertyId, $UIA_ButtonControlTypeId, $treescope_subtree)
If Not IsObj($oTabClose) Then
ConsoleWrite("Failed to get the last tab close object." & @CRLF)
Return
EndIf
$oTabClose.GetCurrentPropertyValue($UIA_BoundingRectanglePropertyId, $t)
If UBound($t) < 4 Then Return
If $aMousePos[0] < $t[0] Or $aMousePos[0] > $t[2]+$t[0] Or $aMousePos[1] < $t[1] Or $aMousePos[1] > $t[3]+$t[1] Then
; Mouse not on the tab close button
Return
EndIf
EndIf
; open a new tab within chrome
ConsoleWrite("The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing." & @CRLF)
Send("^t")
ConsoleWrite("A new tab created!" & @CRLF)
Sleep(100)
ConsoleWrite("Close the old tab and return..." & @CRLF)
;~ Local $pPattern
;~ $oTabClose.GetCurrentPattern($UIA_InvokePatternId, $pPattern)
;~ $oPattern = ObjCreateInterface($pPattern, $sIID_IUIAutomationInvokePattern, $dtagIUIAutomationInvokePattern)
;~ If IsObj($oPattern) Then
;~ ConsoleWrite("Invoke to close the tab..." & @CRLF)
;~ $oTabClose.SetFocus()
;~ $oPattern.Invoke()
;~ EndIf
_WinAPI_UnhookWindowsHookEx($hMouseHook)
$aMousePos = MouseGetPos()
If $aMousePos[0] >= $rtTab[0] And $aMousePos[0] <= $rtTab[2]+$rtTab[0] And $aMousePos[1] >= $rtTab[1] And $aMousePos[1] <= $rtTab[3]+$rtTab[1] Then
MouseClick("middle", $aMousePos[0], $aMousePos[1], 1, 0)
Else
MouseClick("middle", $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2, 1, 0) ; close the tab
MouseMove($aMousePos[0], $aMousePos[1], 0) ; move mouse back to previous position
EndIf
;~ _WinAPI_Mouse_Event($MOUSEEVENTF_MIDDLEDOWN, $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2)
;~ Sleep(100)
;~ _WinAPI_Mouse_Event($MOUSEEVENTF_MIDDLEUP, $rtTab[0]+$rtTab[2]/2, $rtTab[1]+$rtTab[3]/2)
$hMouseHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hMouseEvent), _WinAPI_GetModuleHandle(0))
Return True ; to block mouse click/hot key
EndFunc
Func UIA_getFirstElement($obj, $propertyID, $tval, $treeScope)
Local $pCondition, $oCondition
$UIA_oUIAutomation.CreatePropertyCondition($propertyID, $tval, $pCondition)
$oCondition = ObjCreateInterface($pCondition, $sIID_IUIAutomationPropertyCondition, $dtagIUIAutomationPropertyCondition)
Local $UIA_oUIElement, $UIA_pUIElement
$t = $obj.Findfirst($treeScope, $oCondition, $UIA_pUIElement)
$UIA_oUIElement = ObjCreateInterface($UIA_pUIElement, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
If IsObj($UIA_oUIElement) Then Return $UIA_oUIElement
Return SetError(1, 0, "")
EndFunc ;==>UIA_getFirstElement
My question is related to #AutoIt3Wrapper_UseX64 when run this script on 64-bit Win 7.
No mater I set #AutoIt3Wrapper_UseX64=Y or #AutoIt3Wrapper_UseX64=N, this script works very well on hotkey event, while mouse clicks sometimes works if #AutoIt3Wrapper_UseX64=N and sometimes works on Y. Can some one test this and finger out what's wrong?
Info from SciTe if #AutoIt3Wrapper_UseX64=Y :
@OSArch: X64, @AutoItX64: 1
Hotkey on a Chrome window: 0x0000000000140330
There is ONLY one tab within Chrome window.
The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing.
A new tab created!
Close the old tab and return...
LClick on a Chrome window: 0x0000000000140330
There is ONLY one tab within Chrome window.
The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing.
A new tab created!
Close the old tab and return...
Info if #AutoIt3Wrapper_UseX64=N or comment out this line:
@OSArch: X64, @AutoItX64: 0
Hotkey on a Chrome window: 0x00140330
There is ONLY one tab within Chrome window.
The last tab is about to be closed, so we have to open a new tab to prevent Chrome window from closing.
A new tab created!
Close the old tab and return...
LClick on a Chrome window: 0x00140330
Failed to get Chrome tab bar object.Hotkey events and mouse click events share the same function KeepLastTab($hWnd, $action = "LClick"),Why this function triggered by hotkey works on both 32-bit and 64-bit while that triggered by mouse events failed on 32-bit autoit?