Sign in to follow this  
Followers 0
AlexII

Method .WinList() in VBScript

9 posts in this topic

I try to use the method .WinList () of AutoItX component in VBScript (I need to follow state of the windows). Repeated calls of this method causes an increase memory size occupied by this script more and more.

Steps to reproduce this behavior

Trying simple script:

Option Explicit

Dim objAutoItX
Dim arrWinList

Set objAutoItX = WScript.CreateObject("AutoItX3.Control")

Do
    arrWinList = objAutoItX.WinList("[ALL]")
    WScript.Sleep 200
Loop

Set objAutoItX = Nothing

WScript.Quit 0

The memory occupied by this script will continuously grow.

Any comments, please?!

OFF: Sorry, I'm not speaking English.

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Hello AlexII,

I do not check your code, but I mean you forgot the condition and your loop works for ever:

Do While|Until Condition
  ...
Loop

or

Do
  ...
Loop While|Until Condition

Look at the example in the help file.

Cheers

Stefan

P.S. Is not necessary to say sorry for your english knowledge, it is good. :)

Edited by StSchnell

Share this post


Link to post
Share on other sites

Not only does your post have nothing to do with his problem, it is also wrong. VB does not require conditions on loops.

The reason the memory grows is that the object must return an array but the host is not freeing the array.

Share this post


Link to post
Share on other sites

I do not check your code, but I mean you forgot the condition and your loop works for ever.

Yes, I know about it. This was only a demonstration example to reproduce incorrectly behavior. For example, full script listing:

Option Explicit

Dim objAutoItX
Dim objSWbemServicesEx
Dim dictOldWinList
Dim dictNewWinList

Dim arrWinList
Dim arrOldWinListHandles

Dim i
Dim strItHandle


Set objAutoItX       = WScript.CreateObject("AutoItX3.Control")
Set dictOldWinList   = WScript.CreateObject("Scripting.Dictionary")
Set objSWbemServicesEx = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")


objAutoItX.AutoItSetOption "WinWaitDelay", 100
' Search only top-level windows
objAutoItX.AutoItSetOption "WinSearchChildren", 0
' Search top-level and child windows
'objAutoItX.AutoItSetOption "WinSearchChildren", 1

arrWinList = objAutoItX.WinList("[ALL]")

For i = 1 to arrWinList(0, 0)
    dictOldWinList.Add arrWinList(1, i), arrWinList(0, i)
Next

Do
    Set dictNewWinList  = WScript.CreateObject("Scripting.Dictionary")
    
    arrWinList = objAutoItX.WinList("[ALL]")
    
    For i = 1 to arrWinList(0, 0)
        If Not dictOldWinList.Exists(arrWinList(1, i)) Then
            strItHandle = "[HANDLE:" & arrWinList(1, i) & "]"
            
            WScript.Echo Time() & " | New window created." & vbCrLf & _
                vbTab & "Handle:     [" & arrWinList(1, i) & "]" & vbCrLf & _
                vbTab & "Title:   [" & arrWinList(0, i) & "]" & vbCrLf & _
                vbTab & "Process ID: [" & objAutoItX.WinGetProcess(strItHandle) & "]" & vbCrLf & _
                vbTab & "Process:   [" & GetProcessNameByPID(objAutoItX.WinGetProcess(strItHandle)) & "]" & vbCrLf & _
                vbTab & "Position:   [" & objAutoItX.WinGetPosX(strItHandle) & "x" & objAutoItX.WinGetPosY(strItHandle) & "]" & vbCrLf & _
                vbTab & "Size:     [" & objAutoItX.WinGetPosWidth(strItHandle) & "x" & objAutoItX.WinGetPosHeight(strItHandle) & "]" & vbCrLf
        End If
        
        dictNewWinList.Add arrWinList(1, i), arrWinList(0, i)
    Next
    
    arrOldWinListHandles = dictOldWinList.Keys
    
    For i = 0 To dictOldWinList.Count - 1
        If Not dictNewWinList.Exists(arrOldWinListHandles(i)) Then
            WScript.Echo Time() & " | Window destroyed." & vbCrLf & _
                vbTab & "Handle:     [" & arrOldWinListHandles(i) & "]" & vbCrLf & _
                vbTab & "Title:   [" & dictOldWinList.Item(arrOldWinListHandles(i))  & "]" & vbCrLf
        End If
    Next
    
    dictOldWinList.RemoveAll
    
    Set dictOldWinList = Nothing
    Set dictOldWinList = dictNewWinList
    Set dictNewWinList = Nothing
    
    WScript.Sleep 200
Loop

WScript.Quit 0
'=============================================================================

'=============================================================================
Function GetProcessNameByPID(PID)
    GetProcessNameByPID = "unknown"
    
    ' If process already non-exists
    On Error Resume Next
    
    GetProcessNameByPID = objSWbemServicesEx.Get("Win32_Process.Handle='" & PID & "'").Name
End Function
'=============================================================================

Share this post


Link to post
Share on other sites

The reason the memory grows is that the object must return an array but the host is not freeing the array.

There are any ways to solve this problem? I also tryed

Erase arrWinList

with badly result.

Richard, sorry, I have a question: «but the host is not freeing», in this case, you mean that the «host» Windows Script Host or AutoItX? I try calling method .WinList() in AutoIt script also (for debugging only, I remember about «WinList» function :)):

Local $arrWinList
Local $objAutoItX = ObjCreate("AutoItX3.Control")

Do
    $arrWinList = $objAutoItX.WinList("[ALL]")
    Sleep(200)
    $arrWinList = 0
Until False

$objAutoItX = 0

Exit(0)

with identifically results memory grows by this script too.

Share this post


Link to post
Share on other sites

The "host" is the Windows script host. AutoItX is creating an array. This array is being passed to the WSH, and the memory isn't being freed at any point. I really don't know anything about VB script but I do know that unfreed arrays will pile up.

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

This is an interesting problem. Alex's last example does consume memory, even though he is freeing the array. It seems to be a memory leak in the COM object. Even this will consume memory -

Local $arrWinList
Local $objAutoItX = ObjCreate("AutoItX3.Control")

Do
    $objAutoItX.WinList("[ALL]")
    Sleep(200)
Until False

$objAutoItX = 0

Exit(0)

However replacing the COM object with a normal WinList() will not consume memory.

Edited by wraithdu

Share this post


Link to post
Share on other sites

The "host" is the Windows script host. AutoItX is creating an array. This array is being passed to the WSH, and the memory isn't being freed at any point. I really don't know anything about VB script but I do know that unfreed arrays will pile up.

This is an interesting problem. Alex's last example does consume memory, even though he is freeing the array. It seems to be a memory leak in the COM object. Even this will consume memory However replacing the COM object with a normal WinList() will not consume memory.

Richard, wraithdu, thanks. I understand that too. However, such behavior is observed in various scripting languages using .WinList() by AutoItX (COM), and this alerted me. Do you think, can does something in AutoItX? Maybe try to make a ticket in bug-tracker about this behavior?

Share this post


Link to post
Share on other sites

The problem comes in that the COM object has to return an array. There's no easy way to always enforce the freeing of such an array.

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