Jump to content

Recommended Posts

Good morning everyone :)

I'm trying to build, and, first of all, understand, how a Virtual ListView works.
By now, I've already downloaded all the samples from Virtual List View samples provided by @LarsJ, but I still don't understand few things.
What I understood by now, is that the Virtual ListView doesn't need to be filled by object like Items, because it use directly the source to display the data.
I've already understood that the $LVN_ODCACHEHINT Notification Code does provide the range of the items to be displayed, but I don't understand how to fill $iFrom and $iTo which are in the $NMLVCACHEHINT, or who fill them.
And, "finally", I understood that the $LVN_GETDISPINFO is used to fill the ListView with "items".
Can someone please explain to me how do these Virtual ListView controls work?
Thanks in advance.


Best Regards.


 

Edited by FrancescoDiMuro

Click here to see my signature:

Spoiler

ALWAYS GOOD TO READ:

 

Link to post
Share on other sites

You should start by reading the Microsoft documentation.

Then I suggest you extract all data from the database into an array with _SQLite_GetTable2d. Compared to virtual listviews, an array is the same as a cache. When all data is stored in a cache, there is no need to use the caching technique.

So look at the array examples where only LVN_GETDISPINFO notifications are used.


LVN_ODCACHEHINT notifications are used to fill the cache (the array). When all rows are stored in the cache you don't need them.

LVN_GETDISPINFO notifications contains information about which items/subitems in the listview that should be updated. A LVN_GETDISPINFO notification is send to the listview for each cell to be updated. Then the cell text is read in the cache (the array) and displayed in the cell.


When you receive a DllStruct through the $lParam parameter in the WM_NOTIFY function, data in the DllStruct is filled in by code in the operating system.

Edited by LarsJ
Link to post
Share on other sites

Hey @LarsJ:)
Thanks for your tips.
I was trying to build my own Virtual ListView, but, I have an errore in my script.
I'd like to show you step by step what I've done...

  1. Catch the $LVN_ODCACHEHINT Notification Code, and create the Struct $tagNMLVCACHEHINT, in which there are information about ListView Items stored in the cache ( and it is iFrom = 0 and iTo = 23 );
  2. Query the DB using iFrom and iTo;
  3. Catch the $LVN_GETDISPINFO Notification Code, and create the Struct $tagNMLVDISPINFO, in which set the Text of the Item, basing on the Item ( row ) and SubItem ( column ) parameters obtained from the Struct  $tagNMLVDISPINFO... 
    But, when I'm in this Notification Code ( LVN_GETDISPINFO ), and I'm setting Text and TextMax for each LV Item, I'm not understaing this passage ( which generates an error ):
    ; Information about how many items should be displayed in the VLV
    $intMaxItemsCount = 28558
    ; Sending $LVM_SETITEMCOUNT... 
    
    Local $tNMLVDISPINFO = DllStructCreate($tagNMLVDISPINFO, $lParam)
    
    ; Since arrResult[0][n] contains Table headers from the Query, we start from 1 to UBound($arrResult) ?
    Local $intItemRow = DllStructGetData($tNMLVDISPINFO, "Item") -  $intFrom + 1 ; Equals to [0...$intMaxItemsCount] + 0 + 1 = 28559 ?
    
    Local $intItemColumn = DllStructGetData($tNMLVDISPINFO, "SubItem") ; From 0 to 7
    
    ConsoleWrite("$intItemRow: " & $intItemRow & @CRLF & _
                 "$intItemColumn: " & $intItemColumn & @CRLF)
    
    If $intItemRow > 0 And $intItemRow < $intMaxItemsCount + 1 Then ; intItemRow Will be > 0 and < 28559 since it meets the last LV Item, isn't it?
    
        DllStructSetData($tItemText, 1, $arrResult[$intItemRow][$intItemColumn])
        DllStructSetData($tNMLVDISPINFO, "Text", $ptrItemText)
        DllStructSetData($tNMLVDISPINFO, "TextMax", StringLen($tItemText))
    
    EndIf

    The error is correctly:

    (704) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.:
    DllStructSetData($tItemText, 1, $arrResult[$intItemRow][$intItemColumn])
    DllStructSetData($tItemText, 1, ^ ERROR

     

Thanks for your help :)

EDIT:

Now it works! :)
; Information about how many items should be displayed in the VLV
$intMaxItemsCount = 28558
; Sending $LVM_SETITEMCOUNT... 

Local $tNMLVDISPINFO = DllStructCreate($tagNMLVDISPINFO, $lParam)

; Since arrResult[0][n] contains Table headers from the Query, we start from 1 to UBound($arrResult) ?
Local $intItemRow = DllStructGetData($tNMLVDISPINFO, "Item") -  $intFrom + 1 ; Equals to [0...$intMaxItemsCount] + 0 + 1 = 28559 ?

Local $intItemColumn = DllStructGetData($tNMLVDISPINFO, "SubItem") ; From 0 to 7

ConsoleWrite("$intItemRow: " & $intItemRow & @CRLF & _
             "$intItemColumn: " & $intItemColumn & @CRLF)

; Error ===> If $intItemRow > 0 And $intItemRow < $intMaxItemsCount + 1 Then ; intItemRow Will be > 0 and < 28559 since it meets the last LV Item, isn't it?

If $intItemRow > 0 And $intItemRow < $intRows + 1 Then ; $intRows is the number of Rows returned by the query!



    DllStructSetData($tItemText, 1, $arrResult[$intItemRow][$intItemColumn])
    DllStructSetData($tNMLVDISPINFO, "Text", $ptrItemText)
    DllStructSetData($tNMLVDISPINFO, "TextMax", StringLen($tItemText))

EndIf


Best Regards.
Edited by FrancescoDiMuro

Click here to see my signature:

Spoiler

ALWAYS GOOD TO READ:

 

Link to post
Share on other sites

Good morning @LarsJ:)
I was tidying my code a little bit in my WM_NOTIFY function, and I decided to make these changes...
This is the "style" of the Switch..Case...EndSwitch statements beforethe tidy:

Switch $intMessageCode ; Which code has been sent from a ListView
    
    Case $NM_DBLCLK
        Switch $hdlWindowFrom ; From which ListView the Notification Code is sent
        
            Case $hdlListView1
                ConsoleWrite("NM_DBLCLK" & @CRLF)
        EndSwitch
        
        
    Case $LVN_COLUMNCLICK
        Switch $hdlWindowFrom
        
            Case $hdlListView2
                ConsoleWrite("LVN_COLUMNCLICK" & @CRLF) ; It works!
        EndSwitch
        
    Case $LVN_ODCACHEHINT
        Switch $hdlWindowFrom
        
            Case $hdlListView2
                ConsoleWrite("LVN_ODCACHEHINT" & @CRLF) ; It works!
        EndSwitch
        
    Case $$LVN_GETDISPINFO
        Switch $hdlWindowFrom
        
            Case $hdlListView2
                ConsoleWrite("LVN_GETDISPINFO" & @CRLF) ; It works!
        EndSwitch

EndSwitch

And this is after the tidy ( I switched first the ListView which sents the Notification Code, and then the Notification Code sent ):

Switch $hdlWindowFrom       ; From which ListView the Notification Code is sent
    Case $hdlListView1
    
        Switch $intMessaggeCode ; Which code has been sent from this particular ListView
            
            Case $NM_DBLCLK
                ConsoleWrite("NM_DBLCLK" & @CRLF) ; It doesn't work!
        
        EndSwitch ; Message Code
      
    Case hdlListView2
        Switch $intMessaggeCode ; Which code has been sent from this particular ListView
            
            Case $LVN_COLUMNCLICK
                ConsoleWrite("LVN_COLUMNCLICK" & @CRLF) ; It works!
                
            Case $LVN_ODCACHEHINT
                ConsoleWrite("LVN_ODCACHEHINT" & @CRLF) ; It doesn't work!
                
            Case $LVN_GETDISPINFO
                ConsoleWrite("LVN_GETDISPINFO" & @CRLF) ; It doesn't work!
        
        EndSwitch ; Message Code
        
EndSwitch ; Window From

Some Notification Codes work and some not.
Can you please tell me if is this normal? :)
Thank you very much :)


Best Regards.

 

Click here to see my signature:

Spoiler

ALWAYS GOOD TO READ:

 

Link to post
Share on other sites

The listviews receives notifications.

Notifications related to the virtual listview should be placed in top of the Switch statements. I'm sure you are clicking very fast. But not that fast. Put your click notifications in the bottom.

And you have remembered to add the $LVS_OWNERDATA (virtual listview) style to the listview?

Your code should look like this:

Switch $hdlWindowFrom       ; From which ListView the Notification Code is received
    Case hdlListView2
        Switch $intMessaggeCode ; Which code has been received by this particular ListView
            Case $LVN_GETDISPINFO
                ConsoleWrite("LVN_GETDISPINFO" & @CRLF) ; It doesn't work!

            Case $LVN_ODCACHEHINT
                ConsoleWrite("LVN_ODCACHEHINT" & @CRLF) ; It doesn't work!

            Case $LVN_COLUMNCLICK
                ConsoleWrite("LVN_COLUMNCLICK" & @CRLF) ; It works!
        EndSwitch ; Message Code

    Case $hdlListView1
        Switch $intMessaggeCode ; Which code has been received by this particular ListView
            Case $NM_DBLCLK
                ConsoleWrite("NM_DBLCLK" & @CRLF) ; It doesn't work!
        EndSwitch ; Message Code
EndSwitch ; Window From

 

Notifications that do not work usually result from lack of understanding or bad code.


Post all your code and a test database and I'll take a look.

Edited by LarsJ
Link to post
Share on other sites

Hey @LarsJ:)
Thanks for your reply.
Sorry if I ask always for Help, but this thing is stoping me by keep going with the work I have to do, and so, I always search for explanations! :)
By the way, let me reply you "statement by statement"...

Quote

And you have remembered to add the $LVS_OWNERDATA (virtual listview) style to the listview?

Yes, I did :)

16 minutes ago, LarsJ said:

Notifications that do not work are usually result from lack of understanding or bad code.

I just switched the two switches ( before it was ):

Switch $intMessageCode
    Case $LVN_ODCACHEHINT
        Switch $hdlWindowFrom
            Case $hdlListView
        
        EndSwitch
EndSwitch

And after it is:

Switch $hdlWindowFrom
    Case $hdlListView
        Switch $intMessageCode
            Case $LVN_ODCACHEHINT
        
        EndSwitch
EndSwitch

And this little change, it's the cause of the issue.
 

16 minutes ago, LarsJ said:

Notifications related to the virtual listview should be placed in top of the Switch statements. I'm sure you are clicking very fast. But not that fast. Put your click notifications in the bottom.

The sample script I've made to understand ListViews ( and VLV too ) has $LVN_ODCACHEHINT and $LVN_GETDISPINFO at the bottom of the Switch statement.

At the moment, I can't post anything ( neither a similiar script ).
I have a distorsion ( I should stay in bed ), and I'm making a big effort to write here.
Can you please explain this to me?
Thanks again :ILA2:

Best Regards.

Edited by FrancescoDiMuro

Click here to see my signature:

Spoiler

ALWAYS GOOD TO READ:

 

Link to post
Share on other sites
Link to post
Share on other sites

@LarsJ
Little Update: I found what were causing the Virtual ListView to not be created.
I'm digging a little bit more to understand how to "free" the cache now, since I think ( as a non-expert I am ), the cache has to be free after some work with it.
I want to thank you to poiting me to the right direction, and I'd like to let everyone knows, that your help made me happier, 'cause from your help, I ( almost, I miss few things ) understood how Virtual ListView can be created, and this is amazing, since, from now on, I can use them, and they're really powerful.
So, I said all these stuffs because I want to spur everyone that help people is amazing, and it gives back ( hopefully ),a good result, both phisycal and moral.
I hope you got this :)
Have a wonderful day.


Best Regards.

Click here to see my signature:

Spoiler

ALWAYS GOOD TO READ:

 

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.

  • Similar Content

    • By Mbee
      I've been researching this topic for about three weeks, but I'm still very confused. What I'm trying to do is to add a menu item to an arbitrary, third- party applications title bar system menu and process user events relating to it. The main problem I'm having is that there appears to be  a wealth of UDFs and other techniques to accomplish this, and I can't figure out which to concentrate on as a good solution.  For example, I've looked closely at @BrewManNHB 's  GUIRegisterMsg replacement for GUICtrlSetOnEvent and GUIGetMsg, and @LarsJJ 's stunning product GUIRegisterMsg20 UDF, as well as standard build- in AutoIt functions and WinAPI functions, But before I ask for recommendations, I'll elaborate my needs in more detail...

      I'm nearing completion of my muilt-monitor UDF and AutoWinSwitch stand-alone application, but one tricky issue remains. The purpose of the application is to automatically move newly arisen windows and context menus and the like from a monitor which is being used for some other purpose (like playing a game or watching videos) to the other monitor so that the user can see them and act upon them.
      The feature I need all this for is to allow the user to prevent a user-designated window from being moved, even though it exists on the "blocked" or used monitor so that it would normally require moving. In my testing, I've run into this situation often enough that a solution is necessary.
      So what I've done so far is to call _GUICtrlMenu_GetSystemMenu() for the window in question, then called  _GUICtrlMenu_AddMenuItem() to add a new menu item named "Pinned". This is working perfectly, but of course that's far from sufficient. Because obviously I need to establish some kind of event handler so that when the user selects that item, I can then toggle or alter the state and style of that item to show whether it's pinned or not.
      I've tried simply adding an on-event handler when I created the menu item, but even with the proper "Opt" value for On Event mode, the handler is never invoked.
      I also tried BrewmanNH's UDF and then LarsJ's, but even though I get no errors, again the handlers are never invoked.
      Finally, before I post the code of my last try using -- probably erroneously -- LarsJ's GUIRegisterMsg20 UDF, may I please ask for each of your recommendations and advice as to what approach you would favor? It's clear I don't know the most apt solution. Thanks!
      When you examine my code snippets, please overlook the mistakes and false starts that still remain there until I'm in the final stages of cleanup. Note also that the code depends vitally on Maps, so it will only run properly on the latest Beta version of AutoIt. But I guess that's actually irrelevant, since the posted code won't pass syntax checking...
       
      #AutoIt3Wrapper_Res_Fileversion=1.3 #AutoIt3Wrapper_icon=iPulse.ico #AutoIt3Wrapper_UseX64=Y #AutoIt3Wrapper_Run_After=copy "%out%" AutoWinSwitch.exe #include <WinAPIGdi.au3> #include <APIGdiConstants.au3> #include <GUIConstants.au3> #include <Misc.au3> #include <MsgBoxConstants.au3> #include <Process.au3> #include <String.au3> #include <StringConstants.au3> #include <Timers.au3> #include <WinAPIProc.au3> #include <GUIConstantsEx.au3> #include <GuiMenu.au3> ;~ #include <TrayConstants.au3> #include "LocalIncludes\GetMonitorTables_v1.1.au3" #include "LocalIncludes\_GetHwndFromPID.au3" #include "LocalIncludes\GUIRegisterMsg20.au3" AutoItSetOption("MustDeclareVars", 1) ; Variables must be declared before use AutoItSetOption("GUIOnEventMode", 1) ; We will temporarily disable this later for StateChange GUI AutoItSetOption("WinTitleMatchMode", 1) ; 1 = Matches partial titles from the start AutoItSetOption("MouseCoordMode", 0) ; 0 = Coords are relative to window (not screen) AutoItSetOption("GUICloseOnESC", 1 ) ; Close if user presses ESC AutoItSetOption("TrayOnEventMode", 0) ; We will enable this later ; ; Const $C_ThisAppName = "AutoWinSwitch" Const $C_VersionStr = "v1.3 Beta" Global Const $C_Title = $C_ThisAppName & " " & $C_VersionStr ; ;- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Const $C_AutoSwitchRegRootKey = "HKCU\Environment" Const $C_AutoSwitchRegValName = "AUTOWINSWITCHSTATE" Const $C_PathToTestApp = "B:\AutoIt Scripts\!ISN My Folder\My Projects\NFS Mounter\NFS_Mounter.exe" Const $C_StateDisabled=1, $C_StateBlockPrimary=2, $C_StateBlockSecondary=3, $C_StateUnset=4, $C_NumStateStrs=8 Const $C_ValidStateStrAra[$C_NumStateStrs] = [ "DIS", "TOP", "TOS", "DISABL", "BLOCKN", "BLOCKS", "BLOCKP", "NOTSET" ] Const $C_MoveTblNumCols=8 Global Enum $C_MoveTblHdlIx, $C_MoveTblMonIxIx, $C_MoveTblTitleIx, $C_MoveTblClassIx, $C_MoveTblProcIx, _ $C_MoveTblTypeIx, $C_MoveTblWinLocIx, $C_MoveTblDimsIx Const $C_NumDimCols=6 Global Enum $C_DimsWidthIx, $C_DimsHeightIx, $C_DimsLeftIx, $C_DimsTopIx, $C_DimsRightIx, $C_DimsBottomIx Const $C_CalcAbsLeft=1, $C_CalcAbsRight=2, $C_CalcAbsTop=3, $C_CalcAbsBot=4 Const $C_WinTypeMain=1, $C_WinTypePopup=2 Const $C_OnOpenMon = 1, $C_OnBlockedMon = 2, $C_SpansMon = 3 Const $C_TitlesToIgnoreAraCount = 7 Const $C_TitlesToIgnoreAra[$C_TitlesToIgnoreAraCount] = [ "Microsoft Text Input", _ "Program Manager", _ "*AutoIt", _ "Settings", _ "WorkerW", _ "*Debugger", _ "*DBUG" ] ;- - - - - - Global $G_GenMonTbl[$Gc_GMTblNumCols], $G_MMonitorTbl[1][$Gc_MMTblNumCols] Global $G_CurWinMap[], $G_PrevWinMap[], $G_PinnedMap[], $L_EmptyMap[] Global $G_MoveTimerAra[1][2], $G_MoveTimerAraIdx=-1, $G_AWS_HidWinHdl = -1 Global $G_MutexHdl, $G_WeOwnMutex, $G_Gid_PriRadioBtn, $G_Gid_SecRadioBtn, $G_Gid_DisabledRadioBtn Global $G_MonIxToShowGUI = 0 Global $G_SwitchStateStr, $G_SwitchStateCode, $G_SwitchStateChanged = False Global $G_NumValidMons=0, $G_TestAppPID, $G_TestAppWinHdl, $G_TestAppTitle="NFS_Mounter", $G_TestAppPosAra[2] ;~ $WM_CLOSE = 0x0010 ;~ $WM_MENUCOMMAND = 0x0126 ;~ $WM_MENUSELECT = 0x011F ;~ $WM_MOVE = 0x0003 ;~ $WM_MOVING = 0x0216 Global Enum $C_UseExtWinHdl, $C_UseExtCtrlHdl Global $G_SysWinMsgCodesRegAraCount = 5 Global $G_SysWinMsgCodesRegAra[$G_SysWinMsgCodesRegAraCount][3] = [ _ [ $C_UseExtWinHdl, $WM_CLOSE, __ExtCloseCmdHandler ], _ [ $C_UseExtWinHdl, $WM_MOVE, __ExtMoveWinHandler ], _ [ $C_UseExtWinHdl, $WM_MOVING, __ExtMovIngWinHandler ], _ [ $C_UseExtCtrlHdl, $WM_MENUCOMMAND, __ExtMenuCmdHandler ], _ [ $C_UseExtCtrlHdl, $WM_MENUSELECT, __ExtMenuSelectHandler ] ] ; ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ; Local $L_WorkDir = "B:\ISN AutoIt Studio\Projects\AutoWinSwitch\AutoWinSwitch Release 1.3 Beta" $L_RptPath = _UniqueOutFilePath( "WinReport", "txt", $L_WorkDir, 99 ) If @error <> 0 Then MsgBox($MB_OK, $C_Title, "Error: Unable to generate unique output filepath -- Exiting...") Exit 0 EndIf Global $G_RptFileHdl = FileOpen( $L_RptPath, $FO_OVERWRITE + $FO_UTF8_NOBOM ) If $G_RptFileHdl = -1 Then MsgBox($MB_OK, $C_Title, "ERROR - Unable to open report file -- Exiting...") Exit EndIf OnAutoItExitRegister("_ExitCapture") FileWriteLine( $G_RptFileHdl, "-- Begin run --" & @CRLF ) ; ;=================================================================================================================== ; ; ; The following code ensures that no other instance of AutoWinSwitch is running. ; ;~ $G_RptFileHdl = FileOpen( $L_RptPath, "About to check Mutex") ;~ $G_MutexHdl = _Singleton( "Global\AutoWinSwitchMUTEX", 3 ) ;~ If $G_MutexHdl = 0 Then ;~ FileWriteLine( $G_RptFileHdl, "We do NOT own the Mutex -- Exiting" ) ;~ MsgBox($MB_OK + $MB_TOPMOST, $C_Title, "Another copy of this tool is already running -- This copy will now exit") ;~ Exit 47 ;~ EndIf $G_WeOwnMutex = True ; ; Setup all the information about whatever monitors are present in this system ; ;~ <snip> <snip> <snip> ; ;=================================================================================================================== ; $G_SwitchStateStr = RegRead( $C_AutoSwitchRegRootKey, $C_AutoSwitchRegValName ) If @error <> 0 Then FileWriteLine( $G_RptFileHdl, "Error reading Switch State -- Setting Reg to DIS for now" ) $L_Stat = RegWrite( $C_AutoSwitchRegRootKey, $C_AutoSwitchRegValName, "REG_SZ", "DIS" ) ; Set registry to disabled $G_SwitchStateStr = "NOTSET" $G_SwitchStateCode = $C_StateUnset Else FileWriteLine( $G_RptFileHdl, "Switch State read from Registry is: " & $G_SwitchStateStr ) EndIf $G_SwitchStateCode = _StateStrToCode( $G_SwitchStateStr ) If @error <> 0 Then FileWriteLine( $G_RptFileHdl, "_StateStrToCode() Returned @error = " & @error & " -- Exiting") MsgBox($MB_OK + $MB_TOPMOST, $C_Title, "AutoWinSwitch - ERROR: Unrecognizable State in Registry - Exiting") Exit 6 EndIf $L_VerboseStateStr = _StateCodeToStr( $G_SwitchStateCode, "Verbose" ) If @error <> 0 Then FileWriteLine( $G_RptFileHdl, "_StateCodeToStr() Returned @error = " & @error & " -- Exiting") MsgBox($MB_OK + $MB_TOPMOST, $C_Title, "_StateCodeToStr() Returned @error = " & @error & " -- Exiting") Exit 7 EndIf $L_SpashMonIdx = $L_PrimaryMonTblIdx If $G_SwitchStateCode = $C_StateBlockPrimary Then $L_SpashMonIdx = $L_SecondaryMonTblIdx EndIf $L_SplashStr = "Initial State: " & $L_VerboseStateStr $L_SplashStrLen = StringLen( $L_SplashStr ) $L_SplashX = $G_MMonitorTbl[$L_SpashMonIdx][$Gc_MMTblCenterXIx] - Floor($L_SplashStrLen / 2) $L_SplashY = $G_MMonitorTbl[$L_SpashMonIdx][$Gc_MMTblTopIx] + 4 ;~ For $i = 1 To 2 $L_SplashHdl = SplashTextOn( "AutoWinSwitch Current State", $L_SplashStr, 400, 40, $L_SplashX, $L_SplashY, _ $DLG_CENTERONTOP + $DLG_MOVEABLE, "Lucida Sans Unicode", 11, $FW_SEMIBOLD ) Sleep( 1000 ) SplashOff() ;~ Sleep( 500 ) ;~ Next $L_OrigStateCode = $G_SwitchStateCode If ($L_OrigStateCode = $C_StateDisabled) Or ($L_OrigStateCode = $C_StateUnset) Then $G_SwitchStateCode = _ShowSwitchStateChangeGUI() If $G_SwitchStateChanged Then If $G_SwitchStateCode = $C_StateDisabled Then FileWriteLine( $G_RptFileHdl, "The GUI told us to disable and exit. Code = " & $G_SwitchStateCode ) MsgBox($MB_OK + $MB_TOPMOST, $C_Title, "The GUI told us to disable and exit!") Exit 5 EndIf EndIf $G_SwitchStateChanged = False EndIf Switch $G_SwitchStateCode Case $C_StateBlockPrimary $L_BlockedMonIdx = $L_PrimaryMonTblIdx $L_OpenMonIdx = $L_SecondaryMonTblIdx $L_CurMonTblIdx = $L_SecondaryMonTblIdx $G_MonIxToShowGUI = $L_OpenMonIdx Case $C_StateBlockSecondary $L_BlockedMonIdx = $L_SecondaryMonTblIdx $L_OpenMonIdx = $L_PrimaryMonTblIdx $L_CurMonTblIdx = $L_PrimaryMonTblIdx $G_MonIxToShowGUI = $L_OpenMonIdx EndSwitch $L_MoveTblCount = -1 ; We'll increment this when we want to add new windows to be moved $L_NumPrevWins = 0 $L_NumPrevPopups = 0 ; ; Begin Primary Operations... ; $G_TestAppWinHdl = WinGetHandle( $G_TestAppTitle ) If $G_TestAppWinHdl = 0 Then $G_TestAppPID = ShellExecute( $C_PathToTestApp ) ; This launches the test app asynchronously If $G_TestAppPID = 0 Then ; If the launch failed... MsgBox($MB_OK, $C_Title, "Fatal Error: Unable to launch test app -- Exiting") Exit 12121 EndIf $G_TestAppWinHdl = _GetHwndFromPID( $G_TestAppPID ) $G_TestAppTitle = WinGetTitle( $G_TestAppWinHdl ) Else $G_TestAppPID = WinGetProcess( $G_TestAppWinHdl ) EndIf $L_Stat = _AddPinMenuItem( $G_TestAppWinHdl ) ;~ <snip> <snip> <snip> $L_Stat = HotKeySet( "+!a", "_ShowSwitchStateGUIWrapper" ) ; Shift-Alt-A brings up the State Change GUI While True Local $L_CurTopWinAra = _WinAPI_EnumWindowsTop() $L_NumCurWins = $L_CurTopWinAra[0][0] If $L_NumPrevWins = 0 Then ; If this is the first time through this watch loop... For $i = 1 To $L_NumCurWins ; Build the Map of all top-level wins, to provide a basis for later comparison $L_CurWinHdl = $L_CurTopWinAra[$i][0] If WinExists( $L_CurWinHdl ) <> 1 Then ; This window may have been closed by this time ContinueLoop EndIf If ($L_CurWinHdl <> 0) And (IsHWnd($L_CurWinHdl) = 1) Then $L_ThisClass = $L_CurTopWinAra[$i][1] EndIf Next $L_NumPrevWins = $L_NumCurWins Else ; If we've been through this loop more than once, we'll have something to compare against $G_PrevWinMap = $G_CurWinMap ; Move the contents of the "current" Map (from from the previous pass) here. $G_CurWinMap = $L_EmptyMap ; We must empty that old Map so that we can load it up with ONLY the current windows $L_NumPrevWins = $L_NumCurWins ; That count now represents the number of top-level windows from previous pass For $i = 1 To $L_NumCurWins ; Re-build the current window Map $L_CurWinHdl = $L_CurTopWinAra[$i][0] If WinExists( $L_CurWinHdl ) <> 1 Then ; This window may have been closed by this time ContinueLoop EndIf If ($L_CurWinHdl <> 0) And (IsHWnd($L_CurWinHdl) = 1) Then $L_ThisClass = $L_CurTopWinAra[$i][1] ;~ If MapExists( $G_PinnedMap, $L_CurWinHdl ) Then ; If we're supposed to leave this win where it is ;~ ContinueLoop ;~ EndIf ;~ _Add2Map( $L_CurSpclMap, $L_CurWinHdl, $L_ThisClass ) ;~ If @error <> 0 Then ;~ FileWriteLine( $G_RptFileHdl, "ERROR - _Add2Map( #2 = " & Hex($L_CurWinHdl) * " ) Failed -- Aborting") ;~ MsgBox($MB_OK, $C_Title, "ERROR - _Add2Map( #2 = " & Hex($L_CurWinHdl) * " ) Failed -- Aborting") ;~ Exit 6 ;~ EndIf EndIf Next ; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - For $i = 1 To $L_NumCurWins ; Now that we have the NEW Cur Map built, we can compare it to Previous Map $L_CurWinHdl = $L_CurTopWinAra[$i][0] If ($L_CurWinHdl <> 0) And (IsHWnd($L_CurWinHdl) = 1) Then If WinExists( $L_CurWinHdl ) <> 1 Then ; This window may have been closed by this time ContinueLoop EndIf If MapExists( $G_PinnedMap, $L_CurWinHdl ) Then ; If we're supposed to leave this win where it is ContinueLoop EndIf $L_ThisClass = $L_CurTopWinAra[$i][1] If Not MapExists( $G_PrevWinMap, $L_CurWinHdl ) Then ; If this window wasn't in the prev list, i'ts new $L_Stat = _AnalyzeNewWin( $G_PrevWinMap, $L_CurWinHdl, $L_ThisClass ) If $L_Stat <> 0 Then ; If this window is new & has been added to the table of wins to be moved $L_ThisClass = $G_PrevWinMap[$L_CurWinHdl] MapRemove( $G_CurWinMap, $L_CurWinHdl ) ; Ensure that this win doesn't look like a fresh one MapRemove( $G_PrevWinMap, $L_CurWinHdl ) ; ... either past or present EndIf EndIf EndIf Next EndIf ; ; = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ; If $L_MoveTblCount >= 0 Then ; IF we need to move at least 1 win (valid indices/count start at 0) $L_Stat = _RelocateWins( ) $L_MoveTblCount = -1 EndIf ;;~ <snip> <snip> <snip> Sleep( 1000 ) WEnd Exit 12 ; ;=================================================================================================================== ; ; This function was kindly contributed by jchd ; See: https://www.autoitscript.com/forum/topic/204187-please-review-my-usage-of-scripting-dictionaries/?do=findComment&comment=1467804 ; Func _Add2Map(ByRef $map, $key, $value) If MapExists($map, $key) Then ; same as If $m[$key] = Null Then If IsMap($map[$key]) Then MapAppend($map[$key], $value) Else Local $m2[] ; an empty map $m2[1] = $map[$key] $m2[2] = $value $map[$key] = $m2 EndIf Else $map[$key] = $value EndIf EndFunc ;==>_Add2Map ; ;=================================================================================================================== ; ; _AnalyzeNewWin -- Analyze a newly discovered window to see if it should be moved to the other monitor. ; ; Return code: 0 = Ignore this window. ; 1 = This is an intruding window entirely on a blocked monitor, so it must be moved ; 2 = This is an intruding window partially on a blocked monitor, so it might need to be moved ; -1 = An error occurred during processing ; Func _AnalyzeNewWin( ByRef $arg_Map, $arg_WinHdl, $arg_Class ) ;~ <snip> <snip> <snip> Return 2 EndFunc ;==>_AnalyzeNewWin ; ;=================================================================================================================== ; ; We now know that there's at least one window (normal or popup) that needs to be relocated to a ; different monitor. ; ; Currently, there's no need to distinguish between the two window types (normal or popup), but ; there might need to be in the future, so I've decided to prepare for this eventuality. ; Func _RelocateWins() Const $Lfc_MoveFull = 1, $Lfc_MovePartial = 2 ;~ <snip> <snip> <snip> Return 1 EndFunc ;==>_RelocateWins ; ;= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ; Func _AddIfUnseenWin( ByRef $arg_Map, ByRef $arg_MoveTblWinInfoRow ) ; ; This function determines if we've seen this window before, and if NOT, then it ; gets added to the appropriate Map. ; ;~ <snip> <snip> <snip> Return 1 EndFunc ;==>_AddIfUnseenWin ; ;= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ; Func _AddPinMenuItem( $arg_ExtWinHdl ) Local $Lf_Stat, $Lf_ExtPID, $Lf_ExtSysMenuHdl, $Lf_ExtPinMenuHdl, $Lf_PinMenuItemIndex, $Lf_PinMenuItemID Local $Lf_InPinnedMap, $Lf_PinMenuItemExists, $Lf_SysMsgCodeMin, $Lf_SysMsgCodeMax If MapExists( $G_PinnedMap, $arg_ExtWinHdl ) Then $Lf_InPinnedMap = True Else $Lf_InPinnedMap = False EndIf $Lf_ExtPID = WinGetProcess( $arg_ExtWinHdl ) $Lf_ExtSysMenuHdl = _GUICtrlMenu_GetSystemMenu( $arg_ExtWinHdl, False ) $Lf_PinMenuItemIndex = _GUICtrlMenu_FindItem( $Lf_ExtSysMenuHdl, "Pinned", True ) If $Lf_PinMenuItemIndex = -1 Then $Lf_PinMenuItemExists = False Else $Lf_PinMenuItemExists = True $Lf_PinMenuItemID = _GUICtrlMenu_GetItemID( $Lf_ExtSysMenuHdl, $Lf_PinMenuItemIndex, True ) EndIf If Not $Lf_PinMenuItemExists Then If $Lf_InPinnedMap Then MapRemove( $G_PinnedMap, $arg_ExtWinHdl ) Return SetError( 0, 0, 2 ) EndIf $Lf_PinMenuItemIndex = _GUICtrlMenu_AddMenuItem( $Lf_ExtSysMenuHdl, "Pinned", 47 ) If $Lf_PinMenuItemIndex = -1 Then Return SetError( 17226, 0, 0 ) EndIf $Lf_PinMenuItemID = _GUICtrlMenu_GetItemID( $Lf_ExtSysMenuHdl, $Lf_PinMenuItemIndex, True ) _Add2Map( $G_PinnedMap, $arg_ExtWinHdl, $Lf_PinMenuItemID) $Lf_ExtPinMenuHdl = GUICtrlGetHandle( $Lf_PinMenuItemID ) $Lf_Stat = _SetupExtWinMsgRegs( $arg_ExtWinHdl, $Lf_ExtPinMenuHdl ) Return SetError( @error, 0, $Lf_Stat ) EndIf Return SetError( 0, 0, 1 ) EndFunc Func __PinMenuCmdHandler() Local $Lf_ControlID, $Lf_SendingWinHdl, $Lf_ControlHdl, $Lf_ExtSysMenuHdl Local $Lf_Enabled, $Lf_Highlighted $Lf_ControlID = @GUI_CtrlId $Lf_SendingWinHdl = @GUI_WinHandle $Lf_ControlHdl = @GUI_CtrlHandle $Lf_ExtSysMenuHdl = _GUICtrlMenu_GetSystemMenu( $Lf_SendingWinHdl, False ) $Lf_Enabled = _GUICtrlMenu_GetItemDisabled( $Lf_ExtSysMenuHdl, $Lf_ControlID, False ) $Lf_Highlighted = _GUICtrlMenu_GetItemHighlighted( $Lf_ExtSysMenuHdl, $Lf_ControlID, False ) If $Lf_Enabled Or $Lf_Highlighted Then _GUICtrlMenu_SetItemEnabled( $Lf_ExtSysMenuHdl, $Lf_ControlID, False, False ) _GUICtrlMenu_SetItemHighlighted( $Lf_ExtSysMenuHdl, $Lf_ControlID, False, False ) _GUICtrlMenu_EnableMenuItem( $Lf_ExtSysMenuHdl, $Lf_ControlID, 2, False ) If MapExists( $G_PinnedMap, $Lf_ExtSysMenuHdl) Then MapRemove( $G_PinnedMap, $Lf_ExtSysMenuHdl ) EndIf Else _GUICtrlMenu_SetItemEnabled( $Lf_ExtSysMenuHdl, $Lf_ControlID, True, False ) _GUICtrlMenu_SetItemHighlighted( $Lf_ExtSysMenuHdl, $Lf_ControlID, True, False ) _GUICtrlMenu_EnableMenuItem( $Lf_ExtSysMenuHdl, $Lf_ControlID, 0, False ) _GUICtrlMenu_DrawMenuBar( $Lf_ExtSysMenuHdl ) _Add2Map( $G_PinnedMap, $Lf_ExtSysMenuHdl, $Lf_ControlID ) EndIf _GUICtrlMenu_DrawMenuBar( $Lf_ExtSysMenuHdl ) Return EndFunc ; ;= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = ; Func _IsMonitorBlocked( ByRef $arg_MoveTblWinInfoRow ) ; ; This func determines if the passed window is currently presented on a "blocked" monitor ; ; Returns: $C_OnOpenMon (=1) If the window in the passed Move Table Window Info Row is on an Open monitor ; $C_OnBlockedMon (=2) If ... on Blocked mon ; $C_SpansMon (=3) If the window spans across both open and blocked mon ; ;~ <snip> <snip> <snip> EndFunc ;==>_IsMonitorBlocked ; ; = = = = = = = = = = = = = = = = = = = ; Func _SetupExtWinMsgRegs( $arg_ExtWinHdl, $arg_ExtControlHdl ) Local $Lf_Stat For $i = 0 To $G_SysWinMsgCodesRegAraCount -1 If $G_SysWinMsgCodesRegAra[$i][0] = $C_UseExtWinHdl Then $Lf_Stat = GUIRegisterMsg20( $arg_ExtWinHdl, $G_SysWinMsgCodesRegAra[$i][1], $G_SysWinMsgCodesRegAra[$i][2] ) Else $Lf_Stat = GUIRegisterMsg20( $arg_ExtControlHdl, $G_SysWinMsgCodesRegAra[$i][1], $G_SysWinMsgCodesRegAra[$i][2] ) EndIf If $Lf_Stat <> 1 Then Return SetError( @error, 0, 1 ) EndIf Next Return SetError( 0, 0, 0 ) EndFunc ; ; = = = = = = = = = = = = = = = = = = = ; Func __ExtCloseCmdHandler( $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam ) #forceref $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam Return $GUI_RUNDEFMSG EndFunc ; ; - - - - - - - - - - - - - - - - - - - ; Func __ExtMoveWinHandler( $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam ) #forceref $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam FileWriteLine( $G_RptFileHdl, "In __ExtMoveWinHandler()" ) Return $GUI_RUNDEFMSG EndFunc ; ; - - - - - - - - - - - - - - - - - - - ; Func __ExtMovIngWinHandler( $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam ) #forceref $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam FileWriteLine( $G_RptFileHdl, "In __ExtMovIngWinHandler()" ) Return $GUI_RUNDEFMSG EndFunc ; ; - - - - - - - - - - - - - - - - - - - ; Func __ExtMenuCmdHandler( $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam ) #forceref $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam FileWriteLine( $G_RptFileHdl, "In __ExtMenuCmdHandler()" ) Return $GUI_RUNDEFMSG EndFunc ; ; - - - - - - - - - - - - - - - - - - - ; Func __ExtMenuSelectHandler( $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam ) #forceref $arg_hWnd, $arg_Msg, $arg_wParam, $arg_lParam FileWriteLine( $G_RptFileHdl, "In __ExtMenuSelectHandler()" ) Return $GUI_RUNDEFMSG EndFunc  
    • By sandokanfirst
      Quick description: I'm building an application for a friend who wants to quit the use of Picasa but is reluctant because of the customised order of his pictures. The only way I know how to retain the custom order is to export all folders (and/or albums) managed by Picasa.
      I suppose the quick question is whether the way to trap messages for a window and/or control of your own making (GUI) is fundamentally different from trapping it for windows in a third party GUI. If that makes sense.
      My wizard style application has grown steadily but mainly slowly I found out that almost none of the parts of Picasa are recognisable Windows controls. (according to Au3Info and several other detection tools I've tried) So far, I've managed to work around that by reading and writing values to the registry w/ regard to export size, export location and quality.
      However. the final step appears to be the most difficult for me. What I tried to do is for the user to indicate two folders by double clicking on them. (Those two folders represent the range of folders that have to be exported). On this forum I found a contribution by Rasim to subclass a Window and replace the Windows procedure by a new one; see https://www.autoitscript.com/forum/topic/76841-detect-double-click-on-edit-box/ Here is his code to detect a double click in an edit control:
      $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 The(/a) fundamental difference between this code and my program is that I try to do the same with Picasa and that that is not a GUI created in AutoIt. So instead of GUICtrlGetHandle($edit), I call the above function with the Picasa window handle. However, it doesn't work. Neither does Notepad if I pass its handle. I'm sure I express myself rather clumsily, but is it imperative to create and address your own GUI if you want to detect a double click in a third party program? Why wouldn't the above code work when automating a non AutoIt GUI?
      Cheers,
      Sando
      PS (Edit): I'm sure there are smarter ways to achieve what I want in Picasa, but I'm also really interested in AutoIts working. I was a Delphi developer in the past, and although i see quite a few similarities, especially between Koda and the IDE, I've also come to realise how much we Delphi developers have been shielded from the intricacies of Windows and its frameworks. I find AutoIt fascinating, and incredibly useful.
×
×
  • Create New...