Jump to content

Centered FileOpenDialog() using self-terminating WM_TIMER call


Recommended Posts

 Hiho Team,

attached a small example for a self-centering FileOpenDialog() using a self-terminating timer call.

In the past I used _Timer_SetTimer() with a callback function, which works on x86, but does not under x64.

So here's an example working for x64 too, using a global variable and WM_TIMER calls.

What I just realized is that the doc for _WinAPI_SetTimer(), but also the MSDN doc for SetTimer, seem to be false.

Success: The timer identifier. An application can pass this value to the _WinAPI_KillTimer() function to destroy the timer.

_WinAPI_KillTimer() does not work when used with a hWnd and no callback func, _WinAPI_SetTimer() needs to be fed a dedicated TimerID (see "9876" used below). When I use the TimerID returned by _WinAPI_SetTimer() then _WinAPI_KillTimer() does not work.

Hopefully useful for someone ūüėČ.

Edit: Replace fixed TimerID value 9876 with a TimerInit() call, I like the idea of a unique ID more.

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_UseX64=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <WinAPISysWin.au3>

Global $a_Global_FileOpenDialog_Wrapper[3] ; 0 = Title, 1 = hWnd, 2 = TimerID

Local $s_Selected_File = _FileOpenDialog_Wrapper("Select File", @ScriptDir, "All (*.*)", 1 + 2 + 4)
If Not @error Then MsgBox(0, "", $s_Selected_File)

Func _FileOpenDialog_Wrapper($sTitle, $sInitDir, $sFilter, $iOptions = 0, $sDefaultName = "", $hWnd = 0)
    ; https://groups.google.com/forum/?hl=en&fromgroups=#!topic/microsoft.public.vc.mfc/HafQr4gIRY0
    ; The problem is that GetOpenFileName changes the current directory to the last browsed one. The current directory and any of its parents cannot be deleted.

    If Not $sInitDir Then $sInitDir = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}" ; CLSID for "My Computer"

    Local $sWorkingDir = @WorkingDir

    $a_Global_FileOpenDialog_Wrapper[0] = $sTitle

    Local $old_GUISwitch = GUISwitch(0)
    $a_Global_FileOpenDialog_Wrapper[1] = GUICreate("_FileOpenDialog_Wrapper")
    GUISwitch($old_GUISwitch)

    GUIRegisterMsg($WM_TIMER, "WM_TIMER")
    $a_Global_FileOpenDialog_Wrapper[2] = _WinAPI_SetTimer($a_Global_FileOpenDialog_Wrapper[1], TimerInit(), 10, 0)

    Local $sFilename = FileOpenDialog($sTitle, $sInitDir, $sFilter, $iOptions, $sDefaultName, $hWnd)
    Local $iError = @error

    ConsoleWrite("_WinAPI_KillTimer-2= " & _WinAPI_KillTimer($a_Global_FileOpenDialog_Wrapper[1], $a_Global_FileOpenDialog_Wrapper[2]) & @CRLF)

    GUIRegisterMsg($WM_TIMER, "")
    GUIDelete($a_Global_FileOpenDialog_Wrapper[1])

    FileChangeDir($sWorkingDir)

    If Not $iError Then
        If StringInStr($sFilename, "|", 2) Then ; multiple selection
            Local $aFilename = StringSplit($sFilename, "|")
        Else
            Local $sPath = StringTrimRight($sFilename, StringLen($sFilename) - StringInStr($sFilename, "\", 2, -1))
        EndIf
    EndIf

    Return SetError($iError, 0, $sFilename)
EndFunc   ;==>_FileOpenDialog_Wrapper

Func WM_TIMER($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $iMsg, $iwParam, $ilParam

    ConsoleWrite("WM_TIMER " & @TAB & TimerInit() & @TAB & $hWnd & @TAB & $iMsg & @TAB & $iwParam & @TAB & $ilParam & @CRLF)

    Local $hWnd_Timer_Move_FileOpenDialog = WinGetHandle("[TITLE:" & $a_Global_FileOpenDialog_Wrapper[0] & ";CLASS:#32770]", "")
    If IsHWnd($hWnd_Timer_Move_FileOpenDialog) Then
        ConsoleWrite("WinMove= " & WinMove($hWnd_Timer_Move_FileOpenDialog, "", (@DesktopWidth - (@DesktopWidth / 4 * 3)) / 2, (@DesktopHeight - (@DesktopHeight / 4 * 3)) / 2, @DesktopWidth / 4 * 3, @DesktopHeight / 4 * 3) & @CRLF)
        ConsoleWrite("_WinAPI_KillTimer-1= " & _WinAPI_KillTimer($a_Global_FileOpenDialog_Wrapper[1], $a_Global_FileOpenDialog_Wrapper[2]) & @CRLF)
        ConsoleWrite(_WinAPI_GetLastErrorMessage() & @CRLF)
    EndIf

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_TIMER

Different approach, same result as with

 

Edited by KaFu
Link to post
Share on other sites
  • KaFu changed the title to Centered FileOpenDialog() using self-terminating WM_TIMER call
On 11/26/2022 at 3:54 PM, KaFu said:

_WinAPI_KillTimer() does not work when used with a hWnd and no callback func, _WinAPI_SetTimer() needs to be fed a dedicated TimerID (see "9876" used below). When I use the TimerID returned by _WinAPI_SetTimer() then _WinAPI_KillTimer() does not work.

I guess you wrote this in case the 2nd parameter in _WinAPI_SetTimer is set to 0 ($iTimerID) then the function returns 1 (tested) and _WinAPI_KillTimer() fails because of this 0/1 discrepancy.

msdn SetTimer adds 4 crucial words at the very beginning of this 2nd parameter description :

A nonzero timer identifier.

Unfortunately these 4 words aren't found in AutoIt help file, topic  _WinAPI_SetTimer, maybe because at the very end of the description, we also read the following sentence, where this 2nd parameter can sometimes be = 0 in a special case :

If the call is not intended to replace an existing timer, $iTimerID should be 0 if the $hWnd is 0.

Nevertheless (imho) this important short sentence "A nonzero timer identifier" should be indicated in AutoIt help file, at the same place where it's found on msdn,  making it clearer for the user.

Link to post
Share on other sites

Good point, I'll report that in bug tracker and also ask for a second example for this different scenario, e.g.

#include <GUIConstantsEx.au3>
#include <Misc.au3>
#include <WinAPISysWin.au3>
#include <WindowsConstants.au3>

Opt('TrayAutoPause', 0)

Global $g_iCount = 0
Local $iTimerID = 999

Local $hGUI = GUICreate("Example")
GUIRegisterMsg($WM_TIMER, "WM_TIMER")

$iTimerID = _WinAPI_SetTimer($hGUI, $iTimerID, 1000, 0)

Do
    Sleep(100)
Until _IsPressed('1B')

_WinAPI_KillTimer($hGUI, $iTimerID)


Func WM_TIMER($hWnd, $iMsg, $iwParam, $ilParam)
    #forceref $hWnd, $iMsg, $iwParam, $ilParam

    ConsoleWrite($g_iCount & @CRLF)
    $g_iCount += 1

    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_TIMER

 

Link to post
Share on other sites

I used AdlibRegister() for that in the past. The great advantage of the TImers is, that they work even if a blocking function is called ūüėČ.

#include <GUIConstantsEx.au3>
#include <Misc.au3>
#include <WinAPISysWin.au3>
#include <WindowsConstants.au3>


ToolTip("1a53da04-c3d6-48fb-819f-6cf3dbe01c4c")
Global $hWnd_ToolTip = WinGetHandle("1a53da04-c3d6-48fb-819f-6cf3dbe01c4c")
ToolTip("")
Sleep(1000) ; Tooltip doesn't flash!

ToolTip("AdlibRegister")
AdlibRegister("_Move_Tooltip", 10) ; Does move Tooltip
Do
    Sleep(100)
Until _IsPressed('1B')
AdlibUnRegister("_Move_Tooltip")


AdlibRegister("_Move_Tooltip", 10) ; Does NOT move Tooltip because of blocking function
MsgBox(262144, "", "Blocks")
AdlibUnRegister("_Move_Tooltip")


ToolTip("_WinAPI_SetTimer")
Local $hTimerProc = DllCallbackRegister('_TimerProc', 'none', 'hwnd;uint;uint_ptr;dword')
Local $iTimerID = _WinAPI_SetTimer(0, 0, 10, DllCallbackGetPtr($hTimerProc)) ; Does move Tooltip, even with blocking function

Do
    Sleep(100)
Until _IsPressed('1B')

MsgBox(262144, "", "Does not block")

_WinAPI_KillTimer(0, $iTimerID)
DllCallbackFree($hTimerProc)




Func _TimerProc($hWnd, $iMsg, $iTimerID, $iTime)
    #forceref $hWnd, $iMsg, $iTimerId, $iTime
    _Move_Tooltip()
EndFunc   ;==>_TimerProc

Func _Move_Tooltip()
    Local $aMousePos = MouseGetPos()
    If Not @error Then
        WinMove($hWnd_ToolTip, "", $aMousePos[0] + 20, $aMousePos[1] + 20)
    EndIf
EndFunc   ;==>_Move_Tooltip

 

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
  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...