[RESOLVED] Can't Loop Through Items in a Collection
CAUTION: Noob using Win32 API ahead
I want to display a list of open windows on my computer in a rich text box. So, I'm trying to store the names of all windows in myString, and then set RichTextBox1.Text = myString.
Here's the code
Code:
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Text
Public Class Form1
Private Function Display(ByVal cs As Collection(Of String))
Dim builder As New StringBuilder
For Each item As String In cs
builder.Append(item & vbCrLf)
Next item
Return builder.ToString
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
RichTextBox1.Text = Display(GetActiveWindows)
End Sub
' CallBack delegate
Public Delegate Function CallBack(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
' Imports
Public Declare Function EnumWindows Lib "user32" (ByVal Adress As CallBack, ByVal y As Integer) As Integer
Public Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As IntPtr) As Boolean
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpWindowText As String, ByVal cch As Integer) As Integer
' /Imports
Private ActiveWindows As New Collection(Of String)
Public Function GetActiveWindows() As Collection(Of String)
ActiveWindows.Clear()
EnumWindows(AddressOf Enumerator, 0)
Return ActiveWindows
End Function
Private Function Enumerator(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
Dim text As String = Space(Int16.MaxValue)
GetWindowText(hwnd, text, Int16.MaxValue)
If IsWindowVisible(hwnd) Then
ActiveWindows.Add(text)
End If
Return True
End Function
End Class
So, Display function above doesn't loop through the Collection of window titles. I don't know why. I just need to know why for educational purposes. I managed to display the list of window titles in the rich text box using another method...shown below.
Thanks,
Code:
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Text
Public Class Form1
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
For Each item As String In GetActiveWindows()
RichTextBox1.AppendText(item)
RichTextBox1.AppendText(vbCrLf)
Next
'RichTextBox1.Text
End Sub
' CallBack delegate
Public Delegate Function CallBack(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
' Imports
Public Declare Function EnumWindows Lib "user32" (ByVal Adress As CallBack, ByVal y As Integer) As Integer
Public Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As IntPtr) As Boolean
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As Integer, ByVal lpWindowText As String, ByVal cch As Integer) As Integer
' /Imports
Private ActiveWindows As New Collection(Of String)
Public Function GetActiveWindows() As Collection(Of String)
ActiveWindows.Clear()
EnumWindows(AddressOf Enumerator, 0)
Return ActiveWindows
End Function
Private Function Enumerator(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
Dim text As String = Space(Int16.MaxValue)
GetWindowText(hwnd, text, Int16.MaxValue)
If IsWindowVisible(hwnd) Then
ActiveWindows.Add(text)
End If
Return True
End Function
End Class
Re: Can't Loop Through Items in a Collection
Since you claim to be new, are you familiar with breakpoints and stepping through code? Since you want to figure it out for educational purposes, a motivation that I thoroughly applaud, the best way to learn it is to figure it out yourself, and stepping through that loop would be the way to go.
Put a breakpoint on the For line of the loop. When execution pauses at the breakpoint, take a look at the collection (either hover over it, or use Shift+F9 on it) and see whether there are any items in it. Then step through the loop watching what happens in the stringbuilder. Frankly, I think you will find that the collection is empty, as the rest looks reasonable. The next step, then, would be to go figure out why the collection is empty, but this is a start.
Re: Can't Loop Through Items in a Collection
Actually, the Collection isn't empty. It contains 15 items in my case. And as I watch the code executes, I see that the code inside the loop is iterated 15 times, each time item has the value that it should and that value seems to be appended to builder, but for some reason the final value of the StringBuilder isn't output'd correctly on the rich text box. :confused:
Re: Can't Loop Through Items in a Collection
Where are you using string builder???
Re: Can't Loop Through Items in a Collection
Quote:
Originally Posted by
The Fire Snake
Where are you using string builder???
Inside Display function.
Re: Can't Loop Through Items in a Collection
Turn on Option Explicit and turn on Option Strict... Display doesn't return a string... it returns an object...
Private Function Display(ByVal cs As Collection(Of String))
SHOULD BE
Private Function Display(ByVal cs As Collection(Of String)) As String
-tg
Re: Can't Loop Through Items in a Collection
Quote:
Originally Posted by
techgnome
Turn on Option Explicit and turn on Option Strict... Display doesn't return a string... it returns an object...
Private Function Display(ByVal cs As Collection(Of String))
SHOULD BE
Private Function Display(ByVal cs As Collection(Of String)) As String
-tg
I changed the code as you suggested, but the problem persists.
Code:
Option Explicit On
Option Strict On
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Text
Public Class Form1
Private Function Display(ByVal cs As Collection(Of String)) As String
Dim builder As New StringBuilder
For Each item As String In cs
builder.Append(item & vbCrLf)
Next item
Return builder.ToString
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
RichTextBox1.Text = Display(GetActiveWindows)
End Sub
' CallBack delegate
Public Delegate Function CallBack(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
' Imports
Public Declare Function EnumWindows Lib "user32" (ByVal Adress As CallBack, ByVal y As Integer) As Integer
Public Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As IntPtr) As Boolean
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As System.IntPtr, ByVal lpWindowText As String, ByVal cch As Integer) As Integer
' /Imports
Private ActiveWindows As New Collection(Of String)
Public Function GetActiveWindows() As Collection(Of String)
ActiveWindows.Clear()
EnumWindows(AddressOf Enumerator, 0)
Return ActiveWindows
End Function
Private Function Enumerator(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
Dim text As String = Space(Int16.MaxValue)
GetWindowText(hwnd, text, Int16.MaxValue)
If IsWindowVisible(hwnd) Then
ActiveWindows.Add(text)
End If
Return True
End Function
End Class
Re: Can't Loop Through Items in a Collection
I'm a little confused why you have it setup but, hey, what ever....
If you breakpoint on this line:
Return builder.ToString
What does Builder have?
Does it have what you expect it to?
-tg
Re: Can't Loop Through Items in a Collection
Quote:
Originally Posted by
techgnome
I'm a little confused why you have it setup but, hey, what ever....
If you breakpoint on this line:
Return builder.ToString
What does Builder have?
Does it have what you expect it to?
-tg
In QuickWatch, StringBuilder has four attributes(?)
Capatcity = 524304
Chars = In order to evaluate an indexed property, the property must be qualified and the arguments must be explicitly supplied by the user.
Length = 458766
MaxCapacity = 2147483647
I'm not sure if it should have something else?
Re: Can't Loop Through Items in a Collection
Consider this:
Code:
Option Explicit On
Option Strict On
Imports System
Imports System.Collections.Generic
Imports System.Collections.ObjectModel
Imports System.Text
Public Class Form1
Private Function Display(ByVal cs As Collection(Of String)) As String
Dim builder As New StringBuilder
For Each item As String In cs
builder.Append(item & vbCrLf)
Next item
Return builder.ToString
End Function
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim windowText As String = ""
GetActiveWindows
windowText = Display(ActiveWindows) 'Breakpoint here... does AcvtiveWinows have what you expect?
RichTextBox1.Text = windowText 'Breakpoint here, does windowText have what you expect?
End Sub
' CallBack delegate
Public Delegate Function CallBack(ByVal hWnd As IntPtr, ByVal lParam As Integer) As Boolean
' Imports
Public Declare Function EnumWindows Lib "user32" (ByVal Adress As CallBack, ByVal y As Integer) As Integer
Public Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As IntPtr) As Boolean
Private Declare Function GetWindowText Lib "user32" Alias "GetWindowTextA" (ByVal hwnd As System.IntPtr, ByVal lpWindowText As String, ByVal cch As Integer) As Integer
' /Imports
Private ActiveWindows As New Collection(Of String)
Public Sub GetActiveWindows()
ActiveWindows.Clear()
EnumWindows(AddressOf Enumerator, 0)
End Function
Private Function Enumerator(ByVal hwnd As IntPtr, ByVal lParam As Integer) As Boolean
Dim text As String = Space(Int16.MaxValue)
GetWindowText(hwnd, text, Int16.MaxValue)
If IsWindowVisible(hwnd) Then
ActiveWindows.Add(text)
End If
Return True
End Function
End Class
Re-arranged a few things...
-tg
Re: Can't Loop Through Items in a Collection
Quote:
Originally Posted by
techgnome
Consider this:
Re-arranged a few things...
-tg
Thanks for that, techgnome.
Yes, ActiveWindows does have what I expect. windowText, on the other hand, only contains the name of the first window in the Collection (Apologize for forgetting to mention this earlier. Printing only the name of the first window in the Collection has always been the case).
Re: Can't Loop Through Items in a Collection
Just for fun, change this:
builder.Append(item & vbCrLf)
to this:
builder.Append(item & " // ")
See if you're still only getting the first window... also check to see if you actually get the "//" to show...
-tg
Re: Can't Loop Through Items in a Collection
Quote:
Originally Posted by
techgnome
Just for fun, change this:
builder.Append(item & vbCrLf)
to this:
builder.Append(item & " // ")
See if you're still only getting the first window... also check to see if you actually get the "//" to show...
-tg
The first window still shows. The "//" don't show. The output doesn't look any different from what I had before.
Re: Can't Loop Through Items in a Collection
That tells me a lot then....
This is line is actually the source of the problem:
GetWindowText(hwnd, text, Int16.MaxValue)
When APIs return strings, they are NULL-terminated... so as you are stringing along your strings when adding them to string builder, when the NULL is hit on the end of the first string, that effectively ends the string.... so what gets appended after that "doesn't exist" ...
You can do one of two things.... 1) Strip the null character before adding it to your collection of string... or 2) strip the null character from the string before passing it to the string builder.
-tg
Re: Can't Loop Through Items in a Collection
Quote:
Originally Posted by
techgnome
That tells me a lot then....
This is line is actually the source of the problem:
GetWindowText(hwnd, text, Int16.MaxValue)
When APIs return strings, they are NULL-terminated... so as you are stringing along your strings when adding them to string builder, when the NULL is hit on the end of the first string, that effectively ends the string.... so what gets appended after that "doesn't exist" ...
You can do one of two things.... 1) Strip the null character before adding it to your collection of string... or 2) strip the null character from the string before passing it to the string builder.
-tg
Spot on!
So, I added item = Replace(item, Chr(0), "") before builder.Append(item) and now it works!
Much appreciated. Thanks!
Re: Can't Loop Through Items in a Collection
Excellent! Sorry for the run around initially.
Don't forget to mark the thread resolved (under Thread Tools at the top).
-tg