Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Chris Miller on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Code works fine until I put it into an HTA

Status
Not open for further replies.

kmcferrin

MIS
Jul 14, 2003
2,938
US
OK, so I have a number of scripts that do AD queries and other administrative reporting tasks, but I want to put them in a nice GUI, so I'm making an HTA. I figure this should be a cakewalk since most of the code and logic is already written, I just need to build the interfaces around it and account for all of the checkboxes, etc. But I have this piece of code that just plain doesn't work in the HTA, even though it does work perfectly fine when run from the command prompt.

The code is in a Sub that is supposed to get a collection of groups back from AD, and then check the groups to see if there are any members. Now these aren't normal memberships because these groups are used with Services for Unix, so the membership info is stored in an extended attribute called "memberUid" and the syntax is "IA5-String", and the string is semi-colon delimited (from ADSI Edit).

Code:
    Dim strEmailTo, strSMTPServer, strEmailFrom, strNotifyMessage, strUserMsg, dictResults, strDictReturn
    Set dictResults = CreateObject("Scripting.Dictionary")
    
    Sub RunReport
        strUserMsg = ""
        strNotifyMessage = ""
        strSMTPServer = "mail.domain.com"
        strEmailFrom = "address@domain.com"
        strEmailTo = EmailAddrBox.Value
        If OS(0).Checked Then
            GetUnix
        ElseIf OS(1).Checked Then
            GetWindows
        ElseIf OS(2).Checked Then 
            GetUnix ' Note:  Because of the way that the Unix list checks for members, the Unix search must always come before any other.
            GetWindows
        End If
        Notify
    End Sub

    Sub GetUnix
        Set objConnection = CreateObject("ADODB.Connection")
        objConnection.Open "Provider=ADsDSOObject;"
        Set objCommand = CreateObject("ADODB.Command")
        objCommand.ActiveConnection = objConnection
        objCommand.CommandText = "<GC://EDITED FOR PRIVACY REASONS-BUT IT IS CORRECT>;(&(objectCategory=group)(name=*_root));distinguishedName,name;subtree"
        Set objRecordSet = objCommand.Execute
        If objRecordset.EOF Then
            strUserMsg = strUserMsg & "No Unix groups found ending with '_root'."
        Else
            While Not objRecordset.EOF
                strCN = objRecordset.Fields("distinguishedName")
                On Error Resume Next
                strTemp = ""
                strMember = ""
                Set objRootDSE = GetObject("LDAP://rootDSE")
                Set objItem = GetObject("LDAP://" & strCN)
                strMember = objItem.GetEx("memberUid")
                For Each Item in strMember
                    strTemp = strTemp & Item & "  "
                Next       
                If strTemp = "  " Then               
                Else
                    strUserMsg = strUserMsg & UCase(objRecordset.Fields("name")) & ":<br>Members:<br>"& strTemp & "<br>"
                    dictResults.Add UCase(objRecordset.Fields("name")), strTemp 
                    'MsgBox UCase(objRecordset.Fields("name")) & "member ." & strTemp & "."
                End If
                objRecordset.MoveNext
            Wend
        End If
        objConnection.Close
        MsgBox strUserMsg
    End Sub
When I call the GetUnix Sub from within my HTA, it doesn't return any results. Now this code worked fine before I put it into the HTA, and I copied it out of the HTA and put it in a new file that looks like this:

Code:
strUserMsg = ""
GetUnix
Wscript.Echo strUserMsg

    Sub GetUnix
        Set objConnection = CreateObject("ADODB.Connection")
        objConnection.Open "Provider=ADsDSOObject;"
        Set objCommand = CreateObject("ADODB.Command")
        objCommand.ActiveConnection = objConnection
        objCommand.CommandText = "<GC://EDITED FOR PRIVACY REASONS-BUT IT IS CORRECT>;(&(objectCategory=group)(name=*_root));distinguishedName,name;subtree"
        Set objRecordSet = objCommand.Execute
        If objRecordset.EOF Then
            strUserMsg = strUserMsg & "No Unix groups found ending with '_root'."
        Else
            While Not objRecordset.EOF
                strCN = objRecordset.Fields("distinguishedName")
                On Error Resume Next
                strTemp = ""
                strMember = ""
                Set objRootDSE = GetObject("LDAP://rootDSE")
                Set objItem = GetObject("LDAP://" & strCN)
                strMember = objItem.GetEx("memberUid")
                For Each Item in strMember
                    strTemp = strTemp & Item & "  "
                Next       
                If strTemp = "  " Then               
                Else
                    strUserMsg = strUserMsg & UCase(objRecordset.Fields("name")) & ":<br>Members:<br>"& strTemp & "<br>"
                    'dictResults.Add UCase(objRecordset.Fields("name")), strTemp 
                    'MsgBox UCase(objRecordset.Fields("name")) & "member ." & strTemp & "."
                End If
                objRecordset.MoveNext
            Wend
        End If
        objConnection.Close

    End Sub

And it returns the intended results. It literally is the same code.

The Windows Sub isn't used in this case, so I left it out. The Notify Sub just parses strUserMsg and generates either a text, file, csv, email, etc, so I left it out too.

I've added some comments during testing to try to find the error, and the problem seems to come from "strMember = objItem.GetEx("memberUid")". Even if there are no members to the group, it looks like it returns something for "memberUid", I'm not sure if it's empty, null, or whatever. But when the group does have members on the "memberUid" attribute it still doesn't return the actual account names (as long as I'm running it from within the HTA). At first I thought that maybe it was related to being inside a subroutine, which is why I made that separate test file. But that's not it. As near as I can tell, the only significant difference is that it's running from an HTA.

Does anyone have any ideas or suggestions?

One last clue, if I remove the "On Error Resume Next" I get different errors depending on how it's run. Via cscript.exe I get:

C:\test.vbs(22, 17) Active Directory: The directory property cannot be found in the cache.

Via the HTA I get:

Line: 68
Error: Illegal assignment: 'Item'

The line 68 is "strMember = objItem.GetEx("memberUid")", but I've noticed that HTA tend to report errors being on the line previous to where the error actually is (i.e., the last successfully completed line).
 
Stupid array of variants gets me every time. So the fix was to replace this:

Code:
                strMember = objItem.GetEx("memberUid")
                For Each Item in strMember
                    strTemp = strTemp & Item & "  "
                Next       
                If strTemp = "  " Then               
                Else
                    strUserMsg = strUserMsg & UCase(objRecordset.Fields("name")) & ":<br>Members:<br>"& strTemp & "<br>"
                    'dictResults.Add UCase(objRecordset.Fields("name")), strTemp
                    'MsgBox UCase(objRecordset.Fields("name")) & "member ." & strTemp & "."
                End If

With this:

Code:
                strMember = Join(objItem.GetEx("memberUid"))
                arrMember = Split(strMember, " ")
                For i = 0 to Ubound(arrMember)
                    strTemp = strTemp & arrMember(i) & "<br>"
                Next
                If strTemp = "" Then               
                Else
                    strUserMsg = strUserMsg & UCase(objRecordset.Fields("name")) & ":<br>Members:<br>"& strTemp & "<br>"
                    dictResults.Add UCase(objRecordset.Fields("name")), strTemp 
                End If

Now it works just fine. As to why it worked fine the other way when I ran it from CSCRIPT.EXE but won't work in the HTA, I have no idea. And yes, it does seem silly that I'm retrieving and array into strMember by joining all the elements together and then I'm later splitting strMember into an array, but that's the way it works. It won't let me treat strMember as an array.
 
Why would you use [tt]On Error Resume Next[/tt] where you have it anyway?

This is poor form, and not how the statement is meant to be used. It just masks errors and makes diagnosis more difficult.


Is it possible you copied this code from those hacks at Microsoft? I notice they write lots of very poor VBScript and foist it on the BOFH population.


I have no idea how this supposedly worked under the CScript host. That method call may return a collection of Strings, not an array of Strings. If so you'd need to say:

[tt]Set colMembers = objItem.GetEx("memberUid")
For Each strMember In colMembers[/tt]

... or something to that effect. I doubt that though.

Maybe it really does return an array, but you need to declare the variable as in:

[tt]Dim strMember()[/tt]

ADSI is pretty goofy generally. Exploring the [tt]ActiveDs[/tt] type library from VB6's Object Browser I see the return from [tt]GetObject("LDAP://...")[/tt] is an object of type [tt]IADs[/tt]. This has a method named [tt]GetEx()[/tt] that returns a value, but the typelib doesn't say what type. I guess it's just a Variant containing who-knows-what. Looks like sloppy work on Microsoft's part, but I suppose they might have their reasons.

At least you have it working.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top