UserPrincipal.Save() - vshost32.exe已停止工作 - 损坏堆

时间:2015-07-08 09:31:11

标签: vb.net directoryservices

我正在编写一些软件以将CSV文件导入Active Directory(以创建用户帐户)。在某些时候,我知道它正在完美地导入多个帐户。我不知道自从我上次工作以来已经有一段时间我改变了什么。但它现在成功导入2个帐户,然后在第三次循环迭代期间在下面的行中崩溃(但仍然创建了第三个帐户):

newUser.Save()

当它崩溃时我收到错误“vshost32.exe已停止工作”。然后,我启用了本机代码调试,现在出现此错误:“0xC0000374:堆已损坏”和InvalidCastException(请参阅发布结束时的立即窗口以获取完整错误)。为了测试我一直在删除并重新创建相同的帐户。如果我不删除前三个帐户,则处理主体存在异常,然后程序在第4次迭代时崩溃,然后是第5次,依此类推。但它从未在前两个崩溃。 (我导入的数据除了数字之外是相同的 - 例如,sAMAccountNames:Test1,Test2,Test3等)

我的代码

Private Sub bwImport_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bwImport.DoWork
    Dim _worker As BackgroundWorker = CType(sender, BackgroundWorker)

    Dim beginImport As New StartImport(AddressOf progressForm.StartImport)
    Me.Invoke(beginImport, New Object() {dtUsers.Rows.Count})

    Dim log As New UpdateLog(AddressOf progressForm.UpdateLog)

    '### TO DO: Check that all mandatory columns/attributes are in the DataTable

    '### TO DO: Check for duplicate sAMAccountNames, userPrincipalNames and Cononical Names

#If Not Debug Then
    Try
#End If

    Dim rowNum As Integer = 0 'Keep track of how many accounts have been created
        For Each row As DataRow In dtUsers.Rows
            Dim newUser As UserPrincipalEx = New UserPrincipalEx(adCtx)

            newUser.SamAccountName = row("sAMAccountName")
            newUser.SetPassword(row("Password"))

            'General Tab (of Template Exporter)
            If row.Table.Columns.Contains("initials") Then
                newUser.Initials = row("initials")
            End If

            If row.Table.Columns.Contains("givenName") Then
                newUser.GivenName = row("givenName")
            End If

            If row.Table.Columns.Contains("sn") Then
                newUser.Surname = row("sn")
            End If

            If row.Table.Columns.Contains("displayName") Then
                newUser.DisplayName = row("displayName")
            End If

            If row.Table.Columns.Contains("description") Then
                newUser.Description = row("description")
            End If

            If row.Table.Columns.Contains("physicalDeliveryOfficeName") Then
                newUser.Office = row("physicalDeliveryOfficeName")
            End If

            If row.Table.Columns.Contains("telephoneNumber") Then
                newUser.TelephoneNumber = row("telephoneNumber")
            End If

            If row.Table.Columns.Contains("wWWHomePage") Then
                newUser.WebPage = row("wWWHomePage")
            End If

            'Address Tab (of Template Exporter)
            If row.Table.Columns.Contains("streetAddress") Then
                newUser.Street = row("streetAddress")
            End If

            If row.Table.Columns.Contains("postOfficeBox") Then
                newUser.POBox = row("postOfficeBox")
            End If

            If row.Table.Columns.Contains("l") Then 'City
                newUser.City = row("l")
            End If

            If row.Table.Columns.Contains("st") Then 'State/Province
                newUser.State = row("st")
            End If

            If row.Table.Columns.Contains("postalCode") Then
                newUser.PostCode = row("postalCode")
            End If

            '### TO DO: Add country fields

            'Account Tab (of Template Exporter)
            If row.Table.Columns.Contains("userPrincipalName") Then
                newUser.UserPrincipalName = row("userPrincipalName")
            End If

            If row.Table.Columns.Contains("ResetPassword") Then
                If row("ResetPassword").ToString.ToLower = "yes" Then
                    newUser.ExpirePasswordNow() 'Force the user to change their password at next logon
                End If
            End If

            If row.Table.Columns.Contains("PreventPasswordChange") Then
                If row("PreventPasswordChange").ToString.ToLower = "yes" Then
                    newUser.UserCannotChangePassword = True
                End If
            End If

            If row.Table.Columns.Contains("PasswordNeverExpires") Then
                If row("PasswordNeverExpires").ToString.ToLower = "yes" Then
                    newUser.PasswordNeverExpires = True
                End If
            End If

            If row.Table.Columns.Contains("AccountDisabled") Then

                If row("AccountDisabled").ToString.ToLower = "yes" Then
                    newUser.Enabled = False
                Else
                    newUser.Enabled = True
                End If

            Else 'Enable the account by default if not specified
                newUser.Enabled = True
            End If

            If row.Table.Columns.Contains("accountExpires") Then
                Dim expireyDate As Date
                Date.TryParse(row("accountExpires"), expireyDate) 'Try to convert the data from row("accountExpires") into a date
                newUser.AccountExpirationDate = expireyDate
            End If

            'Profile Tab (of Template Exporter)
            If row.Table.Columns.Contains("profilePath") Then
                newUser.ProfilePath = row("profilePath")
            End If

            If row.Table.Columns.Contains("scriptPath") Then
                newUser.ScriptPath = row("scriptPath")
            End If

            If row.Table.Columns.Contains("homeDrive") Then
                newUser.HomeDrive = row("homeDrive")
            End If

            If row.Table.Columns.Contains("homeDirectory") Then
                newUser.HomeDirectory = row("homeDirectory")
            End If

            'Telephones Tab (of Template Exporter)
            If row.Table.Columns.Contains("homePhone") Then
                newUser.HomePhone = row("homePhone")
            End If

            If row.Table.Columns.Contains("pager") Then
                newUser.Pager = row("pager")
            End If

            If row.Table.Columns.Contains("mobile") Then
                newUser.Mobile = row("mobile")
            End If

            If row.Table.Columns.Contains("facsimileTelephoneNumber") Then
                newUser.Fax = row("facsimileTelephoneNumber")
            End If

            If row.Table.Columns.Contains("ipPhone") Then
                newUser.IPPhone = row("ipPhone")
            End If

            'Organization Tab
            If row.Table.Columns.Contains("title") Then
                newUser.Title = row("title")
            End If

            If row.Table.Columns.Contains("department") Then
                newUser.Department = row("department")
            End If

            If row.Table.Columns.Contains("company") Then
                newUser.Company = row("company")
            End If

        rowNum += 1
        _worker.ReportProgress(rowNum) 'Update progress dialog

        Try
            newUser.Save() 'Save the user to Active Directory
            Me.Invoke(log, New Object() {"Successfully created " + row("sAMAccountName") + " (" + row("displayName") + ")", frmProgress.LogType.Success})
        Catch ex As PrincipalExistsException
            Me.Invoke(log, New Object() {"Error creating " + row("sAMAccountName") + " (" + row("displayName") + "). " + ex.Message, frmProgress.LogType.Failure})
            Continue For
        End Try

        'Member Of Tab
        If row.Table.Columns.Contains("MemberOf") Then
            Dim groups() As String = row("MemberOf").ToString.Split(";")

            'Add the user to any specified groups
            Dim groupPrincipal As GroupPrincipal

            Try 'Try adding group(s)

                For Each group As String In groups
                    groupPrincipal = groupPrincipal.FindByIdentity(adCtx, group) 'Search for the group name, sid, sAMAccountName or display name

                    If groupPrincipal IsNot Nothing Then
                        groupPrincipal.Members.Add(newUser) 'Add the user to the group
                        groupPrincipal.Save()
                    Else
                        Me.Invoke(log, New Object() {"Unable to add " + row("sAMAccountName") + " to group: " + group + ". Group not found.", frmProgress.LogType.Failure})
                    End If

                Next

            Catch ex As PrincipalExistsException
                '### TO DO: Try to get group name in exception
                Me.Invoke(log, New Object() {"Error adding " + row("sAMAccountName") + " (" + row("displayName") + ") to " + "group(s). " + ex.Message, frmProgress.LogType.Failure})
            End Try

        End If

        newUser.Dispose() 'Dispose of the newUser object

        Next

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


End Sub

立即窗口(启用本机调试时)

Critical error detected c0000374
First-chance exception at 0x76fbf996 in AD User Importer.exe: 0xC0000374: A heap has been corrupted.
A first chance exception of type 'System.InvalidCastException' occured in System.DirectoryServices.AccountManagement.dll

1 个答案:

答案 0 :(得分:1)

我认为您的问题出在这个问题上:

Try
    newUser.Save() 'Save the user to Active Directory
    Me.Invoke(log, New Object() {"Successfully created " + row("sAMAccountName") + " (" + row("displayName") + ")", frmProgress.LogType.Success})
Catch ex As PrincipalExistsException
    Me.Invoke(log, New Object() {"Error creating " + row("sAMAccountName") + " (" + row("displayName") + "). " + ex.Message, frmProgress.LogType.Failure})
    Continue For
End Try

此代码可处理异常,但在继续下一个循环迭代之前不会处理newUser

我最近开始收到类似的堆异常,经过仔细检查,我意识到我没有在处理UserPrincipal对象。一旦我正确处置了该物体,问题似乎就停止了。

您应该将newUser对象包装在Using block中:

For Each row As DataRow In dtUsers.Rows
    Using newUser As UserPrincipalEx = New UserPrincipalEx(adCtx)
        newUser.SamAccountName = row("sAMAccountName")
        newUser.SetPassword(row("Password"))

        ' ... the remainder of the code
        ' ... now wrapped in a Using block
    End Using
Next

无论如何退出Using块,newUser对象都会自动处理。由于Using块为您处理,因此您可以删除对newUser.Dispose()的显式调用。

如果在您的VB.Net版本中using块不可用,则应将循环包装在Try...Finally块中,并将newUser明确地放置在Final块中。