Getting all windows of a multiple instance application like word or iexplorer

Hi!

If I launch winword multiple times I only have one process named winword.exe in my processlist. Same with e.g. internet explorer.

Now I need to create some tasklist and from this tasklist the user should be able to click on the application or window he wants to bring to foreground.

I have the complete process object as the word application is launched by my application, but I don't know how to enumerate the other instances.

I have tried with EnumChildWindows and EnumThreadWindows but I'm stuck now.

If I'm using

For Each proc As Process In Process.GetProcesses
            EnumThreadWindows(proc.Id, New EnumThreadDelegate(AddressOf EnumThreadCallback), IntPtr.Zero)

Next

I get all thread windows of ALL processes including the ones from winword.exe.

But I only want them from winword so I assume this should work:

For Each proc As Process In Process.GetProcessesByName("winword")

            EnumThreadWindows(proc.Id, New EnumThreadDelegate(AddressOf EnumThreadCallback), IntPtr.Zero)
       
Next

But this returns me nothing. Hmm....

So in short I need a method to get all window handles with their windows text (via GetWindowText) of a given process so the user can decide which he needs and brings them to foreground.

Thanks and regards
Jan
LVL 1
janwrageAsked:
Who is Participating?
 
nffvrxqgrcfqvvcCommented:
Well the Alt+Tab window just displays windows that are visible with no owner windows. You could use something like this to get the effect but you have to add your own filtering to which windows you want to display.. For example you can display windows even if they don't have title information or you can filter them. You can take it further by checking the extended style of the window even. The minimum basics of what you need are shown in the example.
Imports System.Runtime.InteropServices
Imports System.Text

Public Class Class1

    Private Const GW_OWNER = 4

    <DllImport("user32.dll", CharSet:=CharSet.Unicode, ExactSpelling:=True, SetLastError:=True)> _
    Private Shared Function EnumWindows(ByVal lpEnumFunc As EnumWindowsProcDelegate, ByVal lParam As IntPtr) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
    Private Shared Function IsWindowVisible(ByVal hWnd As IntPtr) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
    Private Shared Function GetWindowText(ByVal hWnd As IntPtr, ByVal lpString As StringBuilder, ByVal nMaxCount As Integer) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
    Private Shared Function GetParent(ByVal hWnd As IntPtr) As Integer
    End Function
    <DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
    Private Shared Function GetWindow(ByVal hWnd As IntPtr, ByVal uCmd As Integer) As Integer
    End Function

    Private Shared sb As New StringBuilder(260)
    Public Shared Sub BeginEnum()
        EnumWindows(AddressOf EnumWindowsProc, IntPtr.Zero)
    End Sub

    Private Shared Function GetTitle(ByVal hWnds As IntPtr) As String
        If GetWindowText(hWnds, sb, sb.Capacity) Then
            Return sb.ToString
        Else
            Return String.Empty
        End If
        sb.Length = 0
    End Function

    Private Delegate Function EnumWindowsProcDelegate(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Integer
    Private Shared Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Integer
        Dim title As String = String.Empty
        Dim exStyle As Integer = 0
        If IsWindowVisible(hWnd) Then
            If GetParent(hWnd) = 0 Then
                If GetWindow(hWnd, GW_OWNER) = 0 Then
                    title = GetTitle(hWnd)
                    If title.Length <> 0 Then
                        Console.WriteLine("hWnd= {0}    {1} ", hWnd.ToString, title)
                    End If
                End If
            End If
        End If

        Return 1 ' push
    End Function
End Class

Open in new window

0
 
nffvrxqgrcfqvvcCommented:
You can still use Process class for this... If you look deeper in the process class it has  MainWindowHandle and MainWindowTitle. You shouldn't need the pInvoke here unless you want handles to windows other than the main window.
0
 
janwrageAuthor Commented:
Hi egl1044!

I know the properties but they do not return the handles of the threadwindows nor the titles of them.

Try for urself, spawn multiple instances of an internet explorer process and look at the MainWindowTitle-property. It will always return one window title only even if all instances have different titles.
0
Cloud Class® Course: Microsoft Office 2010

This course will introduce you to the interfaces and features of Microsoft Office 2010 Word, Excel, PowerPoint, Outlook, and Access. You will learn about the features that are shared between all products in the Office suite, as well as the new features that are product specific.

 
nffvrxqgrcfqvvcCommented:
Hi,
Well the iexplore.exe is very different in how it works. If you can remember back to version 6 of IE there was no idea of (TABS) this meant that all windows would be launched in seperate process. When you move to later versions that implemented the (TAB) feature microsoft has changed the way this works. When iexplore is launched it opens a new process and associates it with the tabbed window rather than the main window.
iexplore shouldn't be used for testing purposes in this case because one would need to look deeper at all the IEFrame windows using pinvoke. However this isn't the case for most other windows. Do you need iexplore windows specifically?
 
 

Dim myProcesses As Process() = Process.GetProcessesByName("notepad")
        Dim myProcessesObj As Process

        For Each myProcessesObj In myProcesses
            Console.WriteLine(myProcessesObj.MainWindowTitle)
        Next

Open in new window

0
 
janwrageAuthor Commented:
Hi!

I think you misunderstood me, but that's my fault eventually :)

I do not mean any tabs in ie but a completely new ie window. But use winword as a better example. If you launch winword u get a process called winword.exe and a new window with document 1 as title. Now launch winword a second time: u get a new window with document 2 as title...but u get NO second process called winword.exe. So one process but two windows...

Now I think you understand my problem, right? :)
0
 
nffvrxqgrcfqvvcCommented:

Okay ... Yes I understand.
1)
This is what you can do use Process.GetProcessByName() then for that single instance use the .MainWindowHandle property(assuming winword main window is always visible). Instead of EnumThreadWindows replace it with EnumChildWindows. Specifyc the .MainWindowHandle for hWndParent member.
I don't have Windword(MS WORD) so you will need to do the following:
What you will need though is to run SPY++ (Start > MS Visual Studio > MS Visual Studio Tools > SPY++)
Under the SPY++ application main menu choose (Search) > Find Window (Drag the little circle icon onto one of the WinWord documents and click OK button. Under the list right click and choose Properties what you need to find is the (ClassName) of the document so that you can search for all of them and filter anything that isn't a document. Using GetClassName you can check if its the correct classname you want if so this returns the hWnd to the document iteself and then you can use GetWindowText.
2) You could probrably use office interop / automation where you can get an instance and then enumerate each document. This is the easiest solution.
http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.getactiveobject.aspx
0
 
janwrageAuthor Commented:
Thanks for your comment but this seems to be limited to winword. I need this for all types of applications. And I don't want to hard-code the classname for my common applications.

There must be a way to do this as the explorer does the same thing when pressing alt-tab. It shows all windows and let one select them.

Any other ideas?
0
 
nffvrxqgrcfqvvcCommented:
Ahh you want all windows.... So what you trying to do here something like Task managers (application tab)?
0
 
janwrageAuthor Commented:
My app is an application launcher for users on a terminal server. The user clicks on word for example and then word is started. as the users have no taskbar (my launcher replaces the shell) they have no chance to switch between the application-windows. So I want to implement some sort of task-switcher just like alt-tab.

Therefore I need to be able to handle all types of applications.
0
 
janwrageAuthor Commented:
Hi!

Thanks for your code. I got a little different one by googling around but then I have the idea to get the process from the window handle. Then check, if the process exists in my process-list and if so, extract the icon from the exe and add it to my task-list (with the title of course).

See the snippet on how I get the process from the handle. Now I'm thinking about the enumeration of the internet explorer tabs you mentioned earlier...
Private Shared Function EnumWindowsProc(ByVal hWnd As IntPtr, ByVal lParam As IntPtr) As Integer
        Dim title As String = String.Empty
        If IsWindowVisible(hWnd) Then
            If GetParent(hWnd) = 0 Then
                If GetWindow(hWnd, GW_OWNER) = 0 Then
                    title = GetTitle(hWnd)
                    If title.Length <> 0 Then
                        Dim iProcessID As IntPtr
                        GetWindowThreadProcessId(hWnd, iProcessID)

                        Dim parentProcess As Process = Process.GetProcessById(iProcessID.ToInt32)

                        Console.WriteLine("hWnd= {0}    Title= {1}     ParentProcessName= {2}", hWnd.ToString, title, parentProcess.ProcessName)
                    End If
                End If
            End If
        End If

        Return 1 ' push
    End Function

Open in new window

0
 
nffvrxqgrcfqvvcCommented:
You can directly get the HICON of the application from hWnd in a couple different ways...
1) Use GetClassLong API and specify GCL_HICON.
http://msdn.microsoft.com/en-us/library/ms633580(VS.85).aspx

2) Use WM_GETICON message.
http://msdn.microsoft.com/en-us/library/ms632625(VS.85).aspx
 
0
 
janwrageAuthor Commented:
Here's the code to get the tabs and the urls...
Dim SW As ShellWindows = New ShellWindowsClass
        Dim processName As String
        Dim htmlDoc As HTMLDocumentClass

        For Each IE As InternetExplorer In SW
            processName = IO.Path.GetFileNameWithoutExtension(IE.FullName).ToLower

            If processName.Equals("iexplore") Then
                Try
                    htmlDoc = CType(IE.Document, HTMLDocumentClass)

                    Console.WriteLine("Tab-Name= {0}        Tab-URL= {1}", htmlDoc.IHTMLDocument2_nameProp, htmlDoc.IHTMLDocument2_url)
                Catch ex As Exception
                End Try
            End If
        Next

Open in new window

0
 
janwrageAuthor Commented:
Could you give me an example on using either GetClassLong-API or WM_GETICON-Message?

I'm using SHGetFileInfo to get the icon of the exe. Thats fine for applications that are not running. But in case of the task-switcher your way may be faster.
0
 
nffvrxqgrcfqvvcCommented:
Sure..
Private Const GCL_HICON As Integer = (-14)
<DllImport("user32.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
    Private Shared Function GetClassLong(ByVal hWnd As IntPtr, ByVal nIndex As Integer) As IntPtr
    End Function

   Private Shared Function GethIconFromhWnd(ByVal hWnd As IntPtr) As IntPtr
        Return GetClassLong(hWnd, GCL_HICON)
    End Function

Dim hIcon As IntPtr = IntPtr.Zero
Dim ico as Icon
hIcon = GethIconFromhWnd(hWnd)
If hIcon.ToInt32 <> 0 Then
ico = Icon.FromHandle(hIcon)
End If

Open in new window

0
 
janwrageAuthor Commented:
Sorry for late reply. Give u the points now.
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.