0

This thread was marked as a duplicate becauseI have encountered this problem before and have asked a question before. I would now normally know how to solve it (i.e. by using a variable pointing to an instance of the form instead of its name). However this time it is my default startup form I need to reference. So there is no variable storing its instance and I don't know how I can reference the existing instance of it without one.

I have 2 forms (frmMain and frmStatus) I create an instance of frmStatus and store it in the statusDialog variable on frmMain. Then I create a new thread (for my ADSearcher class) which fires events that then use a delegate to invoke subs on statusDialog. The ADSearcher thread also creates multiple other threads (for my ComputerSearcher thread) which also needs to raise events that will use a delegate to invoke subs on statusDialog.

The problem is in the ComputerSearcher threads. The AddHandler lines appear to be creating a new instance of frmMain which means that statusDialog is Nothing/Null. Why is AddHandler creating a new instance of the form and is there a way I can get it to use the correct instance?

The reason I think it's creating a new instance is because when I step through the code it executes the first AddHandler line and then steps through the variable declarations at the top of frmMain (outside of any subs) at which point statusDialog becomes Nothing.

The starting point is Private Sub mnuSync_ADSync_Click(...). This creates the first thread and class instance. I've tried to provide enough code for you to see what's happening without posting a huge essay. If you need any additional code please ask.

Thanks in advance,

frmMain

Dim statusDialog As frmStatus 'Used to create an instance of the status dialog

Private Sub mnuSync_SyncAD_Click(sender As Object, e As EventArgs) Handles mnuSync_SyncAD.Click
    With adSync
        .RootPath = "LDAP://domain.com"
        .FilterString = "(objectCategory=organizationalUnit)"

        '### TO DO: Replace .UserID and .Password values with a stored value
        If Not integratedAuth Then
            .UserID = "...."
            .Password = "...."
        End If
        .PageSize = 5
        .PropertiesToLoad = New String() {"cn", "name", "distinguishedName", "dNSHostName", "objectCategory", "objectGUID"}

        'Create the status dialog
        statusDialog = New frmStatus

        ThreadPool.QueueUserWorkItem(AddressOf .StartSearch)

        statusDialog.ShowDialog()

    End With
End Sub

'[ADSearcher Events]
Private Delegate Sub displayOUStatus(ByVal ousFound As Integer)
Private Sub adSync_OUResultFound(ByVal ousFound As Integer) Handles adSync.OUResultFound
    Dim updateStatus As New displayOUStatus(AddressOf statusDialog.UpdateOUSearcherStatus)
    Me.Invoke(updateStatus, New Object() {ousFound})
End Sub

Private Delegate Sub displayResult(ByVal node As TreeNode)
Private Delegate Sub findMACAddresses(ByVal compCollection As ComputerCollection)
Private Sub adSync_SearchCompleted(ByVal ouNodes As TreeNode) Handles adSync.SearchCompleted
    Dim display As New displayResult(AddressOf AddOUToTreeView)
    Me.Invoke(display, New Object() {ouNodes})

    'Check for any computers which are no longer in Active Directory
    For Each compComparison As String In compGUIDComparison
        Dim query = From comp As Computer In computers Where comp.GUID = compComparison Select comp

        'If this computer is no longer in Active Directory remove it
        If query.Count > 0 Then
            computers.Remove(query(0))
        End If
    Next

    Dim macAddresses As New findMACAddresses(AddressOf GetMacAddresses)
    Me.Invoke(macAddresses, New Object() {computers})

End Sub
'[/ADSearcher Events]

'[ComputerSearcher Events]
Private Delegate Sub displayCompStatus(ByVal pcsFound As Integer)
Public Sub adSync_CompResultFound(ByVal pcsFound As Integer)
    Dim updateStatus As New displayCompStatus(AddressOf statusDialog.UpdateCompSearcherStatus)
    Me.Invoke(updateStatus, New Object() {pcsFound})
End Sub

Private Delegate Sub newComputer(ByVal computer As Computer)
Public Sub adSync_ComputerFound(ByVal name As String, ByVal fqdn As String, ByVal path As String, ByVal guid As String)
    Dim computer As New Computer
    computer.Name = name
    computer.FQDN = fqdn
    computer.Path = path
    computer.GUID = guid

    compGUIDComparison.Add(guid)

    Dim query = From comp As Computer In computers Where comp.GUID = computer.GUID Select comp

    If query.Count <= 0 Then 'If the computer hasn't already been discovered add it to the collection

        If Me.InvokeRequired Then
            Dim pc As New newComputer(AddressOf Me.computers.Add)
            Me.Invoke(pc, computer)
        Else
            computers.Add(computer)
        End If

    End If

End Sub
'[/ComputerSearcher Events]

ADSearcher Class

Sub New()
    SearchScope = DirectoryServices.SearchScope.OneLevel

    'Create reset events for the ComputerSearcher class threads
    For i As Integer = 0 To compSearchThreads.Count - 1
        compSearchThreads(i) = New ManualResetEvent(True)
    Next

End Sub

<MTAThread()>
Public Sub StartSearch()

#If Not Debug Then
    Try
#End If

    Dim rootEntry As New DirectoryEntry(RootPath)
    Dim rootNode As New TreeNode(rootEntry.Name)
    rootNode.Name = rootEntry.Path

    If Not IntegratedAuthentication Then
        rootEntry.Username = UserID
        rootEntry.Password = Password
    End If

    Dim searcher As New DirectorySearcher(rootEntry)
    searcher.PropertiesToLoad.AddRange(PropertiesToLoad)
    searcher.SearchScope = SearchScope
    searcher.PageSize = PageSize
    searcher.ServerTimeLimit = New TimeSpan(0, 10, 0)
    searcher.Filter = FilterString

    Dim queryResults As SearchResultCollection
    queryResults = searcher.FindAll()

    Dim result As SearchResult
    For Each result In queryResults

        Dim freeThread As Integer = WaitHandle.WaitAny(compSearchThreads)

        compSearchInstances(freeThread) = New ComputerSearcher(compSearchThreads(freeThread))

        With compSearchInstances(freeThread)
            .FilterString = "(objectCategory=computer)"
            If Not IntegratedAuthentication Then
                .UserID = UserID
                .Password = Password
            End If
            .PageSize = PageSize
            .SearchScope = SearchScope
            .PropertiesToLoad = PropertiesToLoad
        End With

        ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf compSearchInstances(freeThread).FindComputers), result)

        Dim childNode As New TreeNode(CStr(result.Properties("name")(0)))
        childNode.Name = result.Path
        childNode.Tag = result.Properties("objectGUID")(0)
        rootNode.Nodes.Add(SearchSub(result, childNode))

        ouResultCount += 1
        RaiseEvent OUResultFound(ouResultCount)
    Next

    WaitHandle.WaitAll(compSearchThreads)
    RaiseEvent SearchCompleted(rootNode)
    ouResultCount = 0 'Reset the result count


#If Not Debug Then
    Catch Ex as Exception
        MsgBox(ex.Message, MsgBoxStyle.Critical)
    End Try
#End If

ComputerSearcher Class

Sub New(ByVal doneEvent As ManualResetEvent)
    _doneEvent = doneEvent

    'Add Event Handler
    AddHandler Me.ComputerFound, AddressOf frmMain.adSync_ComputerFound
    AddHandler Me.IncrementCompResult, AddressOf frmMain.adSync_CompResultFound
End Sub

Public Sub FindComputers(ByVal parent As SearchResult)
#If Not Debug Then
    Try
#End If

    _doneEvent.Reset() 'Signal that the thread is working

    Dim subEntry As New DirectoryEntry(parent.Path)

    If Not IntegratedAuthentication Then
        subEntry.Username = UserID
        subEntry.Password = Password
    End If

    Dim searcher As New DirectorySearcher(subEntry)
    searcher.PropertiesToLoad.AddRange(PropertiesToLoad)
    searcher.SearchScope = SearchScope
    searcher.PageSize = PageSize
    searcher.ServerTimeLimit = New TimeSpan(0, 10, 0)
    searcher.Filter = FilterString

    Dim queryResults As SearchResultCollection
    queryResults = searcher.FindAll()

    Dim result As SearchResult
    For Each result In queryResults
        pcResultCount += 1

        Dim dNSHostName As String
        If result.Properties.Contains("dNSHostName") Then 'If the computer object has a value in dNSHostName (FQDN) store it else store the basic name
            dNSHostName = result.Properties("dNSHostName")(0)
        Else
            dNSHostName = result.Properties("name")(0)
        End If

        RaiseEvent ComputerFound(result.Properties("name")(0), dNSHostName, result.Path, result.Properties("objectGUID")(0).ToString)
        RaiseEvent IncrementCompResult(pcResultCount)

    Next

    _doneEvent.Set() 'Signal that the thread is finished

#If Not Debug Then
    Catch Ex as Exception
        MsgBox(ex.Message, MsgBoxStyle.Critical)
    End Try
#End If

End Sub
Daniel
  • 1,399
  • 3
  • 16
  • 29
  • Is `frmMain` the name of your form _class_ or is it the name of a variable? In VB.Net you can open a form by just referring to its name without using `Dim`. This is called default instances. This is a feature added to make it easier to migrate VB6 programs to .Net. They're a pain in the a** You my be running into that. – Chris Dunaway Sep 25 '14 at 21:54
  • frmMain is the name of the form class. I have asked this question before when using a variable and know how to solve that. Hence this question being marked as duplicate. However this time as it is the main startup form I can't create an instance in a variable (to my knowledge anyway). So I'm not sure how I can reference the existing instance of the form. – Daniel Sep 26 '14 at 08:18
  • Create a class (e. g. Program.vb). In that class add a `Public Shared Sub Main`. In that method, you can create an instance of your main form and then call `Application.Run(mainFormInstance)` In your project's properties, set your start up object to Sub Main. – Chris Dunaway Sep 26 '14 at 13:55

1 Answers1

0

Replace the following 2 lines:

AddHandler Me.ComputerFound, AddressOf frmMain.adSync_ComputerFound
AddHandler Me.IncrementCompResult, AddressOf frmMain.adSync_CompResultFound

with:

AddHandler Me.ComputerFound, AddressOf My.Forms.frmMain.adSync_ComputerFound
AddHandler Me.IncrementCompResult, AddressOf My.Forms.frmMain.adSync_CompResultFound

This is another example of VB default form instances shooting developers in the foot.

Dave Doknjas
  • 6,394
  • 1
  • 15
  • 28
  • Thank you for your answer. I have tried your suggestion but it's still creating a new instance of the form. – Daniel Sep 26 '14 at 08:16