none
Each obj in objShell.Windows does not return all objects RRS feed

  • Question

  • I have VBA code to automate IE sessions, which take prescribed actions within a corporate IE based application.  During the process sometimes a secondary window is intentionally opened up from the primary application to gather data from the "user".  Often the code below works to find and interact with this second window, but other times it does not.  The block of code in question is:

    Dim objShell As Object
    Dim ie As InternetExplorer

    .....
    found = False
    .....

    While Not found
    Set objShell = CreateObject("Shell.Application")
    For Each obj In objShell.Windows
    If TypeName(obj.Document) = "HTMLDocument" Then
    Set ie = obj
    If ie.LocationURL Like ....  Then
    ....
    found = True
    ....
    End If
    End If
    Next obj
    Wend
    ....

    The problem is that sometimes the system gets into a state where the "For Each obj In objShell.Windows" only returns one object, even though there are mutliple windows objects (IE browsers and MS Access sessions)  open.  The code gets stuck in an endless loop because it never finds the ie session that its looking for (even though it is there).  Only one object is being returned at this particular moment of investigation.

    One other point that may be contributing:  The code seems to work OK when I manually launch the routines, but runs into problems often when the MS  Access session is launched via the Task Scheduler.

    This is running from MS Access 2007 in a Windows 7 environment.

    Any suggestions would be greatly appreciated.

    Paul

    Wednesday, March 20, 2013 2:01 PM

All replies

  • Why do you need to 'find' those windows? Is it because you want to close them? If that's the case, try using the code I adapted from http://www.pbdr.com/vbtips/api/FindCloseAPI.htm:

    Declare Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As Any, ByVal lpWindowName As Any) As Long
    Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" _
        (ByVal hwnd As Long, ByVal lpString As String, _
        ByVal aint As Long) As Long
    Declare Function GetWindow Lib "user32" _
        (ByVal hwnd As Long, ByVal wCmd As Long) As Long
    Declare Function EnumWindows Lib "user32" _
        (ByVal wndenmprc As Long, ByVal lParam As Long) As Long
    Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
        (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, _
        lParam As Any) As Long
    
    Private Const WM_CLOSE = &H10
    Private Const GW_HWNDFIRST = 0
    Private Const GW_HWNDLAST = 1
    Private Const GW_HWNDNEXT = 2
    Private Const GW_HWNDPREV = 3
    Private Const GW_OWNER = 4
    Private Const GW_CHILD = 5
    Private Const GW_MAX = 5
    
    Private mstrTarget As String
    Private mblnSuccess As Boolean
    
    
    Sub Example()
        'The usage of these function is straight forward and fall into 2 parts: determining if a specific application
        'is open and if necessary closing that application. The 2 function all are as follows:
        Const strWindow2Close As String = "Notepad"
        
        If blnFindWindow(strWindow2Close) Then
            If Not blnCloseWindow(strWindow2Close) Then
                MsgBox "Problems encountered closing Window", _
                       vbInformation, "API Call"
                Exit Sub
            End If
        End If
    End Sub
    
    Public Function blnFindWindow(strApplicationTitle As String) As Boolean
    
        Dim hWndTmp As Long
        Dim nRet As Integer
        Dim TitleTmp As String
        Dim TitlePart As String
        Dim MyWholeTitle As String
        Dim mCounter As Long
        Dim hWndOver As Integer
        Dim sClassName As String * 100
    
        blnFindWindow = False
    
        TitlePart = UCase$(strApplicationTitle)
    
        'loop through all the open windows
        hWndTmp = FindWindow(0&, 0&)
    
        Do Until hWndTmp = 0
    
            TitleTmp = Space$(256)
            nRet = GetWindowText(hWndTmp, TitleTmp, Len(TitleTmp))
    
            If nRet Then
                'retrieve window title
                TitleTmp = UCase$(VBA.Left$(TitleTmp, nRet))
                'compare window title & strApplicationTitle
                If InStr(TitleTmp, TitlePart) Then
                    blnFindWindow = True
                    Exit Do
                End If
            End If
    
            hWndTmp = GetWindow(hWndTmp, GW_HWNDNEXT)
            mCounter = mCounter + 1
    
        Loop
    End Function
    
    
    Public Function blnCloseWindow(strApplicationTitle As String) As Boolean
    
    ' retrieve Windows list of tasks.
        mblnSuccess = False
        mstrTarget = strApplicationTitle
        EnumWindows AddressOf EnumCallback, 0
        blnCloseWindow = mblnSuccess
    
    End Function
    
    
    Public Function EnumCallback(ByVal app_hWnd As Long, _
                                 ByVal param As Long) As Long
    
        Dim buf As String * 256
        Dim title As String
        Dim length As Long
    
        ' Checks a returned task to determine if App should be closed
    
        ' get window's title.
        length = GetWindowText(app_hWnd, buf, Len(buf))
        title = Left$(buf, length)
    
        ' determine if target window.
        If InStr(UCase(title), UCase(mstrTarget)) <> 0 Then
            ' Kill window.
            SendMessage app_hWnd, WM_CLOSE, 0, 0
            mblnSuccess = True
        End If
    
        ' continue searching.
        EnumCallback = 1
    
    End Function

    Also, aren't the windows/applications created attached to variables? Why don't you invoke their method to close them?

    Sub fExample()
        Dim appIE1 As InternetExplorer
        Dim appIE2 As InternetExplorer
        Dim appAccess As Access.Application
        
        '...code goes here...
        
        appIE1.Quit
        appIE2.Quit
        appAccess.Quit
    End Sub


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br


    Wednesday, March 20, 2013 9:42 PM
  • Thanks for the coding examples.  I'll review and see if there is anything I can reapply.

    To answer your question, I need to find the new window, and then automate it.  ie: I need to stuff some data in a field and call a javascript action (which then submits the form and closes it).

    In general I'm using a VBA (MS Access driven) project to automate a set of pre-defined/repetitive tasks but on different "object codes" in the IE based application.  The IE app doesn't know that it is being  driven by automation rather than a 'human' user.

    Hope that explains things a bit more.

    Paul


    Thursday, March 21, 2013 2:57 PM
  • Ok... I think we're on the right track.  

    Using the "blnFindWindow" function above, I was able to find the desired window, when my current coding was not able to.  This is a big part of the answer I need.

    Since what I need to do now is declare that window as my "appIE2" object, how do I do that?

    my current approach is:

    Set objShell = createObject("Shell.Application")
    For Each obj in objShell.Windows
    if TypeName(obj.Document) = "HTMLDocument" then
    set appIE2=obj

    So how do I replicate the "Set appIE2=obj" using your approach?

    Paul


    • Edited by ProctoidPaul Thursday, March 21, 2013 6:14 PM typo
    Thursday, March 21, 2013 6:14 PM
  • I know hot to get an Excel Application Object by its handle, but never tried Internet Explorer (see post #5 for Excel example: http://www.mrexcel.com/forum/excel-questions/499935-getobject-hwnd.html#post2468114). The key API to use is the AccessibleObjectFromWindow. I don't know if this API can get an Internet Explorer Object too.

    Usually to get an opened instace of Internet Explorer, I use the code below:

    Sub Navegar2()
        Dim appIE As InternetExplorer
        
        Set appIE = ObterIE
        
        appIE.Navigate "http://www.ambienteoffice.com.br"
    End Sub
    
    Function ObterIE() As InternetExplorer 'Object
        Dim appShell As Variant
        Dim vWindows As Variant
        Dim v As Variant
        
        Set appShell = CreateObject("Shell.Application")
        Set vWindows = appShell.Windows()
        
        On Error Resume Next
        For v = 0 To vWindows.Count - 1
            If InStr(vWindows.Item(v).FullName, "iexplore.exe") > 0 Then
                Set ObterIE = vWindows.Item(v)
            End If
        Next v
        On Error GoTo 0
        
        If ObterIE Is Nothing Then
            Set ObterIE = CreateObject("InternetExplorer.Application")
        End If
        
        ObterIE.Visible = True
    End Function

    However, you're saying that under some circunstance the Shell.Application object is not working, I don't know how to help you. Maybe you could open a new thread called "How to get an object knowing its handle", explain that Shell.Application doesn't work and link to this topic.


    Felipe Costa Gualberto - http://www.ambienteoffice.com.br

    Thursday, March 21, 2013 10:28 PM
  • Looks promising.  It may be next week before I have a chance to test.  On the surface i see no reason why the example in your link shouldn't work.  I'll post back the results once tested.

    Thanks again,

    Paul

    Friday, March 22, 2013 2:12 PM
  • Ok... admittedly a bit over my head.  Can't seem to get there yet.

    I modified the blnFindwindow procedure above to return the handle of the desired window.  I tried to implement this with the above referenced example using "AccessibleObjecFromWindow", but its not working yet.  Can this work?  The IE session I want to find and take control of has "Edit/Archive" as part of the URL.  I can confirm that the modified blnFindwindow locates the appropirate IE session, and returns its hwnd value.  But the "obj" in the call returns a value of "nothing".

    Public Function test_ie2()
    Dim IDispatch As GUID
    Dim hwnd As Long
    Dim obj As Object
    Dim ie2 As InternetExplorer
    
        While Not found
            hwnd = blnFindWindow("Edit/Archive")
            
            If hwnd > 0 Then
                found = True
                Call AccessibleObjectFromWindow(hwnd, OBJID_NATIVEOM, IDispatch, obj)
                Set ie2 = obj
                Debug.Print ie2.LocationURL
            End If
        Wend
    
    
    End Function

    Paul

    Monday, March 25, 2013 4:33 PM