czardas

Recent Files Logic

1 post in this topic

#1 ·  Posted (edited)

The following code is intended to illustrate a method of adding recent files to the File menu, once files have been opened or saved. This is not a UDF, nor a universal solution which fits all requirements. It is just the solution I made for myself and I thought it worth sharing. I haven't noticed much about this subject. The example only emulates opening and saving files: nothing is actually written to disk. I have not included any code for opening or saving files - only the File menu and Title bar are updated. The code is intended to illustrate the logic I used. Perhaps you can improve on it or get some ideas from this example.

#include <GUIConstants.au3>
#include <MsgBoxConstants.au3>

Example()

#Region ; core functions

Func UpdateRecentFiles(ByRef $aRecentFiles, $ahMenu, $sNewPath, $iMenuInsertPos)
    Local $iMaxFiles = UBound($aRecentFiles) -1

    For $i = 1 To $aRecentFiles[0][0] ; check to see if the path was accessed recently
        If $aRecentFiles[$i][0] = $sNewPath Then ; file is already in the list
            For $j = $i To 2 Step -1
                $aRecentFiles[$j][0] = $aRecentFiles[$j -1][0] ; push items down the list
            Next
            $aRecentFiles[1][0] = $sNewPath
            For $j = 1 To $aRecentFiles[0][0] ; update all recent file menu items
                GUICtrlSetData($aRecentFiles[$j][1], $aRecentFiles[$j][0]) ; overwrite existing control data
            Next
            Return ; the list has simply been reordered
        EndIf
    Next

    ; if we are here, then the file was not found in the recent files list
    For $i = $iMaxFiles To 2 Step -1
        $aRecentFiles[$i][0] = $aRecentFiles[$i -1][0] ; push all existing items down the list
    Next

    If $aRecentFiles[0][0] < $iMaxFiles Then
        $aRecentFiles[0][0] += 1 ; increment the number of recent files in the list
        $aRecentFiles[$aRecentFiles[0][0]][1] = GUICtrlCreateMenuItem($sNewPath, $ahMenu[0], $aRecentFiles[0][0] + $iMenuInsertPos) ; create a new control
        ; add a second spacer to the menu after the first recent item appears (one time action)
        If $aRecentFiles[0][0] = 1 Then $aRecentFiles[0][1] = GUICtrlCreateMenuItem('', $ahMenu[0], $aRecentFiles[0][0] + $iMenuInsertPos +1) ; add divider after adding the first recent item
    EndIf

    $aRecentFiles[1][0] = $sNewPath ; add the new path to the files list
    For $i = 1 To $aRecentFiles[0][0] ; update all recent file menu items
        GUICtrlSetData($aRecentFiles[$i][1], $aRecentFiles[$i][0]) ; overwrite existing control data
    Next
EndFunc ;==> UpdateRecentFiles

Func ClearRecentFiles(ByRef $aRecentFiles, $idDummyInactive)
    For $i = 0 To $aRecentFiles[0][0]
        GUICtrlDelete($aRecentFiles[$i][1]) ; delete menu items
        $aRecentFiles[$i][1] = $idDummyInactive ; render all control IDs inactivate
    Next
    $aRecentFiles[0][0] = 0 ; there are now zero files in the list
EndFunc ;==> ClearRecentFiles


Func DeleteRecentItem(ByRef $aRecentFiles, $iItem, $idDummyInactive)
    For $i = $iItem To $aRecentFiles[0][0] -1
        $aRecentFiles[$i][0] = $aRecentFiles[$i +1][0]
        GUICtrlSetData($aRecentFiles[$i][1], $aRecentFiles[$i][0])
    Next

    GUICtrlDelete($aRecentFiles[$aRecentFiles[0][0]][1])
    $aRecentFiles[$aRecentFiles[0][0]][1] = $idDummyInactive
    $aRecentFiles[0][0] -= 1

    If $aRecentFiles[0][0] = 0 Then
        GUICtrlDelete($aRecentFiles[0][1])
        $aRecentFiles[0][1] = $idDummyInactive
    EndIf
EndFunc ;==> DeleteRecentItem

#EndRegion ;==> core functions

#Region ; example code

Func Example()
    Local $sGUITitle = "New Project", $hGUI = GUICreate($sGUITitle, 500, 200) ; create GUI

    Local _ ; declare menu arrays
    $ahMenu[2] = [" File "," Options"], _ ; 1D array assigned to menus
    $ahMenuItem = GetMenuItems() ; 2D array assigned to menu item controls

    For $i = 0 To UBound($ahMenu) -1
        $ahMenu[$i] = GUICtrlCreateMenu($ahMenu[$i]) ; create menu
        For $j = 1 To $ahMenuItem[0][$i] ; add menu item controls
            $ahMenuItem[$j][$i] = GUICtrlCreateMenuItem($ahMenuItem[$j][$i], $ahMenu[$i])
        Next
    Next

    Local $iMenuInsertPos = 4 ; this is the menuentry position for the most recent file
    GUICtrlCreateMenuItem('', $ahMenu[0], $iMenuInsertPos) ; create dividor

    Local $aRecentFiles[9][2] = [[0]], _ ; zero previously stored items - assuming that the recent files list is empty at startup
    $idDummyInactive = GUICtrlCreateDummy() ; never send messages to $idDummyInactive

    For $i = 0 To 8 ; [IMPORTANT] Windows assigned a unique control ID to $idDummyInactive
        $aRecentFiles[$i][1] = $idDummyInactive ; temporarily set all (recent files list) controls to never receive messages
    Next

    GUISetState(@SW_SHOW)

    Local $msg, $sFilePath

    While 1
        $msg = GUIGetMsg()
        Switch $msg
            Case $GUI_EVENT_CLOSE, $ahMenuItem[5][0]
                ExitLoop

            Case $ahMenuItem[1][0] ; File ==> New
                New()
                WinSetTitle($hGUI , WinGetTitle($hGUI), $sGUITitle)

            Case $ahMenuItem[2][0] ; File ==> Open
                $sFilePath = Open()
                If Not @error Then
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos)
                EndIf

            Case $ahMenuItem[3][0] ; File ==> Save
                $sFilePath = Save(WinGetTitle($hGUI))

                If @error Then
                    MsgBox($MB_OK, "Action Failed", "Unable to save file")
                    ContinueLoop ; [use your own code logic]
                Else
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here
                EndIf

            Case $ahMenuItem[4][0] ; File ==> Save As
                $sFilePath = SaveAs()
                If @error Then
                    MsgBox($MB_OK, "Action Failed", "Unable to save file")
                    ContinueLoop ; [use your own code logic]
                Else
                    WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                    UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here
                EndIf

            Case $ahMenuItem[1][1] ; Options ==> Clear Recent Files
                ClearRecentFiles($aRecentFiles, $idDummyInactive)

            Case $aRecentFiles[1][1], $aRecentFiles[2][1], $aRecentFiles[3][1], $aRecentFiles[4][1], $aRecentFiles[5][1], $aRecentFiles[6][1], $aRecentFiles[7][1], $aRecentFiles[8][1]
                For $i = 1 To $aRecentFiles[0][0]
                    If $msg = $aRecentFiles[$i][1] Then
                        $sFilePath = OpenRecent($aRecentFiles[$i][0])
                        If Not @error Then
                            WinSetTitle($hGUI , WinGetTitle($hGUI), $sFilePath)
                            UpdateRecentFiles($aRecentFiles, $ahMenu, $sFilePath, $iMenuInsertPos) ; update recent files here
                        ElseIf @error = 1 Then
                            If MsgBox(BitOR($MB_YESNO, $MB_DEFBUTTON2, $MB_TASKMODAL), "Action Failed", "Unable to locate " & $aRecentFiles[$i][0] & @CRLF & _
                            "Do you want to remove this item from the menu?") = 6 Then DeleteRecentItem($aRecentFiles, $i, $idDummyInactive)
                        EndIf
                        ExitLoop ; return to the While loop
                    EndIf
                Next
        EndSwitch
    WEnd
EndFunc ;==> Example

Func New()
    ; start a new project [your code here]
EndFunc ;==> New

Func Open()
    Local $sFilePath = FileOpenDialog("Open", @DocumentsCommonDir, "All (*.*)")
    If @error Then Return SetError(1)

    ; read file [your code here]

    Return $sFilePath
EndFunc ;== Open

Func OpenRecent($sFilePath)
    If Not FileExists($sFilePath) Then Return SetError(1) ; unable to open recent file

    ; read file [your code here]

    Return $sFilePath
EndFunc ;== OpenRecent

Func Save($sFilePath)
    If Not FileExists($sFilePath) Then
        Local $sNewPath = SaveAs()
        If @error Then Return SetError(1) ; unable to save file
        Return $sNewPath
    EndIf

    ; save file [your code here]

    Return $sFilePath
EndFunc ;==> Save

Func SaveAs()
    Local $sFilePath = FileSaveDialog( "Save As", @DocumentsCommonDir, "All (*.*)")
    If @error Then Return SetError(1) ; unable to save file

    ; save new or existing file [your code here]

    Return $sFilePath
EndFunc ;==> SaveAs

Func GetMenuItems()
    Local $aMenuItem[6][2]
    $aMenuItem[0][0] = 5  ; number of items on the File Menu
    $aMenuItem[0][1] = 1  ; number of items on the Options Menu

    ; File Menu
    $aMenuItem[1][0] = "New"
    $aMenuItem[2][0] = "Open"
    $aMenuItem[3][0] = "Save"
    $aMenuItem[4][0] = "Save As"
    $aMenuItem[5][0] = "Exit"

    ; Options Menu
    $aMenuItem[1][1] = "Clear Recent Files"
    Return $aMenuItem
EndFunc ;==> GetMenuItems

#Region ; example code

Run the example, open some files and check for new menu items that are added. Then try a few other menu options and check the various changes occurring in the File menu.

Edited by czardas
2 people like this

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

  • Similar Content

    • SteveJM
      By SteveJM
      I have a working program currently driven largely through menu selections. I would like to add a toolbar where most of the tollbar button actions are basically the same as menu items, but quicker to access. I have been wading around in toolbar examples, MSDN pages etc, it's clearly going to be a bit of a slog to get everything right, including tooltips etc. I thought I would start with something simple to prove the principle.
      Using bits from the help file examples I have a small program that successfully displays a toolbar. However, what seemed like the most elegant way to deal with the button commands does not seem to work. My understanding was that a toolbar button fires a WM_COMMAND message, with the command Id set by the second parameter in the call to
      _GUICtrlToolbar_AddButton ( $hWnd, $iID, $iImage)
      so I though it would be a good idea to set this Id to the same value as my menu item Id; then it would run the same task which is what I wanted. This did not work. I am using message loop mode and would like to stick with this because some of my scripts run hardware at the same time as the gui; it is easier if I don't have to worry about code being interrupted with the hardware in an unknown state . So I added a handler for WM_COMMAND, with some cribbed display code to try and see why. The toolbar button defintely fired a WM_COMMAND message and the Id looked the same, so no explanation there.
      I guess the issue is with GUIGetMsg() which may be constructed to ignore all but a limited number of control handles, i.e. those made with the GuiCtrlCreate... commands; this is speculation.
      I would dearly love to find a tidy way to get around this. Having some controls handled in the message loop and some in a WM_COMMAND handler, performing the same task, feels ugly. I would be very grateful for further insight from someone experienced with handling a toolbar. Perhaps I should be trying to fire the menu item. I have attached a code snippet to try and illustrate the issue.
       
       
       
      ToolbarTrial.au3
    • GordonFreeman
      By GordonFreeman
      Hi, i searched some topics but cannot found a simple way to do that:
      #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> $Form1 = GUICreate("Form1", 615, 438, 192, 124) GUISetState(@SW_SHOW) ; I tried this but user reported that it work in 2006, not seems work actually $filemenu = GUICtrlCreateMenu ("&File") $fileitem = GUICtrlCreateMenuitem ("Open",$filemenu) GuiCtrlSetImage($fileitem, "shell32.dll", 4) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Also tried:
      #include <GUIConstantsEx.au3> #include <WindowsConstants.au3> $Form1 = GUICreate("Form1", 615, 438, 192, 124) GUISetState(@SW_SHOW) $filemenu = TrayCreateMenu("&File") $fileitem = TrayCreateItem("Open",$filemenu) ;GuiCtrlSetImage($fileitem, "shell32.dll", 4) ;GuiCtrlSetImage(TrayItemGetHandle($fileitem), "shell32.dll", 4) While 1 $nMsg = GUIGetMsg() Switch $nMsg Case $GUI_EVENT_CLOSE Exit EndSwitch WEnd Well.. thanks in advance!
       
      Ok. I found this working example from Yashied. If i cannot find a simple then i will go with it
      #Include <GUIConstantsEx.au3> #Include <GUIMenu.au3> #Include <Constants.au3> #Include <WinAPI.au3> #Include <WindowsConstants.au3> Opt('MustDeclareVars', 1) Global $hMenu, $hForm, $hFile = 1000, $idNew, $idExit $hForm = GUICreate('Menu', 400, 300) $hFile = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem ($hFile, 0, ' &Favorites', $idNew) _GUICtrlMenu_InsertMenuItem ($hFile, 1, '', 0) _GUICtrlMenu_InsertMenuItem($hFile, 2, ' E&xit', $idExit) $hMenu = _GUICtrlMenu_CreateMenu() _GUICtrlMenu_InsertMenuItem($hMenu, 0, '&File', 0, $hFile) _GUICtrlMenu_SetMenu($hForm, $hMenu) _GUICtrlMenu_SetItemBmp($hFile, 0, _CreateBitmapFromIcon(_WinAPI_GetSysColor($COLOR_MENU), @SystemDir & '\shell32.dll', 43, 16, 16)) _GUICtrlMenu_SetItemBmp($hFile, 2, _CreateBitmapFromIcon(_WinAPI_GetSysColor($COLOR_MENU), @SystemDir & '\shell32.dll', 27, 16, 16)) GUISetState() Do Until GUIGetMsg() = $GUI_EVENT_CLOSE Func _CreateBitmapFromIcon($iBackground, $sIcon, $iIndex, $iWidth, $iHeight) Local $hDC, $hBackDC, $hBackSv, $hIcon, $hBitmap $hDC = _WinAPI_GetDC(0) $hBackDC = _WinAPI_CreateCompatibleDC($hDC) $hBitmap = _WinAPI_CreateSolidBitmap(0, $iBackground, $iWidth, $iHeight) $hBackSv = _WinAPI_SelectObject($hBackDC, $hBitmap) $hIcon = _WinAPI_PrivateExtractIcon($sIcon, $iIndex, $iWidth, $iHeight) If Not @error Then _WinAPI_DrawIconEx($hBackDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL) _WinAPI_DestroyIcon($hIcon) EndIf _WinAPI_SelectObject($hBackDC, $hBackSv) _WinAPI_ReleaseDC(0, $hDC) _WinAPI_DeleteDC($hBackDC) Return $hBitmap EndFunc ;==>_CreateBitmapFromIcon Func _WinAPI_PrivateExtractIcon($sIcon, $iIndex, $iWidth, $iHeight) Local $hIcon, $tIcon = DllStructCreate('hwnd'), $tID = DllStructCreate('hwnd') Local $Ret = DllCall('user32.dll', 'int', 'PrivateExtractIcons', 'str', $sIcon, 'int', $iIndex, 'int', $iWidth, 'int', $iHeight, 'ptr', DllStructGetPtr($tIcon), 'ptr', DllStructGetPtr($tID), 'int', 1, 'int', 0) If (@error) Or ($Ret[0] = 0) Then Return SetError(1, 0, 0) EndIf $hIcon = DllStructGetData($tIcon, 1) If ($hIcon = Ptr(0)) Or (Not IsPtr($hIcon)) Then Return SetError(1, 0, 0) EndIf Return $hIcon EndFunc ;==>_WinAPI_PrivateExtractIcon Ok, i noticed that is a pain create submenu with _GUICtrlMenu_CreatePopup to do like in Tray, and Tray better located, then i need images/icon with Tray functions, some workaround?? 
    • nacerbaaziz
      By nacerbaaziz
      hello guys
      how are you؟
      I hope to be fine.
      I have a question  please
      how do I get the menu item that was pressed without that contains a variable؟
      For example I have a menu  of Favorites and I want the script recognizes the existing path in the pressed item
      i'll repeat to tell the item does not contain a variable
      Is there any solution
      if you want to explain more I could write an example of what I want.
      Greetings to all,
      thanks in advance
    • ModemJunki
      By ModemJunki
      I'm having a hard time figuring out how to make a menu without any tab stops for the buttons.
      I made a shell menu for BIOS flashing in WinPE and in it I don't want *any* buttons to be tab stopped. If I try to use GUICtrlSetStyle to remove the tab stops from all the buttons, the first button declared in the menu ends up the default (so the user could press the spacebar and the button is activated).
      Melba23 posted some code like below a long time ago in this post but it seems to do the same thing as GUICtrlSetStyle does.
      Any tips/help is appreciated.
      _WinAPI_SetWindowLong(GUICtrlGetHandle(-1), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle(-1), $GWL_STYLE), BitNOT($WS_TABSTOP))) #include <ButtonConstants.au3> #include <Constants.au3> #include <GUIConstantsEx.au3> #include <Misc.au3> #include <StaticConstants.au3> #include <WinAPI.au3> #include <WindowsConstants.au3> Opt("GUICloseOnESC", 0) ; prevent user from pressing escape key and accidentally cancelling the GUI HotKeySet("+!{ESC}", "_doCMD") ; shift-alt-escape for a command dialogue during GUI (lab/service use) #include <Array.au3> #include <ButtonConstants.au3> #include <Constants.au3> #include <GUIConstantsEx.au3> #include <Misc.au3> #include <StaticConstants.au3> #include <WinAPI.au3> #include <WindowsConstants.au3> GLobal $s_Cmd1, $s_Cmd2, $s_Cmd3, $s_Cmd4, $s_Cmd5 $s_Debug = 0 $s_dbgArray = 0 Opt("GUICloseOnESC", 0) ; prevent user from pressing escape key and accidentally cancelling the GUI HotKeySet("+!{ESC}", "_doCMD") ; shift-alt-escape for a command dialogue during GUI (lab/service use) $MainMenu = GUICreate("Main BIOS Menu", 600, 400, -1, -1, 0, $WS_EX_TOPMOST) $Button1 = GUICtrlCreateButton("1", 48, 64, 75, 25) $Label1 = GUICtrlCreateLabel("One", 136, 68, 186, 20) $Button2 = GUICtrlCreateButton("2", 48, 110, 75, 25) $Label2 = GUICtrlCreateLabel("Two", 136, 113, 389, 20) $Button3 = GUICtrlCreateButton("3", 48, 172, 75, 25) $Label3 = GUICtrlCreateLabel("Three", 136, 176, 186, 20) $Button4 = GUICtrlCreateButton("4", 48, 218, 75, 25) $Label4 = GUICtrlCreateLabel("Four", 136, 221, 389, 20) $Button5 = GUICtrlCreateButton("5", 48, 272, 75, 25) $Label5 = GUICtrlCreateLabel("Five", 136, 274, 280, 20) $Button6 = GUICtrlCreateButton("Cancel", 475, 340, 75, 25) $Label6 = GUICtrlCreateLabel("Click to cancel the operations", 300, 345, 175, 20) $Label0 = GUICtrlCreateLabel("Please choose one of the following 5 options:", 171, 16, 271, 20) $Label7 = GUICtrlCreateLabel("Which system do you want to set up? [Press or click 1, 2, 3, 4 or use 5 to flash]", 78, 312, 456, 20) $Group1 = GUICtrlCreateGroup("IVS", 36, 44, 513, 101) GUICtrlCreateGroup("", -99, -99, 1, 1) $Group2 = GUICtrlCreateGroup("IAS", 36, 152, 513, 101) GUICtrlCreateGroup("", -99, -99, 1, 1) _WinAPI_SetWindowLong(GUICtrlGetHandle($Button1), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle($Button1), $GWL_STYLE), BitNOT($WS_TABSTOP))) _WinAPI_SetWindowLong(GUICtrlGetHandle($Button2), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle($Button2), $GWL_STYLE), BitNOT($WS_TABSTOP))) _WinAPI_SetWindowLong(GUICtrlGetHandle($Button3), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle($Button3), $GWL_STYLE), BitNOT($WS_TABSTOP))) _WinAPI_SetWindowLong(GUICtrlGetHandle($Button4), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle($Button4), $GWL_STYLE), BitNOT($WS_TABSTOP))) _WinAPI_SetWindowLong(GUICtrlGetHandle($Button5), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle($Button5), $GWL_STYLE), BitNOT($WS_TABSTOP))) _WinAPI_SetWindowLong(GUICtrlGetHandle($Button6), $GWL_STYLE, BitAND(_WinAPI_GetWindowLong(GUICtrlGetHandle($Button6), $GWL_STYLE), BitNOT($WS_TABSTOP))) ;~ GUICtrlSetStyle($Button1, 0) ;~ GUICtrlSetStyle($Button2, 0) ;~ GUICtrlSetStyle($Button3, 0) ;~ GUICtrlSetStyle($Button4, 0) ;~ GUICtrlSetStyle($Button5, 0) ;~ GUICtrlSetStyle($Button6, 0) GUISetState(@SW_SHOW, $MainMenu) While 1 $nMsg = GUIGetMsg() Select Case $nMsg = $GUI_EVENT_CLOSE Or $nMsg = $Button6 GUISetState(@SW_HIDE, $MainMenu) MsgBox(4096, "Cancel", "Operation cancelled. Please remove the media and click OK to reboot.") Exit Case $nMsg = $Button1 Or _IsPressed("31") Or _IsPressed("61") ; is the button, keyboard number, or keypad number pressed? _doRun(@ScriptDir & "\" & $s_Cmd1) Case $nMsg = $Button2 Or _IsPressed("32") Or _IsPressed("62") _doRun(@ScriptDir & "\" & $s_Cmd2) Case $nMsg = $Button3 Or _IsPressed("33") Or _IsPressed("63") _doRun(@ScriptDir & "\" & $s_Cmd3) Case $nMsg = $Button4 Or _IsPressed("34") Or _IsPressed("64") _doRun(@ScriptDir & "\" & $s_Cmd4) Case $nMsg = $Button5 Or _IsPressed("35") Or _IsPressed("65") _doRun(@ScriptDir & "\" & $s_Cmd5) EndSelect WEnd ;~ ;============================================================================= ;~ ;==================================Functions================================== ;~ ;============================================================================= ;~ ;============================================================================= ;~ ; Run function for batch or executable files, hides main menu when running ;~ ;============================================================================= Func _doRun($RunFile) GUISetState(@SW_HIDE, $MainMenu) RunWait(@ComSpec & " /c " & $RunFile) GUISetState(@SW_SHOW, $MainMenu) EndFunc ;==>_doRun ; -------------------------------------------------- ; Run a command prompt for lab/field troubleshooting ; -------------------------------------------------- Func _doCMD() HotKeySet("+!{ESC}") ; deactivate hotkey to prvent spawning multiple cmd boxes with it GUISetState(@SW_HIDE, $MainMenu) ; hide the GUI RunWait(@ComSpec & " /c " & "cmd.exe", "", @SW_SHOW) GUISetState(@SW_SHOW, $MainMenu) ; bring the GUI back up HotKeySet("+!{ESC}", "_doCMD") ; reset the hotkey shift-alt-escape for a command dialogue (emergency use) EndFunc ;==>_doCMD  
    • k4rl3on
      By k4rl3on
      Hi,
      i'm looking for a way to get the text of selected item from a popup menu
      here is my script, it works fine for non-Explorer.exe window (tested on SciTE on both context menu and menubar menu), but fails on displayed menu on Desktop...
      Local $hDLL, $hWinEventProc, $hHook Local $LastMenuItem[5] ;Save last menu item propieties Local $EVENT_Min = 0x0006 ;An MSAA event indicating that a pop-up menu was displayed. Local $EVENT_Max = 0x0007 ;An MSAA event indicating that a pop-up menu was closed. Local $hWinEventProc = DllCallbackRegister("_WinEventProc", "none", "hwnd;int;hwnd;long;long;int;int") If Not @error Then OnAutoItExitRegister("OnAutoItExit") Else MsgBox(16 + 262144, "Error", "DllCallbackRegister(_WinEventProc) did not succeed.") Exit EndIf $hHook = _SetWinEventHook($EVENT_Min, $EVENT_Max) If @error Then MsgBox(16 + 262144, "Error", "_SetWinEventHook() did not succeed.") Exit EndIf While True Sleep(250) ;function that get last menu item propieties _PopupMenuItem() WEnd Func _WinEventProc($hHook, $iEvent, $hWnd, $idObject, $idChild, $iEventThread, $iEventTime) Switch $iEvent Case 6 ConsoleWrite("(i) pop-up menu was displayed..."&@CRLF) Case 7 _SelectedMenuItem() ConsoleWrite("(i) pop-up menu was closed..."&@CRLF) EndSwitch EndFunc ;==>_WinEventProc Func _SetWinEventHook($iEventMin, $iEventMax) Local $aRet Local Const $WINEVENT_OUTOFCONTEXT = 0x0 Local Const $WINEVENT_SKIPOWNPROCESS = 0x2 $aRet = DllCall("User32.dll", "hwnd", "SetWinEventHook", _ "uint", $iEventMin, _ "uint", $iEventMax, _ "hwnd", 0, _ "ptr", DllCallbackGetPtr($hWinEventProc), _ "int", 0, _ "int", 0, _ "uint", BitOR($WINEVENT_OUTOFCONTEXT, $WINEVENT_SKIPOWNPROCESS)) If @error Then Return SetError(@error, 0, 0) Return $aRet[0] EndFunc ;==>_SetWinEventHook Func OnAutoItExit() If $hWinEventProc Then DllCallbackFree($hWinEventProc) If $hHook Then DllCall("User32.dll", "int", "UnhookWinEvent", "hwnd", $hHook) EndFunc ;==>OnAutoItExit Func _PopupMenuItem() Local $aRect, $hMenu = False, $MenuItem[9] Local $hDesktop = _WinAPI_GetDesktopWindow() Local $hChild = _WinAPI_GetWindow( $hDesktop, 5 ), $i = 0 Local $hWindow = WinGetHandle("[ACTIVE]") Local $hChild = _WinAPI_GetWindow($hDesktop, 5) Local $i = 0 While $hChild And $i < 5 If _WinAPI_GetClassName( $hChild ) = "#32768" Then $hMenu = _SendMessage( $hChild, 0x01E1 ) ExitLoop EndIf $hChild = _WinAPI_GetWindow( $hChild, 2 ) $i += 1 WEnd If $hMenu Then Local $iCount = _GUICtrlMenu_GetItemCount($hMenu), $sText For $i = 0 To $iCount - 1 If $i = _GUICtrlMenu_MenuItemFromPoint($hWindow, $hMenu) Then If _GUICtrlMenu_GetItemEnabled($hMenu, $i) Then $MenuItem[0] = $hWindow $MenuItem[1] = $hMenu $MenuItem[2] = $i $MenuItem[3] = _GUICtrlMenu_GetItemText($hMenu, $i ) $MenuItem[4] = _GUICtrlMenu_GetItemRectEx($hWindow, $hMenu, $i) If $LastMenuItem[0] <> $MenuItem[0] Or $LastMenuItem[1] <> $MenuItem[1] Or $LastMenuItem[2] <> $MenuItem[2] Then $LastMenuItem = $MenuItem ConsoleWrite("* mouse over item: "&$LastMenuItem[3]&@CRLF) EndIf EndIf ExitLoop EndIf Next EndIf EndFunc Func _SelectedMenuItem() Local $tPOINT = _WinAPI_GetMousePos() ConsoleWrite("(i) last mouse position: "&DllStructGetData($tPoint, "X")&":"&DllStructGetData($tPoint, "Y")&@CRLF) If _WinAPI_PtInRect($LastMenuItem[4], $tPOINT) Then ConsoleWrite("-> selected item: "&$LastMenuItem[3]&@CRLF) Else ConsoleWrite("(i) last menu item position Rect: "&DllStructGetData($LastMenuItem[4], "Left")&">"&DllStructGetData($LastMenuItem[4], "Right")&":"&DllStructGetData($LastMenuItem[4], "Top")&"V"&DllStructGetData($LastMenuItem[4], "Bottom")&@CRLF) EndIf EndFunc ; Functions From Include Directory Func _WinAPI_GetWindow($hWnd, $iCmd) Local $aResult = DllCall("user32.dll", "hwnd", "GetWindow", "hwnd", $hWnd, "uint", $iCmd) If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] EndFunc Func _WinAPI_GetClassName($hWnd) If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd) Local $aResult = DllCall("user32.dll", "int", "GetClassNameW", "hwnd", $hWnd, "wstr", "", "int", 4096) If @error Or Not $aResult[0] Then Return SetError(@error, @extended, '') Return SetExtended($aResult[0], $aResult[2]) EndFunc Func _GUICtrlMenu_GetItemCount($hMenu) Local $aResult = DllCall("user32.dll", "int", "GetMenuItemCount", "handle", $hMenu) If @error Then Return SetError(@error, @extended, -1) Return $aResult[0] EndFunc Func _SendMessage($hWnd, $iMsg, $wParam = 0, $lParam = 0, $iReturn = 0, $wParamType = "wparam", $lParamType = "lparam", $sReturnType = "lresult") Local $aResult = DllCall("user32.dll", $sReturnType, "SendMessageW", "hwnd", $hWnd, "uint", $iMsg, $wParamType, $wParam, $lParamType, $lParam) If @error Then Return SetError(@error, @extended, "") If $iReturn >= 0 And $iReturn <= 4 Then Return $aResult[$iReturn] Return $aResult EndFunc Func _GUICtrlMenu_GetItemRectEx($hWnd, $hMenu, $iItem) Local $tRECT = DllStructCreate("struct;long Left;long Top;long Right;long Bottom;endstruct") Local $aResult = DllCall("user32.dll", "bool", "GetMenuItemRect", "hwnd", $hWnd, "handle", $hMenu, "uint", $iItem, "struct*", $tRECT) If @error Then Return SetError(@error, @extended, 0) Return SetExtended($aResult[0], $tRECT) EndFunc Func _GUICtrlMenu_GetItemText($hMenu, $iItem, $bByPos = True) Local $iByPos = 0 If $bByPos Then $iByPos = 0x00000400 Local $aResult = DllCall("user32.dll", "int", "GetMenuStringW", "handle", $hMenu, "uint", $iItem, "wstr", "", "int", 4096, "uint", $iByPos) If @error Then Return SetError(@error, @extended, 0) Return SetExtended($aResult[0], $aResult[3]) EndFunc Func _GUICtrlMenu_GetItemInfo($hMenu, $iItem, $bByPos = True) Local $tInfo = DllStructCreate("uint Size;uint Mask;uint Type;uint State;uint ID;handle SubMenu;handle BmpChecked;handle BmpUnchecked;" & _ "ulong_ptr ItemData;ptr TypeData;uint CCH;handle BmpItem") DllStructSetData($tInfo, "Size", DllStructGetSize($tInfo)) DllStructSetData($tInfo, "Mask", 0x0000003F) Local $aResult = DllCall("user32.dll", "bool", "GetMenuItemInfo", "handle", $hMenu, "uint", $iItem, "bool", $bByPos, "struct*", $tInfo) If @error Then Return SetError(@error, @extended, 0) Return SetExtended($aResult[0], $tInfo) EndFunc Func _GUICtrlMenu_GetItemStateEx($hMenu, $iItem, $bByPos = True) Local $tInfo = _GUICtrlMenu_GetItemInfo($hMenu, $iItem, $bByPos) Return DllStructGetData($tInfo, "State") EndFunc Func _GUICtrlMenu_GetItemEnabled($hMenu, $iItem, $bByPos = True) Return BitAND(_GUICtrlMenu_GetItemStateEx($hMenu, $iItem, $bByPos), 0x00000002) = 0 EndFunc Func _GUICtrlMenu_MenuItemFromPoint($hWnd, $hMenu, $iX = -1, $iY = -1) If $iX = -1 Then $iX = _WinAPI_GetMousePosX() If $iY = -1 Then $iY = _WinAPI_GetMousePosY() Local $aResult = DllCall("user32.dll", "int", "MenuItemFromPoint", "hwnd", $hWnd, "handle", $hMenu, "int", $iX, "int", $iY) If @error Then Return SetError(@error, @extended, -1) Return $aResult[0] EndFunc Func _WinAPI_GetMousePosX($bToClient = False, $hWnd = 0) Local $tPoint = _WinAPI_GetMousePos($bToClient, $hWnd) If @error Then Return SetError(@error, @extended, 0) Return DllStructGetData($tPoint, "X") EndFunc Func _WinAPI_GetMousePosY($bToClient = False, $hWnd = 0) Local $tPoint = _WinAPI_GetMousePos($bToClient, $hWnd) If @error Then Return SetError(@error, @extended, 0) Return DllStructGetData($tPoint, "Y") EndFunc Func _WinAPI_PtInRect(ByRef $tRECT, ByRef $tPoint) Local $aResult = DllCall("user32.dll", "bool", "PtInRect", "struct*", $tRECT, "struct", $tPoint) If @error Then Return SetError(@error, @extended, False) Return $aResult[0] EndFunc Func _WinAPI_GetMousePos($bToClient = False, $hWnd = 0) Local $iMode = Opt("MouseCoordMode",1) Local $aPos = MouseGetPos() Opt("MouseCoordMode", $iMode) Local $tPoint = DllStructCreate("struct;long X;long Y;endstruct") DllStructSetData($tPoint, "X", $aPos[0]) DllStructSetData($tPoint, "Y", $aPos[1]) If $bToClient And Not _WinAPI_ScreenToClient($hWnd, $tPoint) Then Return SetError(@error + 20, @extended, 0) Return $tPoint EndFunc Func _WinAPI_ScreenToClient($hWnd, ByRef $tPoint) Local $aResult = DllCall("user32.dll", "bool", "ScreenToClient", "hwnd", $hWnd, "struct*", $tPoint) If @error Then Return SetError(@error, @extended, False) Return $aResult[0] EndFunc Func _WinAPI_GetDesktopWindow() Local $aResult = DllCall("user32.dll", "hwnd", "GetDesktopWindow") If @error Then Return SetError(@error, @extended, 0) Return $aResult[0] EndFunc Cause of the problem: _WinAPI_PtInRect fails on Explorer.exe windows because _GUICtrlMenu_GetItemRectEx returns relative rectangle bounds to the menu (i think) where always X1 = 0.
      Any suggestions to make this script works on all menus?
      Thanks