Sign in to follow this  
Followers 0
MDCT

Handling file types (extensions) association

13 posts in this topic

#1 ·  Posted (edited)

Hello all,

I want to create an application that could disable double click when an explorer (or any other file managers) is active. Then, it will read the item that I clicked once from the class, and run a custom application for it.

There is an application called C.A.F.E. that could do this. But, CAFE is too extensive detecting clicks, my application that detects _ispressed(01) doesn't work if one of the determined class is active. So, I decided to learn more about this.

I did look around CAFE source, but it confuses me as I'm not familiar at all with AutoHotKey.

To summary, here is the rough Pseudocode:

If Winactive (class or handle)

If double click detected

Disable the last click, so the explorer doesn't run the file

Read information from the class to get the file name

Execute the file with application

There are two things that I do not know how to do here, they are:

1. To disable the mouse last click if a double-click is detected.

2. Read information from the window class to get the file name.

No.1

I was able to detect a double-click using Dllcall from one of the AutoIt gods' script. But, I still need to make so the mouse doesn't send the second click to the explorer class. Otherwise, the script and the explorer both will execute the file.

No.2

I use AutoItWinInfo, but did not find any text that show the item that is selected in the explorer. Then, I tried to get some info from CAFE source and I think this is the one:

filename:=GetFileName()

SplitPath, filename,,, extension

IfEqual extension, lnk

FileGetShortcut, %filename%, filename

But, I'm not sure, as it doesn't work around window class like I think it should. Maybe there is another way to detect what object is selected in the explorer.

Any help would be appreciated.

Thank you.

EDIT: I decided to drop the project, after comparing the performance of CAFE and below code:

While 1
    If WinActive("classname=ExploreWClass") Then
        Sleep(88)
    EndIf   
    Sleep(888)
WEnd

The code takes 1 CPU time noticed by Task Manager, while CAFE could stay 0 even the monitored window is active. I guess, I will talk with CAFE developer to add option to disable the click monitoring when an app is running.

Edited by MDCT

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

I want to create an application that could disable double click when an explorer (or any other file managers) is active. Then, it will read the item that I clicked once from the class, and run a custom application for it.

Does this reflect the structure of you want:

Global $SelSwitch
While 1
    If WinActive("classname=ExploreWClass") Then
        $SelSwitch = -1
    Else
        $SelSwitch = 0
    EndIf
    If $SelSwitch Then
        If Are_Items_Selected() Then
            $FilePath = _GetSelectedItem()
            RunWait("CustomApp" & $FilePath)
            $SelSwitch = 0
        Else
            $SelSwitch = -1
        EndIf
    EndIf
    Sleep(80)
WEnd

Func Are_Items_Selected()
;code that checks if any items in explorer window are selected
    Return 1; assume items are selected
EndFunc ;==>Are_Items_Selected

Func _GetSelectedItem()
;code that returns the path of selected files
    Return '"C:\SquirrelyBusiness.dat"'; assume this file was selected
EndFunc

You haven't been on the forums very long - only 11 posts so far - so I will just tell you: You can do this using AutoIt3.

Edited by Squirrely1

Das Häschen benutzt Radar

Share this post


Link to post
Share on other sites

I do another test and it seems I was mistaken. CAFE also sometime takes 1 CPU.

Thank you for your information, Squirrely1.

I tried to search for _GetSelectedItem(), I could not seem to find it anywhere in the forum. And outside, nothing regarding AutoIt. I think, maybe detecting double click would take less CPU, because it does processing only when two-clicks is detected.

Share this post


Link to post
Share on other sites

#4 ·  Posted (edited)

_GetSelectedItem() is a user function - the last snippet of the code I posted. That function and the other one there are not finished - my code is only a description of the general layout of what I think you want. You will have to do the work, but you can do what is "described" there using AutoIt, MDCT.

Edited by Squirrely1

Das Häschen benutzt Radar

Share this post


Link to post
Share on other sites

Thank you for your kind help, Squirrely1. I know AutoIt can do what CAFE does. It's just I'm not sure I would make a good script. I just worried that I will build one that is not as efficient as CAFE, I would rather use CAFE in that case. But, because of curioussity, I will try to search more on _GetSelectedItem(). Most of articles or sources that I found has nothing to do with AutoIt, so it would be difficult for me to gather this, also considering I have little time to spend outside work. Anyway, before I had done some small research to get efficiency on AutoIt script, it seems higher sleep() time doesn't mean lower CPU time. And I see, sometime someone uses Sleep() with value of 8, like 32 or 128. Perhaps, this are already in the FAQ.

I was thinking to get some references from the CAFE source code. If I got time to search more on this, I will put my findings here. But, I won't put too much optimist on it.

Thanks again, Squirrely1.

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

I think I finally found out what you are trying to do. From your first post, it seems like the overall use of your script is to change "file associations" - search the forums for that, then.

Changing file associations is pretty common. Here is one .au3 way to do it which snippet I found here on the forums:

Func _FiletypeAssociation($extension, $type, $program, $description = '')
; e.g. _FiletypeAssociation('.pdf', 'FoxitReader.Document', '"%ProgramFiles%\FoxitReader\FoxitReader.exe" "%1"')
; e.g. _FiletypeAssociation('.cmd', 'cmdfile', '"%1" %*')
    $exitcode = RunWait(@ComSpec & ' /c ftype ' & $type & '=' & $program & _
             ' && assoc ' & $extension & '=' & $type, '', @SW_HIDE)
    If $description And Not $exitcode Then
        Return RegWrite('HKCR\' & $type, '', 'Reg_sz', $description)
    EndIf
    Return Not $exitcode
EndFunc

ftype assoc batch command help:

Displays or modifies file extension associations

ASSOC [.ext[=[fileType]]]

.ext Specifies the file extension to associate the file type with

fileType Specifies the file type to associate with the file extension

Type ASSOC without parameters to display the current file associations.

If ASSOC is invoked with just a file extension, it displays the current

file association for that file extension. Specify nothing for the file

type and the command will delete the association for the file extension.

Changing file associations on the fly - that's just weird - or malicious if used on someone else's machine - so I am going to have to ask - why do you want to do this?

Other evidence against you:

...want to create an application that could disable...

- from the top of this thread.

I could get to thinking that CAFE stands for cafeteria - like school cafeteria - like school cafeteria prankster, reading "Kid who hasn't learned about morality yet because he is banned by law from doing so in some countries - like mine."

And CAFE as in "cafe standards", reading "no standards".

Edited by Squirrely1

Das Häschen benutzt Radar

Share this post


Link to post
Share on other sites

Yes, the actual method to change file types is using registry. I did search, someone did ask for something similar with what I want, but were told to use registry instead. That's why, I started my own topic with additional information "By intercepting double click to windows-class". Thanks, anyway.

Using the registry would be dirty and need to be added constantly upon restart for system that will revert back on restart, like Deep Freeze, GoBack, etc. I'm trying to use portable applications as much as possible, most users should be like that too, as the usage of portable application could help to reduce Windows slowing down.

I did say: "...want to create an application that could disable...". But, it is because that's the only way I can think of.. to prevent the Windows Explorer from also running the application when I double click an entry. I don't think Explorer can be set not to accept double-click.

To make it clearer, why I asked to disable mouse, here is my reasoning.

I need an application that could detect double click on Explorer. So, if I double click on a file, my app will read what file and run a different application. But, if I do not disable the last click, the explorer will run the application too. That's why I was thinking to disable the last click.

And I don't think there is any other monitoring method regarding this that will be as efficient as detecting the double click.

To make it even more clearer. Go to CAFE website, I thought CAFE was quite popular, so I didn't add it here before: http://clef.usb.googlepages.com/cafe

If you want to read english description of it, go to http://portablefreeware.com/forums/viewtop...asc&start=0

Try it, I'm sure you will like it.

Share this post


Link to post
Share on other sites

#8 ·  Posted (edited)

So why that C.A.F.E. utility doesn't suit your needs again?

It's kinda off-topic, but as far as actual hotkeying and window automating goes - the initial purpose and common beginning of these two scripting languages (AHK vs AU3) - at least right now AHK is winning, hands down IMO. It's smaller and yet at the same time has more built-in features for such tasks, although its syntax and documentation isn't as newb friendly as that of Autoit.

It's true that anything you can do in AHK, you can do in Autoit too, but in the latter case more often than not the result is more bloated and slower.

Anyway, what you need here, is low level mouse hook (WH_MOUSE_LL). To disable a click, return a non-zero value from the callback function. And since there is no doubleclick message for WH_MOUSE_LL, you have to detect such event yourself by calculating the time between buttondown events and click coords.

Working concept (needs post v3.2.10.0 betas for these WindowsHook related function wrappers):

#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#Include <GuiListView.au3>

Global $hLLMouseProc, $hHook, $iLastTime, $iLastX, $iLastY, $hExplore, $aDClickTimeout = DllCall("User32.dll", "int", "GetDoubleClickTime"), _
        $aDClickX = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 36), $aDClickY = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 37)
$hLLMouseProc = DllCallbackRegister("_LLMouseProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hLLMouseProc), _WinAPI_GetModuleHandle(0))

While 1
    Sleep(20)
    If $hExplore <> 0 Then
        $aPaths = _ExplorerGetSelPaths($hExplore)
        If Not @error Then 
            For $i = 1 To $aPaths[0]
                ConsoleWrite($aPaths[$i] & @CRLF)
            Next
        EndIf
        $hExplore = 0
    EndIf
WEnd

Func _LLMouseProc($iCode, $wParam, $lParam)
    Local $tMouseStruct, $hPoint, $iItem, $iX, $iY
    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
    If $wParam = 0x201 Then ;$WM_LBUTTONDOWN Then
        $tMouseStruct = DllStructCreate("int X;int Y;dword MouseData;dword Flags;dword Time;ulong ExtraInfo", $lParam)
        $iX = DllStructGetData($tMouseStruct, 1)
        $iY = DllStructGetData($tMouseStruct, 2)        
        If (DllStructGetData($tMouseStruct, 5) - $iLastTime < $aDClickTimeout[0]) And (Abs($iX-$iLastX) < $aDClickX[0]) And (Abs($iY-$iLastY) < $aDClickY[0]) Then ;double click
            $hPoint = DllCall("User32.dll", "hwnd", "WindowFromPoint", "int", $iX, "int", $iY)
            $hWndParent = _WinAPI_GetAncestor($hPoint[0], 2)
            If (_WinAPI_GetClassName($hPoint[0]) == "SysListView32") And (_WinAPI_GetClassName($hWndParent) == "CabinetWClass") Then ;clicked in explorer listview
                _WinAPI_ScreenToClient($hPoint[0], $tMouseStruct)
                $iItem = _GUICtrlListView_SubItemHitTest($hPoint[0], DllStructGetData($tMouseStruct, 1), DllStructGetData($tMouseStruct, 2))
                If $iItem[0] <> -1 Then ;clicked on item
                    $hExplore = $hWndParent
                    Return 1
                EndIf
            EndIf
        EndIf
        $iLastTime = DllStructGetData($tMouseStruct, 5)
        $iLastX = $iX
        $iLastY = $iY
    EndIf
    Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
EndFunc
Func _ExplorerGetSelPaths($hWnd)
    Local $oShell = ObjCreate("Shell.Application"), $oShellWindows = $oShell.Windows(), $o, $i
    For $o In $oShellWindows
        If $o.HWND = $hWnd Then
            Local $iCount = $o.document.SelectedItems.Count, $aPaths[$iCount + 1] = [$iCount], $iIndex = 1
            If $iCount > 0 Then
                For $i In $o.document.SelectedItems
                    $aPaths[$iIndex] = $i.Path
                    $iIndex += 1
                Next
            EndIf
            Return SetError(0, 0, $aPaths)
        EndIf
    Next
    Return SetError(1, 0, 0)
EndFunc
Func OnAutoItExit()
    _WinAPI_UnhookWindowsHookEx($hHook)
    DllCallbackFree($hLLMouseProc)
EndFunc
Exit

Some of the above may be done differently, for example, to get path of selected file in explorer, you could simply Send a Ctrl+C and Clipget, which is basically how C.A.F.E. does it. This would be faster than the COM approach I used above (just wanted to try something different for myself), and you could do it from callback function.

Also I added check if doubleclick happened on item; which C.A.F.E. doesn't have - it seems to execute selected file even if you click on explorer's titlebar - I consider it bug, you may consider it feature and remove all that ListView related code from above if you want.

If you prefer working with UDFs where you only need to #include and copy/paste some function names, and don't want to instal beta, see this: http://www.autoitscript.com/forum/index.php?showtopic=64738, it provides wrappers to this mouse hook. It will add some more overhead though.

AutoHotkey has built-in WH_MOUSE_LL, so it's more efficient.

Edited by Siao

"be smart, drink your wine"

Share this post


Link to post
Share on other sites

Wow, thank you, Siao. Not just helping me with CAFE source code, you even create another with different method. You must be quite curious about this.

I did test your method, but as you said, it's slow. And mostly autoit3.exe crashes when I double click an explorer file object, and after that the mouse becomes slow, I need to close the error window to get the mouse speed back. And on rare cases, it says failed to get the object. Maybe it's because of my PC, my settings are quite different than normal PCs, one example I had difficulties to get CPU time, because I disable all the performance counters and the info here are mostly regarding WMI and of course the taskman method is not my choice.

The problem I got with CAFE is:

Autoit application that uses _IsPressed('01') will not work. Example, a simple autoit script:

If _IsPressed('01') Then msgbox(0,"","Test")
exit

Now, I try to double-click the exe and hold the left button, but it will not show the msgbox. (Of course user needs to do this in file manager class that is monitored by CAFE). Maybe it's because CAFE is detecting click, then autoit IsPressed is not working properly.

I did try to create a small GUI below the mouse, just to make the file explorer not active (because I'm sure CAFE monitors when the window is active). But, it didn't seem to work.

Actually, there was an easy way to fix this, which is by closing CAFE temporarily when the app with IsPressed is runing. However, for me it's not a beautiful solution, that's why I was thinking to create my own CAFE like app with smaller feature and better efficiency... well, not anymore though. I hope in time AutoIt could be as efficient as AHK. So, for now I'm going to use CAFE.

Thank you very much again for your info, Siao. They are very revealing and educating.

Take care.

Share this post


Link to post
Share on other sites

I did test your method, but as you said, it's slow. And mostly autoit3.exe crashes when I double click an explorer file object, and after that the mouse becomes slow, I need to close the error window to get the mouse speed back. And on rare cases, it says failed to get the object. Maybe it's because of my PC, my settings are quite different than normal PCs, one example I had difficulties to get CPU time, because I disable all the performance counters and the info here are mostly regarding WMI and of course the taskman method is not my choice.

What error window says?

Try adding this line at the top of the script:

ProcessSetPriority(@AutoItPID, 4)

Thing is, as all the messages of the hook are routed through a script function, it's understandable that is slower (as compared to machine code, if the hook was built-in like in AHK).

However, on my oldie P4 1.6 mouse lag is really noticeable only when some other task is taking lots of CPU, which makes AutoIt take a backseat. Otherwise it's not that bad.

Try this slightly modified script:

#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#Include <Misc.au3>

_Singleton("AltAppRunner")
ProcessSetPriority(@AutoItPID, 4)
$oMyError = ObjEvent("AutoIt.Error","MyErrFunc")

Global $hLLMouseProc, $hHook, $iLastTime, $iLastX, $iLastY, $hExplore, $aDClickTimeout = DllCall("User32.dll", "int", "GetDoubleClickTime"), _
        $aDClickX = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 36), $aDClickY = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 37)
$hLLMouseProc = DllCallbackRegister("_LLMouseProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hLLMouseProc), _WinAPI_GetModuleHandle(0))

While 1
    Sleep(20)
    If $hExplore <> 0 Then
        $aPath = _ExplorerGetSelPaths($hExplore)
        If $aPath[0] > 0 Then
            For $i = 1 To $aPath[0]
                ConsoleWrite($aPath[$i] & @CRLF)
            Next
        EndIf
        $hExplore = 0
    EndIf
WEnd

Func _LLMouseProc($iCode, $wParam, $lParam)
    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
    If $wParam = 0x201 Then ;$WM_LBUTTONDOWN Then
        Local $tMouseStruct, $hPoint, $hWndParent, $iItem, $iX, $iY
        $tMouseStruct = DllStructCreate("int X;int Y;dword MouseData;dword Flags;dword Time;ulong ExtraInfo", $lParam)
        $iX = DllStructGetData($tMouseStruct, 1)
        $iY = DllStructGetData($tMouseStruct, 2)        
        If (DllStructGetData($tMouseStruct, 5) - $iLastTime < $aDClickTimeout[0]) And (Abs($iX-$iLastX) < $aDClickX[0]) And (Abs($iY-$iLastY) < $aDClickY[0]) Then ;double click
            $hPoint = DllCall("User32.dll", "hwnd", "WindowFromPoint", "int", $iX, "int", $iY)
            $hWndParent = _WinAPI_GetAncestor($hPoint[0], 2)
            If (_WinAPI_GetClassName($hPoint[0]) == "SysListView32") And (_WinAPI_GetClassName($hWndParent) == "CabinetWClass") Then ;clicked in explorer listview
                $hExplore = $hWndParent
                Return 1
            EndIf
        EndIf
        $iLastTime = DllStructGetData($tMouseStruct, 5)
        $iLastX = $iX
        $iLastY = $iY
    EndIf
    Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
EndFunc
Func _ExplorerGetSelPaths($hWnd)
    Local $oShell = ObjCreate("Shell.Application"), $oShellWindows = $oShell.Windows(), $o, $i
    For $o In $oShellWindows
        If $o.HWND = $hWnd Then
            Local $iCount = $o.document.SelectedItems.Count, $aPaths[$iCount + 1] = [$iCount], $iIndex = 1
            If $iCount > 0 Then
                For $i In $o.document.SelectedItems
                    $aPaths[$iIndex] = $i.Path
                    $iIndex += 1
                Next
            EndIf
            Return SetError(0, 0, $aPaths)
        EndIf
    Next
    Return SetError(1, 0, 0)
EndFunc
Func OnAutoItExit()
    _WinAPI_UnhookWindowsHookEx($hHook)
    DllCallbackFree($hLLMouseProc)
EndFunc
Func MyErrFunc() 
   Msgbox(0,"Error", StringFormat('Scriptline = %s\r\nError number = %08X\r\nWinDescription = %s', _
                                        $oMyError.Scriptline, $oMyError.Number, $oMyError.WinDescription _
                                        ) _
            )
   SetError(1, 0, 0)
Endfunc

Exit

In any case, as I said earlier, you can always simply Send("^c") in callback function and ClipGet() in main loop instead of that _ExplorerGetSelPaths() func.


"be smart, drink your wine"

Share this post


Link to post
Share on other sites

Hi, Siao. This is the error msgbox I got:

---------------------------

Error

---------------------------

Scriptline = 51

Error number = 80020009

WinDescription = Class not registered

And after pressing "ENTER"

---------------------------

Error

---------------------------

Scriptline = 52

Error number = 000000A9

WinDescription = Variable is not of type 'Object'.

Oh, and maybe I should have told you before, that I'm not using explorer as my shell, but GeoShell. I believe it has something to do with the error I'm getting.

Out of curioussity and since you have done all the work, I try to make your code to work on the ClipGet() and other window class:

#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#Include <GuiListView.au3>
#include "MouseSetOnEvent_UDF.au3"
$handle = WinGetHandle("[CLASS:CabinetWClass]")
;Global $WH_MOUSE_LL        = 14
Global $hLLMouseProc, $hHook, $iLastTime, $iLastX, $iLastY, $hExplore, $aDClickTimeout = DllCall("User32.dll", "int", "GetDoubleClickTime"), _
$aDClickX = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 36), $aDClickY = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 37)
$hLLMouseProc = DllCallbackRegister("_LLMouseProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hLLMouseProc), _WinAPI_GetModuleHandle(0))

While 1
    Sleep(250)
WEnd

Func _LLMouseProc($iCode, $wParam, $lParam)
    Local $tMouseStruct, $hPoint, $iItem, $iX, $iY
    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
    If WinActive($handle) then
    If $wParam = 0x201 Then;$WM_LBUTTONDOWN Then
        $tMouseStruct = DllStructCreate("int X;int Y;dword MouseData;dword Flags;dword Time;ulong ExtraInfo", $lParam)
        $iX = DllStructGetData($tMouseStruct, 1)
        $iY = DllStructGetData($tMouseStruct, 2)
            If (DllStructGetData($tMouseStruct, 5) - $iLastTime < $aDClickTimeout[0]) And (Abs($iX-$iLastX) < $aDClickX[0]) And (Abs($iY-$iLastY) < $aDClickY[0]) Then;double click
                _MouseSetOnEvent($MOUSE_PRIMARYDOWN_EVENT);disable click
                _MouseSetOnEvent($MOUSE_PRIMARYDOWN_EVENT);enable click
                Send("^c"); Copy and get clipboard
                $Clip=ClipGet()
                $len = StringLen($Clip)
                $remove = StringInStr($Clip, ".", 0, -1)
                if $len-$remove =3 then; if 3 then it's file w/ ext.
                    $result = StringTrimleft($Clip, $remove)
                    If $result = "txt" then
                        ConsoleWrite($result)
                    Else 
                        Send("{ENTER}") 
                    EndIf
                Else
                    Send("{ENTER}");directory or files w/o ext.
                EndIf
            ;ClipPut("")
            EndIf
        $iLastTime = DllStructGetData($tMouseStruct, 5)
        $iLastX = $iX
        $iLastY = $iY
    EndIf
    EndIf
EndFunc

But, it's not stable at all. I must have done something wrong here.

And your code using the _ExplorerGetSelPaths() seems nicer to CPU, unlike the ClipGet. With ClipGet each time I double click on any place in the correct active window, it takes around 2-3 CPU. But again... I must have done something wrong here.

Also the checking, maybe I need to use FileExists, as above will include directory with "." and 3 letters, but FileExists might be bad on CPU. And also like you said double clicking on title, with CAFE I'm able to double click the title window (even though slower and CAFE takes CPU).. I guess, CAFE is not just ctrl-C & ClipGet.

Any ideas or suggestions? Does CAFE already use the best tricks for this?

Share this post


Link to post
Share on other sites

But, it's not stable at all. I must have done something wrong here.

And your code using the _ExplorerGetSelPaths() seems nicer to CPU, unlike the ClipGet. With ClipGet each time I double click on any place in the correct active window, it takes around 2-3 CPU. But again... I must have done something wrong here.

Also the checking, maybe I need to use FileExists, as above will include directory with "." and 3 letters, but FileExists might be bad on CPU.

Lol, I don't think "2-3 CPU" at the time of doubleclick is a big deal at all. What matters more is that clipboard method is kind of less reliable in general, and actually maybe even a bit slower since you have to wait some for the data to appear on clipboard after you do Copy; and, in case of our current example, overwrites clipboard contents, which may be important to some ppl (AHK has more advanced clipboard functions built-in, which let save/restore clipboard, wait for data, all in just a couple lines of code, and CAFE uses these. To achieve same thing in AutoIt would need more effort).

Also, while you can do all the string parsing and other stuff inside hook proc, and I guess I unintentionally suggested you this myself earlier, but if you want stability, better not do this and return from hook proc asap, so the system can go on processing other mouse messages.

Try

#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#Include <Misc.au3>

_Singleton("AltAppRunner")
Opt("SendKeyDelay", 0)
Opt("WinWaitDelay", 0)
ProcessSetPriority(@AutoItPID, 4)

Global $hLLMouseProc, $hHook, $iLastTime, $iLastX, $iLastY, $hExplore, $aDClickTimeout = DllCall("User32.dll", "int", "GetDoubleClickTime"), _
        $aDClickX = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 36), $aDClickY = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 37)
$hLLMouseProc = DllCallbackRegister("_LLMouseProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hLLMouseProc), _WinAPI_GetModuleHandle(0))

While 1
    Sleep(20)
    If $hExplore Then
        _ExInterceptor($hExplore)
        $hExplore = 0
    EndIf
WEnd

Func _ExInterceptor($hWnd)
    ClipPut("")
    WinActivate($hWnd)
    Send("^c")
    Local $sPath, $sExt, $iTimer = TimerInit()
    Do ;wait for clipboard
        Sleep(20)
        $sPath = ClipGet()
    Until $sPath Or TimerDiff($iTimer) > 500
    If Not FileExists($sPath) Then Return MouseClick("primary", $iLastX, $iLastY, 2)
    $sExt = StringTrimLeft($sPath, StringInStr($sPath, ".", 2, -1))
    Switch $sExt
        Case "txt"
            ConsoleWrite('_ExInterceptor : .TXT file doubleclicked.' & @CRLF)
        Case "jpg", "jpeg", "gif", "png", "bmp"
            ConsoleWrite('_ExInterceptor : Picture file doubleclicked.' & @CRLF)
        Case Else
            ControlSend($hWnd, '', '', "{ENTER}")
    EndSwitch
    Return 1
EndFunc
Func _LLMouseProc($iCode, $wParam, $lParam)
    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
    If $wParam = 0x201 Then ;$WM_LBUTTONDOWN Then
        Local $tMouseStruct, $hPoint, $hWndParent, $iItem, $iX, $iY
        $tMouseStruct = DllStructCreate("int X;int Y;dword MouseData;dword Flags;dword Time;ulong ExtraInfo", $lParam)
        $iX = DllStructGetData($tMouseStruct, 1)
        $iY = DllStructGetData($tMouseStruct, 2)        
        If (DllStructGetData($tMouseStruct, 5) - $iLastTime < $aDClickTimeout[0]) And (Abs($iX-$iLastX) < $aDClickX[0]) And (Abs($iY-$iLastY) < $aDClickY[0]) Then ;double click
            $hPoint = DllCall("User32.dll", "hwnd", "WindowFromPoint", "int", $iX, "int", $iY)
            $hWndParent = _WinAPI_GetAncestor($hPoint[0], 2)
            If (_WinAPI_GetClassName($hPoint[0]) == "SysListView32") And (_WinAPI_GetClassName($hWndParent) == "CabinetWClass") Then ;clicked in explorer listview
;~          If (_WinAPI_GetClassName($hWndParent) == "CabinetWClass") And Not $hExplore Then
                $hExplore = $hWndParent
                Return 1
            EndIf
        EndIf
        $iLastTime = DllStructGetData($tMouseStruct, 5)
        $iLastX = $iX
        $iLastY = $iY
    EndIf
    Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
EndFunc
Func OnAutoItExit()
    _WinAPI_UnhookWindowsHookEx($hHook)
    DllCallbackFree($hLLMouseProc)
EndFunc

Exit

And also like you said double clicking on title, with CAFE I'm able to double click the title window (even though slower and CAFE takes CPU).. I guess, CAFE is not just ctrl-C & ClipGet.

Any ideas or suggestions? Does CAFE already use the best tricks for this?

With CAFE, you can doubleclick anywhere on explorer window (titlebar, toolbar, listview, whatever...) and it will launch currently selected file instead of doing what is supposed to be done (such as maximize/restore when doubleclicked on titlebar). If you consider this a useful feature, in the code above uncomment the second If... _WinAPI_GetClassName.. check and comment the one above it (which additionally checks if click happened in listview).

"be smart, drink your wine"

Share this post


Link to post
Share on other sites

#13 ·  Posted (edited)

Siao, you are a genius! It works very stable and very nice. This is cool.

I add the functionality to save the current clipboard and put it back when everything is done. And I noticed the clipboard method with MS Word running will show the clipboard notification each time I double click on an object. I believe, I could disable this notification.

#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include <StructureConstants.au3>
#Include <Misc.au3>

$ClassName="ATL:ExplorerFrame";File Explorer's Class Name
$ListViewClassName="ATL:BrowserListView";File Explorer's List View Class
_Singleton("AltAppRunner")
Opt("SendKeyDelay", 0)
Opt("WinWaitDelay", 0)
ProcessSetPriority(@AutoItPID, 4)
Global $WH_MOUSE_LL     = 14
Global $hLLMouseProc, $hHook, $iLastTime, $iLastX, $iLastY, $hExplore, $aDClickTimeout = DllCall("User32.dll", "int", "GetDoubleClickTime"), _
        $aDClickX = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 36), $aDClickY = DllCall("User32.dll", "int", "GetSystemMetrics", "int", 37)
$hLLMouseProc = DllCallbackRegister("_LLMouseProc", "long", "int;wparam;lparam")
$hHook = _WinAPI_SetWindowsHookEx($WH_MOUSE_LL, DllCallbackGetPtr($hLLMouseProc), _WinAPI_GetModuleHandle(0))

While 1
    Sleep(20)
    If $hExplore Then
        _ExInterceptor($hExplore)
        $hExplore = 0
    EndIf
WEnd

Func _ExInterceptor($hWnd)
    $Clipboard = ClipGet();Save current clipboard
    ClipPut("")
    WinActivate($hWnd)
    Send("^c")
    Local $sPath, $sExt, $iTimer = TimerInit()
    Do;wait for clipboard
        Sleep(20)
        $sPath = ClipGet()
    Until $sPath Or TimerDiff($iTimer) > 500
    If Not FileExists($sPath) Then 
        ClipPut($Clipboard)
        Return 0;MouseClick("primary", $iLastX, $iLastY, 2)
    EndIf
    $sExt = StringTrimLeft($sPath, StringInStr($sPath, ".", 2, -1))
    Switch $sExt
        Case "avi","mpg","wmv","mp4","mpeg","rmvb","asf","mkv","mov","flv","mpe"
                ConsoleWrite('_ExInterceptor : Video file doubleclicked.' & @CRLF)
        Case "jpg", "jpeg", "gif", "png", "bmp"
            ConsoleWrite('_ExInterceptor : Picture file doubleclicked.' & @CRLF)
        Case Else
            ControlSend($hWnd, '', '', "{ENTER}")
    EndSwitch
    ClipPut($Clipboard)
    Sleep(500)
    Return 1
EndFunc

Func _LLMouseProc($iCode, $wParam, $lParam)
    If $iCode < 0 Then Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
    If $wParam = 0x201 Then;$WM_LBUTTONDOWN Then
        Local $tMouseStruct, $hPoint, $hWndParent, $iItem, $iX, $iY
        $tMouseStruct = DllStructCreate("int X;int Y;dword MouseData;dword Flags;dword Time;ulong ExtraInfo", $lParam)
        $iX = DllStructGetData($tMouseStruct, 1)
        $iY = DllStructGetData($tMouseStruct, 2)       
        If (DllStructGetData($tMouseStruct, 5) - $iLastTime < $aDClickTimeout[0]) And (Abs($iX-$iLastX) < $aDClickX[0]) And (Abs($iY-$iLastY) < $aDClickY[0]) Then;double click
            $hPoint = DllCall("User32.dll", "hwnd", "WindowFromPoint", "int", $iX, "int", $iY)
            $hWndParent = _WinAPI_GetAncestor($hPoint[0], 2)
            If (_WinAPI_GetClassName($hPoint[0]) == $ListViewClassName) And (_WinAPI_GetClassName($hWndParent) == $ClassName) Then;clicked in explorer listview
;~  If (_WinAPI_GetClassName($hWndParent) == "CabinetWClass") And Not $hExplore Then
                $hExplore = $hWndParent
                Return 1
            EndIf
        EndIf
        $iLastTime = DllStructGetData($tMouseStruct, 5)
        $iLastX = $iX
        $iLastY = $iY
    EndIf
    Return _WinAPI_CallNextHookEx($hHook, $iCode, $wParam, $lParam)
EndFunc

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

Exit

That class I'm using there is for Xplorer2 file manager; customizations, color coding, functions, tabs, multi-pane, etc. There is freeware version for it, but the commercial is worth it. For me, it's the best file manager there is... but again, it's a subjective matter.

And I change return mouseclick in "If Not FileExists($sPath) Then". Because it moves my mouse if I double-click while no entry is selected. I assume, you want to show me that we can add our own command when double-clicked on empty space, like ex: go up one level from current folder by sending hotkeys (in this case "backspace").

Lol, I don't think "2-3 CPU" at the time of doubleclick is a big deal at all.

I guess you are right. I must have compared everything with CAFE, even though you told me AHK is more efficient in this case. And because I'm using EWF (Enhanced Write Filter), I must have getting carried away with efficiency.

Off topic about EWF: EWF is a service that would redirect writes that supposed to be happened on harddisk (or other storage device) to RAM, based on clusters. Which means, your harddisk will free of writes that should prolong its life. And, because RAM losses its memory without power, on restart your XP will be on the previous state (like DeepFreeze, but different method). Also, it adds ability to use XP on USB. But, the bad is you would need to reduce writes on XP or try not to make any write to XP to keep more RAM available. If you are interested in EWF, I would be happy to explain more to you.

With CAFE, you can doubleclick anywhere on explorer window (titlebar, toolbar, listview, whatever...) and it will launch currently selected file instead of doing what is supposed to be done (such as maximize/restore when doubleclicked on titlebar).

My apology, you are right. With CAFE, I cannot maximize the window by d-clicking on the title.

I must have been accidentally running the script I posted before this, while checking the CAFE's d-click on title. So, it blocks the CAFE from detecting the d-click. How clumsy of me. And I don't like the double click to run app either, it just changes the way I normally manage windows.

And I tried your method on PC with explorer.exe as shell. I was thinking that using clipboard would waste some resources in time especially with the clipboard app that collects data.

I changed the ConsoleWrite in the main loop to "msgbox(0,"",$aPath[$i])"... hoping to get message in msgbox instead console as the PC doesn't have Scite. However, it shows nothing, no error messagebox from MyErrFunc either. I did't know the settings of the PC I tested the code with, though. I was just looking for PC with explorer as shell and give it a few spins. Next time, I will try to give better test on this, when the time is right.

Anyway, I'm already happy with your code, Siao. It is perfectly fulfilled what I need with more features and stability. Thank you again for lending me your brain. :)

Have a nice weekends.

EDIT:

For the sake of documentation, the ClipPut needs checking to enable copy a file. Otherwise, after changing a folder, the file copy will lost. But it won't work if the copied file is more than one.

If Not FileExists($Clipboard) Then 
    ClipPut($Clipboard)
Else 
    _ClipPutFile($Clipboard)
EndIf
Edited by MDCT

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  
Followers 0