ShowWindowAsync is not working

I have a VB.net application. This app has one user triggered action which is to open a web site on IE browser. The app will search for all the IE tabs to see if the same web site has been opened.  If such a tab is found then refresh that tab with an updated URL (so that it won't create a new tab or window every time). Also if the IE window is minimized, it should restore or maximize the window.  I was able to use "Microsoft Internet Control" to find the tab, update the tab with new URL and get the window handle of the tab.  For restoring window, I did  some search, many people recommend using ShowWindowAsync. I tried that, it did not seem to work. The window would remain minimize. Following is the code snippet.

 

'openIE is SHDocVw.InternetExplorer
If (Not openIE Is Nothing) Then
Try
openIE.Navigate(url) 'update with new URL
Dim ret As Long
ret = ShowWindowAsync(openIE.HWND, 1) '1 is SW_NORMAL,
ret = ShowWindowAsync(openIE.HWND, 9) '3 is SW_SHOWMAX, 9 is SW_RESTORE


ret = BringWindowToTop(openIE.HWND)
ret = SetForegroundWindow(openIE.HWND)

Catch e As Exception  
      MsgBox("Exception: " & e.Message)
End Try

End If

      When I ran this program, there was no error or exception.  However, IE would stay minimized.  I don't know why this is not working and if there is any way to debug it; or if there is other way to accomplish the same thing; Any help will be appreciated.

 
Thanks

mingxunAsked:
Who is Participating?
 
nffvrxqgrcfqvvcConnect With a Mentor Commented:
Can you post the declerations of your ShowWindowAsync() method.
Dim ret As Long <---- This should be an IntPtr value.
Here is an example of restoring a minimized window.

<DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function ShowWindowAsync(ByVal hWnd As IntPtr, ByVal nCmdShow As Integer) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Auto)> _
    Private Shared Function IsIconic(ByVal hWnd As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    End Function
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim hWindow As IntPtr
        hWindow = &H3502B2 ' The handle to IEFrame goes here.
        If IsIconic(hWindow) Then
            ShowWindowAsync(hWindow, 9) 'SW_RESTORE
        End If
    End Sub

Open in new window

0
 
nffvrxqgrcfqvvcCommented:
I think what you will need to do here is verify that OpenIE.HWND is actually the window handle to the (Main) window IEFrame (classname) or not zero. You can use spy++ utility to match to verify the handles.
Which language are you using Classic VB6 or VB.NET?
Which operating system are you testing under?
0
 
nffvrxqgrcfqvvcCommented:
Ahh.. your using VB.NET didn't even see the Try Catch Block... But yeah see if the handles match up correctly.
0
Introducing Cloud Class® training courses

Tech changes fast. You can learn faster. That’s why we’re bringing professional training courses to Experts Exchange. With a subscription, you can access all the Cloud Class® courses to expand your education, prep for certifications, and get top-notch instructions.

 
mingxunAuthor Commented:
Thanks for the response. I used message box to display the handle and  spy++ to get the handle of the IEFrame. They did match, except one is hex, another is digit. However, from spy++,  I didn't see any message going to that IEFrame after that ShowWindowAsync was called.  Do you know any other testing I could do? Thanks for your help.
0
 
mingxunAuthor Commented:
Using your ShowWindowAsync() declaration seems have solved my problem.  The declaration I used did not have the DllImport in the declaration. So it probably could not find the function in runtime. It would be helpful if it could throw a runtime exception for not being able to find the function.

Thanks
0
 
nffvrxqgrcfqvvcCommented:
Good to know it works for you =) .. It would generally throw an exception but the decleration you used might have been incorrect, it's possible that you found a VB6 version which uses the Long Data type but in VB.NET what would be Long in VB6 is actually an Integer data type in VB.NET.
0
 
mingxunAuthor Commented:
The declaration I used is like this:

Public Shared Function ShowWindowAsync(ByVal hWnd As IntPtr, ByVal cmdShow As Integer) As Boolean
End Function

I believed I copied the function declaration from one website, which I could not remember.   But the program compiled and no exception in runtime.
0
 
nffvrxqgrcfqvvcCommented:
Yeah what you have really is just a shared procedure which basically doesn't do anything and is exactly the same as just creating your own method.
You never did call into the actual API at anytime during your calls rather just calling into a blank procedure. =)
0
 
mingxunAuthor Commented:
I started working on this VB.net project several months back. So my experience on VB.net is very limited.  Before that, I mostly programmed in Java and C++.  I thought VB's function declaration is like function definition in C++ header file, which will help compiling. In runtime, it still needs to look for actual function implementation. I also think that my program has only the function declaration for ShowWindowAsync. My program doesn't have the implementation. And when I debug my program, this function always returns 0. That is something I don't understand.  I have another problem, if IE window has many tabs, and the tab I am interesting is not active right now. How could I make that tab active or getting focus using vb.net? Any pointer will be appreciated.
0
 
nffvrxqgrcfqvvcCommented:
The individual tabs are more complicated to activate because they use DirectUIHWND class. Microsoft never added functionality to work with individual tabs the only support I know is with the IAccessible Interface. If you have the Windows SDK you can use the Inspect utility to look at the objects then using IAccessible to iterate through the objects.
Here is an example I quickly wrote that will activate the TAB of a specified (tab title) =)

You must add a reference to IAccessible, Under the IDE menu select Project > Add Reference > .NET Tab > Accessibility
Create new class named: IETab.vb
Add the following code to the class in the code section.
You can use it like this (using this question title as an example)
'// Usage

IETab.ActivateTabByTitle("ShowWindowAsync is not working")
 
 

Imports System.Runtime.InteropServices

Public Class IETab
    'egl1044
    Public Shared Sub ActivateTabByTitle(ByVal title As String)

        Dim objCount As Integer
        Dim tabCount As Integer
        Dim tabTitle As String = String.Empty

        Dim acc As Accessibility.IAccessible = Nothing
        Dim accTabRow As Accessibility.IAccessible = Nothing

        Dim h1 As IntPtr = SafeNativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "IEFrame", Nothing)
        Dim h2 As IntPtr = SafeNativeMethods.FindWindowEx(h1, IntPtr.Zero, "CommandBarClass", Nothing)
        Dim h3 As IntPtr = SafeNativeMethods.FindWindowEx(h2, IntPtr.Zero, "ReBarWindow32", Nothing)
        Dim h4 As IntPtr = SafeNativeMethods.FindWindowEx(h3, IntPtr.Zero, "TabBandClass", Nothing)
        Dim h5 As IntPtr = SafeNativeMethods.FindWindowEx(h4, IntPtr.Zero, "DirectUIHWND", Nothing)

        Dim IID_IAccessible As New Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}")

        If SafeNativeMethods.AccessibleObjectFromWindow(&H9503C8, &HFFFFFFFC, IID_IAccessible, acc) = 0 Then
            '// Enumerate the objects and find the "Tab Row"
            '// Quick(Tabs(Ctrl + Q)
            '// TAB List
            '// TAB Row
            objCount = acc.accChildCount
            For i = 1 To objCount
                If acc.accChild(i).accName(0).ToLower.Equals("tab row") Then
                    '// Found the Tab Row.
                    accTabRow = DirectCast(acc.accChild(i), Accessibility.IAccessible)
                    '// The number of tabs in this row + new tab
                    tabCount = accTabRow.accChildCount
                    For tabs As Integer = 1 To tabCount
                        '// The title of each tabs
                        tabTitle = accTabRow.accChild(tabs).accName(0)
                        '// Activate the tab
                        If tabTitle.ToLower.Equals(title.ToLower) Then
                            accTabRow.accChild(tabs).accDoDefaultAction(0)
                        End If
                    Next
                    Exit For
                End If
            Next
            Marshal.FinalReleaseComObject(acc)
        End If
    End Sub
End Class

Public Class SafeNativeMethods
    <DllImport("user32.dll", ExactSpelling:=False, SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Friend Shared Function FindWindowEx(ByVal hwndParent As IntPtr, _
                                        ByVal hwndChildAfter As IntPtr, _
                                        ByVal lpszClass As String, _
                                        ByVal sWindowTitle As String) As IntPtr
    End Function

    <DllImport("oleacc.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Unicode)> _
    Friend Shared Function AccessibleObjectFromWindow(ByVal hWnd As IntPtr, _
                                                      ByVal dwObjectID As Integer, _
                                                      ByRef refID As Guid, _
                                                      ByRef ppvObject As Accessibility.IAccessible) As Integer
    End Function
End Class

Open in new window

0
 
nffvrxqgrcfqvvcCommented:
My Bad... On Line 22 while I was debugging I left the hard coded window handle. You have to change line 22 to the following (h5) variable.
 

If SafeNativeMethods.AccessibleObjectFromWindow(h5, &HFFFFFFFC, IID_IAccessible, acc) = 0 Then

Open in new window

0
 
nffvrxqgrcfqvvcCommented:
BTW: You also might want to change it up for example its possible that more than one IEFrame exists so start at the IEFrame HWND that has the Tabs you want to target. The example currently will just find the first IEFrame available.
0
 
mingxunAuthor Commented:
Thanks for the quick response. I tried IETab. I got an exception on line 29.

 Exception - Value does not fall within the expected range.      

From the debugger,objCount is 7, and quick watch shows
+            acc.accChild(1)      {"Value does not fall within the expected range."}      Object

I changed the code a little bit so that I can pass the hwnd to the function. Following is the code:


Imports System.Runtime.InteropServices

Public Class IETab
    'egl1044
    Public Shared Sub ActivateTabByTitle(ByVal title As String, ByVal hwnd As IntPtr)

        Dim objCount As Integer
        Dim tabCount As Integer
        Dim tabTitle As String = String.Empty

        Dim acc As Accessibility.IAccessible = Nothing
        Dim accTabRow As Accessibility.IAccessible = Nothing

        Dim h1 As IntPtr = SafeNativeMethods.FindWindowEx(IntPtr.Zero, IntPtr.Zero, "IEFrame", Nothing)
        Dim h2 As IntPtr = SafeNativeMethods.FindWindowEx(h1, IntPtr.Zero, "CommandBarClass", Nothing)
        Dim h3 As IntPtr = SafeNativeMethods.FindWindowEx(h2, IntPtr.Zero, "ReBarWindow32", Nothing)
        Dim h4 As IntPtr = SafeNativeMethods.FindWindowEx(h3, IntPtr.Zero, "TabBandClass", Nothing)
        Dim h5 As IntPtr = SafeNativeMethods.FindWindowEx(h4, IntPtr.Zero, "DirectUIHWND", Nothing)

        Dim IID_IAccessible As New Guid("{618736E0-3C3D-11CF-810C-00AA00389B71}")

        If SafeNativeMethods.AccessibleObjectFromWindow(hwnd, &HFFFFFFFC, IID_IAccessible, acc) = 0 Then
            '// Enumerate the objects and find the "Tab Row"
            '// Quick(Tabs(Ctrl + Q)
            '// TAB List
            '// TAB Row
            objCount = acc.accChildCount
            For i = 1 To objCount
                If acc.accChild(i).accName(0).ToLower.Equals(title) Then
                    '// Found the Tab Row.
                    accTabRow = DirectCast(acc.accChild(i), Accessibility.IAccessible)
                    '// The number of tabs in this row + new tab
                    tabCount = accTabRow.accChildCount
                    For tabs As Integer = 1 To tabCount
                        '// The title of each tabs
                        tabTitle = accTabRow.accChild(tabs).accName(0)
                        '// Activate the tab
                        If tabTitle.ToLower.Equals(title.ToLower) Then
                            accTabRow.accChild(tabs).accDoDefaultAction(0)
                        End If
                    Next
                    Exit For
                End If
            Next
            Marshal.FinalReleaseComObject(acc)
        End If
    End Sub
End Class

Public Class SafeNativeMethods
    'Friend Charset
    '<DllImport("user32.dll", ExactSpelling:=False, SetLastError:=True, CharSet:=CharSet.Unicode)> _
    <DllImport("user32.dll", ExactSpelling:=False, SetLastError:=True)> _
    Friend Shared Function FindWindowEx(ByVal hwndParent As IntPtr, _
                                        ByVal hwndChildAfter As IntPtr, _
                                        ByVal lpszClass As String, _
                                        ByVal sWindowTitle As String) As IntPtr
    End Function

    '<DllImport("oleacc.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Unicode)> _
    <DllImport("oleacc.dll", ExactSpelling:=True, SetLastError:=True)> _
    Friend Shared Function AccessibleObjectFromWindow(ByVal hWnd As IntPtr, _
                                                      ByVal dwObjectID As Integer, _
                                                      ByRef refID As Guid, _
                                                      ByRef ppvObject As Accessibility.IAccessible) As Integer
    End Function
End Class

Open in new window

0
 
nffvrxqgrcfqvvcCommented:
Remove line 14 or comment it out and then the HWND for the IEFrame just needs to begin on line 15 only change needed.
For example.

Dim h2 As IntPtr = SafeNativeMethods.FindWindowEx(hwnd, IntPtr.Zero, "CommandBarClass", Nothing)

Open in new window

0
 
mingxunAuthor Commented:
After the change, I still got the same exception. It seems for some reason acc.accChild is not accessible or is not a list.
0
 
nffvrxqgrcfqvvcCommented:
I have to ask did you change line 22 back to h5 variable?
If SafeNativeMethods.AccessibleObjectFromWindow(h5, &HFFFFFFFC, IID_IAccessible, acc) = 0 Then

Open in new window

0
 
mingxunAuthor Commented:
My fault, I did not change that to h5. Now after that change, it is working now. Thanks. I will probably need to spend some time to read the document to understand what it does.

Thanks again.
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.