Jump to content
Sign in to follow this  
sulfurious

Win Explorer -- get specific column SysListView32

Recommended Posts

sulfurious

Hi.

Been awhile since I have been here. I have searched and found many snippets, but am stumped.

I have a small script that is able to detect when a double/single click happens in Windows Explorer SysListView32 control. But I am looking to find how to see which column of the SysListView to monitor the click action for.

I had seen this snippet in how to monitor for a double click

#include <GuiConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Constants.au3>
#include <WinAPI.au3>

Global Const $WM_LBUTTONDBLCLK = 0x203

$hGUI = GUICreate("Test GUI", 300, 200)

$edit = GUICtrlCreateEdit("", 10, 10, 280, 180)

$wProcHandle = DllCallbackRegister("_WindowProc", "ptr", "hwnd;uint;wparam;lparam")

$wProcOld = _WinAPI_SetWindowLong(GUICtrlGetHandle($edit), $GWL_WNDPROC, DllCallbackGetPtr($wProcHandle))

GUISetState()

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

GUIDelete($hGui)
DllCallbackFree($wProcHandle)

Func _WindowProc($hWnd, $Msg, $wParam, $lParam)
    Switch $hWnd
        Case GUICtrlGetHandle($edit)
            Switch $Msg
                Case $WM_LBUTTONDBLCLK
                    ConsoleWrite("-> Left mouse double click" & @LF)
                    Return 0
            EndSwitch
    EndSwitch

    Local $aRet = DllCall("user32.dll", "int", "CallWindowProc", "ptr", $wProcOld, _
                          "hwnd", $hWnd, "uint", $Msg, "wparam", $wParam, "lparam", $lParam)
    Return $aRet[0]
EndFunc

I like how that code is small and can watch for a dblclick, but I don't know if it can nor understand how to apply that to the windows explorer handle/syslist handle, it seems locked to only the controls in the GUI if I understand it correctly.

All of this is compliments of others here, and are good starting points. I was hoping not to have a mousehook, more like a callback from explorer itself. I haven't used much of this sort of thing, only hacking away at it when I need to. This is the piece I am playing with currently;

#include <WinAPI.au3>
#include <GuiListView.au3>
Opt('MustDeclareVars', 1)

HotKeySet('{ESC}', '_EXIT')

Global Const $tagMSLLHOOKSTRUCT = 'int X;int Y;uint;uint;uint;ulong_ptr'
Global Const $WM_MOUSEMOVE      = 0x0200
Global Const $WM_LBUTTONDOWN             = 0x0201
Global Const $WM_LBUTTONUP                  = 0x0202
Global Const $WM_LBUTTONDBLCLK     = 0x0203
Global Const $WM_RBUTTONDOWN             = 0x0204
Global Const $WM_RBUTTONUP                  = 0x0205
Global Const $WM_RBUTTONDBLCLK              = 0x0206
Global Const $SHELLDLL_DefView        = "SHELLDLL_DefView"

Dim $hMHook, $pMHook, $hHook
Dim $iDiff = TimerInit()

$hMHook = DllCallbackRegister('LowLevelMouseProc', 'long', 'int;wparam;lparam')
$pMHook = DllCallbackGetPtr($hMHook)

$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, $pMHook, _WinAPI_GetModuleHandle(0))

While 1
    Sleep(50)
WEnd

Func _EXIT()
    Exit
EndFunc

Func OnAutoItExit()
    DllCallbackFree($hMHook)
    _WinAPI_UnhookWindowsHookEx($hHook)
EndFunc

Func LowLevelMouseProc($iCode, $iwParam, $ilParam)
    Local $tMSLLHOOKSTRUCT = DllStructCreate($tagMSLLHOOKSTRUCT, $ilParam)
    Local $hWnd

    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $iwParam, $ilParam)

    If $iwParam = $WM_LBUTTONDOWN Then

        If TimerDiff($iDiff) < 501 Then
           ConsoleWrite('click ::: ' & $iDiff & @CRLF)
            $hWnd = _WinAPI_WindowFromPoint($tMSLLHOOKSTRUCT)
            ConsoleWrite(_WinAPI_GetClassName(_WinAPI_GetParent($hWnd)) & @CRLF)
;~             If _WinAPI_GetClassName(_WinAPI_GetParent($hWnd)) = $SHELLDLL_DefView And _
;~                 _WinAPI_IsClassName($hWnd, $__LISTVIEWCONSTANT_ClassName) Then
;~                 Local $avArray = _GUICtrlListView_HitTest($hWnd)
;~                 If $avArray[0] > -1 Then

;~                     $filename=_GUICtrlListView_GetItemText($hWnd, $avArray[0])
;~                     if Stringright($filename,3)="au3" then
;~                         if ControlGetFocus('Program Manager','') = "SysListView321" then ShellExecute("C:\applications\autoit\AutoIt3.exe",'"'&@DesktopDir&"\"&$filename&'"')
;~                         if ControlGetFocus("[class:ExploreWClass]") = "SysListView321" or ControlGetFocus("[class:CabinetWClass]")= "SysListView321"  then ShellExecute("C:\applications\autoit\AutoIt3.exe",'"'&ControlGetText("", "", "Edit1")&"\"&$filename&'"')
;~                         ConsoleWrite(_GUICtrlListView_GetItemText($hWnd, $avArray[0]) & @LF)
;~                         return 1
;~                     EndIf

;~                 EndIf
;~             EndIf
        EndIf
        $iDiff = TimerInit()
    EndIf

    Return _WinAPI_CallNextHookEx($hHook, $iCode, $iwParam, $ilParam)
EndFunc

;~ Func LowLevelMouseProc($iCode, $iwParam, $ilParam)
;~     Local $tMSLLHOOKSTRUCT = DllStructCreate($tagMSLLHOOKSTRUCT, $ilParam)
;~     Local $hWnd

;~     If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $iwParam, $ilParam)

;~     If $iwParam = $WM_LBUTTONDOWN Then
;~         If TimerDiff($iDiff) < 501 Then

;~             $hWnd = _WinAPI_WindowFromPoint($tMSLLHOOKSTRUCT)
;~             If _WinAPI_GetClassName(_WinAPI_GetParent($hWnd)) = $SHELLDLL_DefView And _
;~                 _WinAPI_IsClassName($hWnd, $__LISTVIEWCONSTANT_ClassName) Then
;~                 Local $avArray = _GUICtrlListView_HitTest($hWnd)
;~                 If $avArray[0] > -1 Then ConsoleWrite(_GUICtrlListView_GetItemText($hWnd, $avArray[0]) & @LF)
;~             EndIf
;~         EndIf
;~         $iDiff = TimerInit()
;~     EndIf

;~     Return _WinAPI_CallNextHookEx($hHook, $iCode, $iwParam, $ilParam)
;~ EndFunc

As you can see if you run this piece, it will print out the class name of whatever is under it. No problem to focus on the syslistview control, but I haven't found the correct method to pick one specific column to focus on, if that is even possible.

Any comments appreciated.

Sul.

Share this post


Link to post
Share on other sites
sulfurious

Turns out no matter what I use, Explorer does not return a column value for any but index 1 (Name). You can move this column if you like, but still it remains column 1.

I was able to build a routine that checks for any indices, and if none goes on. This is useful if you need to be sure you can do something without interfering with normal mousing on objects, but not for column specific use.

The low level mouse hook uses up quite a bit of resources on a fast machine if you click more than just infrequently. Would this be normal, or is it because of the timer functions? Using the second code in my examples, I am able to isolate the SysListView only for processing, but because of the return value it cannot tell if a DblClick event happened, you have to use a timer to do this.

Is there a way to simply watch a control/window, such as SysListView32, and retrieve when a double click action occured, without the use of a timer? I don't want to watch all windows, but specifically the active window IF it is the correct one, and even more better IF the chosen control has the focus.

Any input?

Sul.

Share this post


Link to post
Share on other sites
sulfurious

This sure is a hot topic :blink:

After examining the APIs more, it seems like this is the only way to get the mouse clicks from the explorer window (the mouse hook). I tried a bunch of other stuff, including registering my own class and using the CS_DBLCLKS style, which is what is needed to get actual double clicks. But it makes no difference that I can see when it comes to explorer, at least I can't get it to work. I had thought that perhaps by creating a window with that style I could then recieve the DblClck message properly.

So let me pose a new question.

The testing of handles, parent handles and properties chews up some cycles. Explorer itself, when navigating chews up a few cycles, but hook it and it does even more. Maybe not an issue really, but I have noticed that the global mouse hook can cause some effects, such as when you drag a mid-sized explorer window about it can sort of lag and jitter. This is presumably due to the function getting all the mouse messages and dropping them if not the type I want or maybe just because it is filling the struct every time.

So, how would one go about fixing this? One idea is that the callback is not established until an explorer window is active. Maybe a WinWaitActive, then put the hook into place. But, how fast is the unhook/hook? It would be a likely scenario to have 2 explorer windows open, and switching between one explorer instance and the other, as well as other process windows will happen. Is the hook/unhook best utilized in a fashion like this, to only apply the hook when the target window(s) are active?

As well, perhaps there is a better routine for handling the messages the hook sends. Maybe one that drops undesired quicker so that less processing occurs. It is too bad that mouse movement in included in this hook, as I think that is what is causeing the lag.

Here is a working example of what I am playing with in case anyone cares to take a peek.

#NoTrayIcon
#include <WinAPI.au3>
#include <GuiListView.au3>

Opt('TrayMenuMode',1)
$tExit = TrayCreateItem('Exit')
TraySetState()

Global Const $GA_PARENT = 1
Global Const $GA_ROOT = 2
Global Const $GA_ROOTOWNER = 3
Global Const $tagMSLLHOOKSTRUCT = 'int X;int Y;uint;uint;uint;ulong_ptr'
Global Const $WM_LBUTTONDOWN             = 0x0201
Global Const $SHELLDLL_DefView        = "SHELLDLL_DefView"
Global Const $cWEX = "[CLASS:CabinetWClass]"
Global Const $cSys = "[CLASS:SysListView32;INSTANCE:1]"

Global $msSPEED = 501
Dim $hMHook, $pMHook, $hHook
Dim $iDiff = TimerInit()

$hMHook = DllCallbackRegister('LowLevelMouseProc', 'long', 'int;wparam;lparam')
$pMHook = DllCallbackGetPtr($hMHook)

$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, $pMHook, _WinAPI_GetModuleHandle(0))

While 1
    Sleep(50)
    $msg = TrayGetMsg()
    Select
        Case $msg = $tExit
            ExitLoop
    EndSelect
WEnd

Func OnAutoItExit()
    DllCallbackFree($hMHook)
    _WinAPI_UnhookWindowsHookEx($hHook)
EndFunc

Func LowLevelMouseProc($iCode, $iwParam, $ilParam)
    Local $tMSLLHOOKSTRUCT = DllStructCreate($tagMSLLHOOKSTRUCT, $ilParam)
    Local $hWnd, $vIndice

    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $iwParam, $ilParam)
;~ ConsoleWrite('iWparm  :: ' & $iwParam & '  ::: ' & Random(1,100,1) & @CRLF)
    If $iwParam = $WM_LBUTTONDOWN Then
        If TimerDiff($iDiff) < $msSPEED Then
            $hWnd = _WinAPI_WindowFromPoint($tMSLLHOOKSTRUCT)
            If _WinAPI_GetClassName(_WinAPI_GetAncestor($hWnd,$GA_ROOT)) = 'CabinetWClass' Then
                If _WinAPI_GetClassName(_WinAPI_GetParent($hWnd)) = $SHELLDLL_DefView And _
                    _WinAPI_IsClassName($hWnd, $__LISTVIEWCONSTANT_ClassName) Then
                    $vIndice = _GUICtrlListView_GetSelectedIndices($hWnd)
;~                  ConsoleWrite('vIndice :: ' & $vIndice & ' ::: ' & Random(1,100,1) & @CRLF)
                    If $vIndice = '' Then
                        ConsoleWrite('doing something now --  ' & Random(1,100,1) & @CRLF)
                    EndIf
                EndIf
            EndIf
        EndIf
        $iDiff = TimerInit()
    EndIf
    Return _WinAPI_CallNextHookEx($hHook, $iCode, $iwParam, $ilParam)
EndFunc

Sul.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×