Link to home
Start Free TrialLog in
Avatar of Peter Groves
Peter Groves

asked on

Code from part a opens a new window after execution is complete. How do I continue with part B if execution is waiting input?

I click a button to  load an Excel file from a website via a Export to excel menu choice! The code below works fine and
opens the File download window except the newly opened 'save file to' window only opens
when the code exits the subroutine and wait for a new user input! .
How do I continue executing the second part of code that clicks the save button to actually save the file
as the window doesn't open until the first part finishes executing!  



Imports System.Runtime.InteropServices
Imports System.Text

Public Class activeWin

    Private Sub ActiveWin_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        GetActiveWindows()
    End Sub
    'Public Delegate Function CallBack(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
    Public Delegate Function CallBack(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
    Public listcount As Integer = 0
    Public Declare Function EnumWindows Lib "user32" (ByVal Adress As CallBack, ByVal y As Integer) As Integer
    Public Declare Function IsWindowVisible Lib "user32.dll" (ByVal hwnd As IntPtr) As Boolean
    Private Const SW_HIDE As Integer = 0
    Private Const SW_RESTORE As Integer = 9
    Public hwnd As IntPtr
    Public selectedIndex As Integer
    Public xAsIntPtr, cxAsIntPtr As IntPtr
    Private ActiveWindows As New System.Collections.ObjectModel.Collection(Of IntPtr)
    <DllImport("User32")> Private Shared Function ShowWindow(ByVal hwnd As IntPtr, ByVal nCmdShow As Integer) As Integer
    End Function
    Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As IntPtr, ByVal lpWindowText As String, ByVal cch As Integer) As Integer
    Public Declare Function SetActiveWindow Lib "user32" (ByVal hwnd As Integer) As Integer
    Declare Auto Function SetForegroundWindow Lib "USER32.DLL" _
            (ByVal hWnd As IntPtr) As Boolean
    Public Function GetActiveWindows() As ObjectModel.Collection(Of IntPtr)
        ListView1.Items.Clear()
        'listcount = 0
        EnumWindows(AddressOf Enumerator, 0)

        Return ActiveWindows
    End Function

    Private Function Enumerator(ByVal hwnd As Integer, ByVal lParam As Integer) As Boolean
        Dim intwidth As Integer
        Dim strItemText As String
        Dim objItem As ListViewItem
        Dim text As String = Space(Int16.MaxValue)
        If IsWindowVisible(hwnd) Then
            GetWindowText(hwnd, text, Int16.MaxValue) 'Get error here stating "Option Strict On disallows
            'implicit conversions from 'system.intptr to integer
            'Debug.Print(text & vbCrLf)
            If GetWindowText(hwnd, text, Int16.MaxValue) > 0 Then
                ListView1.View = View.Details
                If listcount = 0 Then
                    listcount = 1
                    intwidth = ListView1.Width - 5
                    ListView1.Columns.Add("WinNum", 25)
                    ListView1.Columns.Add("Name", 800)
                    'ListView1.Columns.Add("WinNum2", CInt(intwidth / 16))
                    'ListView1.Columns.Add("Name2", CInt(intwidth / 16))

                Else
                    'strItemText = cname:"
                    objItem = ListView1.Items.Add(GetWindowText(hwnd, text, Int16.MaxValue))
                    ' Debug.Print(GetWindowText(hwnd, text, Int16.MaxValue) & "TEXT: " & text & "hwnd:= " & hwnd & vbCrLf)
                    With objItem
                        .SubItems.Add(text)
                        .SubItems.Add("555-555-5555")
                        .SubItems.Add("555-555-5555")
                        .ImageIndex = 0
                    End With

                    'ListView1.Items.Add(text)
                End If
            End If
        End If
        If text.Contains("File Download") Then
            Debug.Print(GetWindowText(hwnd, text, Int16.MaxValue) & "Download file found: " & text & "hwnd:= " & hwnd & vbCrLf)
            'MsgBox("Hello- " & text)
            xAsIntPtr = hwnd
            LookForAndCloseIEPopup() '(GetWindowText(hwnd, text, Int16.MaxValue))
        End If
        Return True
        'Add some list view items.ListView1.Items.Add(GetWindowText(hwnd, text, Int16.MaxValue))


    End Function

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        GetActiveWindows()
    End Sub
    Sub ClickButton(ByVal WindowHandle As IntPtr)

        SetActiveWindow(xAsIntPtr)
        SendMessageW(WindowHandle, WM_ACTIVATE, New IntPtr(WA_ACTIVE), IntPtr.Zero)
        SendMessageW(WindowHandle, BM_CLICK, IntPtr.Zero, IntPtr.Zero)

    End Sub


    Sub LookForAndCloseIEPopup()

        'get a handle to any popup window associated with the main form (as is a popup window
        'displayed by the Web browser control)...
        Dim ptrDialogWindow As IntPtr = GetWindow(xAsIntPtr, GW_ENABLEDPOPUP)
        Dim text2 = GetWindowText(xAsIntPtr)

        Debug.Print(GetWindowText(ptrDialogWindow) & "TEXT:=" & Text2 & vbCrLf)
        'if the popup window is one displayed by the browser, then send the close message to the window...
        If text2.Contains("File Download") Then ClosePopup(ptrDialogWindow)

    End Sub

    Sub ClosePopup(ByVal WindowHandle As IntPtr)

        Dim clsChildHandles As ArrayList = GetChildWindowHandles(WindowHandle)
        Dim teststr As String
        Dim prthandleint As Long
        'look through all of the child handles of the window for an "OK" button (this method 
        'can also be used to gather more specific information about the dialog itself, such as 
        'the message being displayed)...
        For Each ptrHandle As IntPtr In clsChildHandles
            'if the SAVE button is found, click it...
            teststr = GetWindowText(ptrHandle)
            prthandleint = ptrHandle
            Debug.Print(teststr & " = Child TEXT:   len=" & teststr.Length & "handle = " & prthandleint & vbCrLf)

            If teststr.Contains("&Save") Then
                SetForegroundWindow(ptrHandle)
                ClickButton(ptrHandle)
                Exit For
            Else
                Debug.Print(teststr & " = ELSE Child TEXT:   len=" & vbCrLf)
            End If
        Next

    End Sub
    Const WM_GETTEXT As Long = &HD
    Const WM_GETTEXTLENGTH As Long = &HE
    Const GW_ENABLEDPOPUP As Long = 6
    Const BM_CLICK = &HF5
    Const GW_CHILD As Long = 5
    Const GW_HWNDNEXT As Long = 2
    Const WM_ACTIVATE = &H6
    Const WA_ACTIVE = &H1
    Public Const BM_SETSTATE = &HF3
    Public Const WM_LBUTTONDOWN = &H201
    Public Const WM_LBUTTONUP = &H202
    Public Const WM_COMMAND = &H111
    'function to retrieve the popup window associated with the form, as well as to find the child windows of the popup...
    Private Declare Auto Function GetWindow Lib "user32.dll" (ByVal hWnd As IntPtr, ByVal uCmd As Integer) As IntPtr
    <DllImport("user32.dll", EntryPoint:="SendMessageW")>
    Private Shared Function SendMessageW(ByVal hWnd As IntPtr, ByVal Msg As UInteger, ByVal wParam As IntPtr, ByVal lParam As IntPtr) As Integer
    End Function
    'sendmessage overload that is used to send messages to the button on the dialog window...
    Private Declare Auto Function SendMessage Lib "user32.dll" Alias "SendMessage" (ByVal hWnd As IntPtr, ByVal Msg As Integer,
    ByVal wParam As Integer, ByRef lParam As IntPtr) As IntPtr

    'sendmessage overloads used to retrieve the window text...
    Private Declare Auto Function SendMessageA Lib "user32.dll" Alias "SendMessageA" (ByVal hWnd As IntPtr, ByVal Msg As Integer,
    ByVal wParam As IntPtr, ByRef lParam As IntPtr) As IntPtr
    <DllImport("User32.dll", CharSet:=CharSet.Auto, EntryPoint:="SendMessage")> Public Shared Function SendMessageString(ByVal hwnd As IntPtr,
    ByVal wMsg As Integer, ByVal wparam As Integer, ByVal lparam As System.Text.StringBuilder) As IntPtr
    End Function
    Function GetChildWindowHandles(ByVal ParentWindowHandle As IntPtr) As ArrayList

        Dim b As Boolean
        Dim ptrChild As IntPtr
        Dim clsRet As New ArrayList
        'ParentWindowHandle = IntPtr(xAsIntPtr)
        'get first child handle...
        ptrChild = GetChildWindowHandle(xAsIntPtr)

        Do Until ptrChild.Equals(IntPtr.Zero)
            'add to collection of handles...
            clsRet.Add(ptrChild)
            'get next child...
            ptrChild = GetNextWindowHandle(ptrChild)

        Loop

        'return...
        Return clsRet

    End Function

    Function GetChildWindowHandle(ByVal ParentWindowHandle As IntPtr) As IntPtr
        Return GetWindow(ParentWindowHandle, GW_CHILD)
    End Function

    Function GetNextWindowHandle(ByVal CurrentWindowhandle As IntPtr) As IntPtr
        Return GetWindow(CurrentWindowhandle, GW_HWNDNEXT)
    End Function

    'this function returns the text of the window, used so that we can confirm that we have the right dialog window...
    Function GetWindowText(ByVal WindowHandle As IntPtr) As String

        Dim ptrRet As IntPtr
        Dim ptrLength As IntPtr

        'get length for buffer...
        ptrLength = SendMessageA(WindowHandle, WM_GETTEXTLENGTH, IntPtr.Zero, IntPtr.Zero)

        'create buffer for return value...
        Dim sb As New System.Text.StringBuilder(ptrLength.ToInt32 + 1)

        'get window text...
        ptrRet = SendMessageString(WindowHandle, WM_GETTEXT, ptrLength.ToInt32 + 1, sb)

        'get return value...
        Return sb.ToString

    End Function

    Public Function MakeLParam(ByVal LoWord As Int32, ByVal HiWord As Int32) As Int32
        Return (HiWord << 16) Or (LoWord And &HFFFF)
    End Function

End Class

Open in new window

Avatar of ste5an
ste5an
Flag of Germany image

When I understand your code correctly:

You're trying to download a file from a web site by automating IE yourself?

If this is correct, why don't you do it the official way and use webdriver? Webdriver is the current cross-browser browser intreaction interface. An other option would be classic Ie automation using hooking into IE and call the actions in the DOM directly.
Avatar of Peter Groves
Peter Groves

ASKER

When you download a file that opens a Windows window tagged 'Save download' ( OPEN ' SAVE  ' CANCEL ) and not a browser window!
Are you saying webdriver can act on a windows window?  Remember that I don`t have access to the file directly its created on the fly!

Pete
When you download a file that opens a Windows window tagged 'Save download' ( OPEN ' SAVE  ' CANCEL ) and not a browser window!
hmm, how should that be possible with a normal web site?

Are you saying webdriver can act on a windows window?  Remember that I don`t have access to the file directly its created on the fly!
What has downloading of a file on the client to do with its life cylce on the server?

Why don't you explain what you're trying to do concretly? And confirm or reject my assumption (the question mark was intenional). I had to guess, cause
I click a button to  load an Excel file from a website via a Export to excel menu choice!
does not tell us where you're doing this. It guess IE from the method names on your code snippet.

p.s. a comment about  your code: You should separate Win32 API stuff from your normal code. Create a separate module or shared class for it. The file where you do this is often called NativeMethods.
From my web browser (visual basic) I open a website that shows me the data I need with an option to export the data to excel.
When I click on the export to excel (via code),  a window opens and asks if I want to OPEN,SAVE, or CANCEL. When my code clicks save
it doesn't go to the next step (Asking where I would like to save the code) util sub closes. I assume there must be a way to flush the
buffers that tell windows I chose the save button so I can continue with the next step which is to fill in where to save the file!

Another option is to use multi threading to start the next step to monitor for the save to window which can take from instant to 30 seconds to open!

Pete
You need to hook into IDownloadManager of your WebBrowser control.
From what I can tell, To use Idownloadmanager you need to know the filename of the file to download.
The Excel file I'm downloading doesn't exist until I click the export to excel link.  

I'm going to do it with multi-threading!  

Pete
Actually pressing buttons you do nothing more then sending request to the server. Check out request content (using Fiddler or other tool) and repeat it using WebClient (or HttpWebRequest).
This question needs an answer!
Become an EE member today
7 DAY FREE TRIAL
Members can start a 7-Day Free trial then enjoy unlimited access to the platform.
View membership options
or
Learn why we charge membership fees
We get it - no one likes a content blocker. Take one extra minute and find out why we block content.