Chris, it is against forum rules to attach binary files or archives containing binary files. You need to remove that attachment and attach the project it was built from.
Printable View
Chris, it is against forum rules to attach binary files or archives containing binary files. You need to remove that attachment and attach the project it was built from.
Oh sorry, I thought it was just EXE's not DLLs as well. I'll remove it and upload the solution file later today.
Hi chris123, I have been using your ADtreeView and just want to say its made my life allot easier so thanks very much. Looking forward to the filter update as mentioned in previous posts.
Thanks for the positive feedback :) I'll try and spend some more time on this soon and get the filter option sorted.
And sorry I forgot to remove the DLL file and upload the project file JMC, I'll do it tonight!
By the way, do you know of any way to catch errors. In my project I require the user to type a domain name in, the project then converts the domain name to a usable LDAP format and calls the "LoadDirectoryObjectsAsync" method. This is fine unless the user types in the wrong domain name, the project returns a "DirectoryServicesCOMException" and closes. I have tried placing the method inside a try method and using a catch as exeption to try and grab hold of the error but it does not work. Any ideas?
Yeah the Try/Catch probably doesnt work because the control uses a background thread to actually do the AD searching so the exception is not actually raised from the method that you are calling. In the next version I will make it catch all errors in the internal code and either make it just raise an event and pass the exception information to that event, or make it return exception information in the LoadCompleted event.
As a workaround for now, you could try just using the Application.UnhandledException event and checking the type of exception thrown
New version (1-5) uploaded to original post in this thread. Here's the new stuff:
Property - ShowContainersAndOUsOnly
Set this to true before calling the LoadDirectoryObjectsAsync method and the treeview will only show the containers and OUs (and the top level domain) rather than showing users and groups etc
Property - LdapPrefix
This string will be added to any LDAP queries that are done from the control internally as a result of LoadDirectoryObjectsAsync being called - it will be added after the LDAP:// but before the rest of the LDAP path, so you can use it to specify a server for all of the LDAP queries to go to (very handy if like me you want to use this from a machine that is not joined to the domain you are loading). This prefix is not applied to the path that you specify initially when calling LoadDirectoryObjectsAsync though, but because you can specify that yourself you can just add the prefix in yourself.
Overloaded version of LoadDirectoryObjectsAsync
There is now a parameterless version of LoadDirectoryObjectsAsync which will attempt to bind to the root of the domain your computer is joined to, rather than you having to supply a specific LDAP path for the tree to start at.
Event - ExceptionThrown
Raised when an exception is thrown in the background thread that loads the AD objects, so now you can handle this to see why the tree view is not loading. The original exception is passed in along with a string containing any relevant additional information.
Righto, so I've a form with your control on it. I use the LoadDirectoryObjectsAsync method and......it does nothing. Nothing added to Treeview, no errors. Just...nothing.
Any idea what I might have not done?
lol was the first post too long to read :P There was a link in it that points you to this post for the description of all the methods and events etc:
http://www.vbforums.com/showpost.php...7&postcount=46
and this one shows the new methods/events in the updated version:
http://www.vbforums.com/showpost.php...3&postcount=67
The first method mentioned in that first link (LoadDirectoryObjectsAsync) is the one that actually loads the tree view, so that accepts a an argument that is the LDAP path you want to load :) Also, in the new version you can just not pass in any arguments and it will bind to the root of the domain.
Ah, you obviously replied to my pre-edit post ;)
I've found that method now but nothing happens. This is what I have and the treeview remains steadfastly empty.
Code:Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Me.AdTreeView1.ExpandGroups = True
Me.AdTreeView1.LoadDirectoryObjectsAsync("<My_LDAP_PATH">
End Sub
It might just be that it is taking a while to loop through all of the objects in AD - remember the method is Asynchronous so it works in the background. If you want to see which objects it is currently loading (to verify that it is actually doing something) then handle the LoadingObjectChanged event and use the e.CurrentObjectPath property that is passed in to that event. You can also handle the ExceptionThrown event to see if an error is occurring in the background thread that is stopping the treeview from loading the AD structure.
If you dont want the treeview to load the entire AD structure in one go, which can take a little while, then set the LoadOnDemand property to true before calling LoadDirectoryObjectsAsync.
Ah that was it. Was just taking a long time. Funnily enough, I built a Treeview for my app (see my sig) that does EXACTLY the same thing!
I didn't bother making it a separate control though. Now I can't decide whether to stick with my code or use your control ;)
ah cool, I might download your app and take a look :)
EDIT: Hmm when I run your application if I put my IP address in then the program freezes for quite a while (might be worth using a background thread so it doesnt do this) and then it comes up with the following error:
EDIT EDIT: Also - in the software tab it shows the Office 2007 SP2 about 7 times, shows 4 items that just have a weird symbol as the name, shows the MS Help Viewer twice, shows that I have both IE 7 and IE 8 installed (which is not possible), and a few other glitches. You might want to try using my class for enumerating software here: http://www.vbforums.com/showthread.php?t=598355 (it works on remote machines as well) :)Quote:
System.Reflection.TargetInvocationException: An exception occurred during the operation, making the result invalid --> System.Management.ManagementException: Not Found
It's a bit more limited as it doesn't show users (as that's not within the scope of my application).
Well the only issue with your treeview is that it doesnt display a + symbol next to any of the folders, but I dont know if you are too bothered about that :) See my previous post as well as I've edited some bugs in your app into there.
EDIT: Another issue with your app, some of the environmental variables are listed several times (TEMP and TMP in particular)
Also, the Mapped Drives section on the Summary tab only detects drives that have been mapped with the "reconnect at logon" box ticked, it does not detect drives mapped via logon script or group policy. If you want to get all mapped drives, just loop through the IO.DriveInfo.GetDrives collection and check the DriveType property of each one to see if it is a network drive - then to get the UNC path that the drive is mapped to, you can use this Windows API that I posted on my blog a while ago: http://cjwdev.wordpress.com/2009/12/...network-drive/
Thanks for the comments. I'll look into the GetDrives method. As for the environment variables, I know about that one. It's because it lists all the environment variables for all users.
Edit: The GetDrives method doesn't seem to support execution against remote machines. Now that I think about it, I remember trying it way back when. I've never found a fully reliable method for enumerating all network drives on remote machines.
Also, my Treeview does show + symbol next to the folders but only after they've been enumerated.
Wow, I love how this work, but I have one issue with it. I am using 2010 express and compiling in 2.0. I am not able to use the control with 2.0. If I change it to 4.0, I can use the control and it works. Am I missing something?
I think I found the issue. 2010 Express will force you to convert your old project when you first load it. Once that's done you can go back and change the framework from 4.0 to 2.0 and it will convert it back to the 2.0 framework. It looks like something goes wrong with that conversion back to 2.0. It's still trying to reference some 4.0 stuff. I used another computer that had 2005 express on it to compile the dll. I then imported it with my 2010 project and it works!
I am having an issue with it hanging when I click on the "groups" in my domain. We have a few hundred, but it should not take that long. I have another OU that contains about 1300 more OU's and it loads in just a few seconds. Any ideas?
Thanks! Chad
Ah yeah I have seen that problem actually with 2010 not working very well when you change the target framework. It does my head in because on my work PC for some reason when I have any project target 3.5 or 2.0 it goes slow as hell so I just make them target 4.0 and it runs fine, but then when I want to work on the project at home I change it back to targeting 3.5 and it almost always screws something up :(
As for the groups - have you got it set so that it displays members of the groups? I think the property is called ExpandGroupMembers or something like that. If so then do you mean when you try to expand a group or do you just mean when you click on a group to select it?
I think it's correct. This is what I'm doing:
Me.AdTreeView1.ExpandGroups = True
Me.AdTreeView1.LoadOnDemand = True 'only load as items are clicked
Me.AdTreeView1.Scrollable = True
Me.AdTreeView1.LoadDirectoryObjectsAsync(strMyDomainTree)
Right so you are using the expand groups option - I'm afraid there's no way I can make that part any faster as this is all the code that is responsible for getting the group members:
and that is the only way I know of that you can get the group members.vb Code:
Dim Members As Object = ObjectDE.Invoke("Members", Nothing) '<<< Get members For Each Member As Object In CType(Members, IEnumerable) '<<< loop through members Dim CurrentMember As New DirectoryEntry(Member) '<<< Get directoryentry for user ChildNode.Nodes.Add(New AdTreeNode(CurrentMember.Name.Remove(0, 3), CurrentMember.Properties("distinguishedName").Value.ToString, GetAdObjectType(CurrentMember))) '<<< Add this node to our group node (which will later be added to the tree view) Next
Do you really need to be able to expand groups? To be honest I couldn't see much use for that option when I added it. I mean if you are using this control to let the user of your application select a User or Group from AD then they dont need to be able to expand groups... they just need to know where the user or group is.
Well after 60 seconds I get this:
The CLR has been unable to transition from COM context 0x20a310 to COM context 0x20a480 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.
I really dont need to expand the groups. I did not know it would enumerate all the members of each group for the entire groups OU. I was hopping it would just show the members when I click on that group within the OU. Anyway, great work and this will really save me a bunch of time:-)
Did you ever get the search filter working?
I never added the ability to specify exactly which objects you want to be visible but I did add a property that lets you choose to only show containers and OUs if that is of any use?
Hi Chris
Firstly, thanks for posting this. Works a treat. I'm just having a small issue with the icons.. In my tree view, all objects appear using the same icon (the computer icon). I followed your instructions with extracting the icons, creating an image list and attaching this to the tree view. Not sure what I might be doing wrong?
Cheers
Jay
You probably didn't name the images correctly then :) You need to set the ImageKey property of each image in the image list to the names that I mentioned in my original post.
Having said that, I'm working on a much better version that will handle the icons for you and be faster and behave more like other .NET dialog windows.
It's always something simple - I had left the file extension in the name. Look forward to checking out the new version! Cheers, Jay
OK new version is now uploaded to the original post in this thread :)
Can't wait to try it out. Thank you! The previous version has been a favorite tool. I'm looking forward to updating all my programs withthis new one.
Thanks Robert :) let me know how it goes and if you have any feedback
I'll also be releasing another dialog window soon that can be used to select users/groups/computers from AD, similar to the native window built in to Windows shown below:
http://www.question-defense.com/wp-c...-windows-7.gif
Is it possible to start in a specified OU, such as domain.org/Computers? I'm using this to select the container in a program that will add computers to AD, I already know they will go in this container.
Our AD structure is highly organizational under that first OU. Also, it takes a while for this to load everything as talked about. It looks like you are looking at what children there are and then looking to see if the children have children. For me that's very time consuming. Anyway to just do the first query for each level?
Actually there is a way. I haven't figure out the little things about it but it's possible.
On the line in AdTreeViewForm.vb:
Using RootDirectoryEntry As New DirectoryEntry("LDAP://" & _DomainController & ":389/" & "OU=***,DC=***,DC=***")
This will take you there BUT, will not auto load that OU, nor show the OU in the tree. Anyone have any ideas? (This cuts out loading about 11000 users among other things for me.)
First I would like to say this app is greate.
I have one semi challenging question. Right now I am able to view the containers and select them in AD and at the bottom of your app it shows the LDAP path of the container you have selected.
What I want to do is search for a user and get the path of the container where it is located.
So my question is, is there anyway to search for a container in AD with out haveing to visually see it, rather then having the user visually click on the OU's and have the program tell what OU they have selected?
PS. I am very very sorry about the spelling.
Yeah that's easy, but nothing to do with my example here. Just use the built in DirectorySearcher class (in the System.DirectoryServices namespace) to search for the user by their username (sAMAccountName) or whatever you want, and then get the DistinguishedName attribute from the user object as that has the full path to the object in it.
This will find me the samAccountName of the userid that is entered by the user.
So how do I use the second part of the code? How do I compare the samAccountName to the DistinguishedName attribute from the user object as that has the full path to the object in it?Code:dirEntry = New System.DirectoryServices.DirectoryEntry("LDAP://CUSTOMER.MPSRX.com")
dirEntry.Username = UserUsername
dirEntry.Password = userPassword
dirSearcher = New System.DirectoryServices.DirectorySearcher(dirEntry)
dirSearcher.Filter = "(samAccountName=" & USERID & ")"
Well you don't want to compare it, you just want to read it. So as a very basic example:
That's off the top of my head and has no error handling etc in but hopefully gives you an ideavb Code:
dirEntry = New System.DirectoryServices.DirectoryEntry("LDAP://CUSTOMER.MPSRX.com") dirEntry.Username = UserUsername dirEntry.Password = userPassword dirSearcher = New System.DirectoryServices.DirectorySearcher(dirEntry) dirSearcher.Filter = "(samAccountName=" & USERID & ")" dirSearcher.PropertiesToLoad.Add("distinguishedName") MessageBox.Show(Cstr(dirSearcher.FindOne.Properties("distinguishedName")(0)))
Thanks very much for the response! Ok so I have everything working but one piece. Is there a way to move a user or object that you bind to up one or down one organizational unit with out binding to that OU directly?
The problem I am having is my company names there OU's a certain way, For example:
Company Sites
Site OU
Site PC Users
The computers and User lives in that Bottom OU. I want to move the user up one OU long enough to make changes to the PC's so ther Group Policy is not applied. But I cant compare the Username to the strings in the OU's because our users are usually something like : z_123 or some type of letter_and a three digit number, Not the Site Name.
So I am looking for an easy way to just move a user Up one OU and then back down with out excatly knowing the OU's Specific name I want to move it into and then back down?
Why do you want to move them into an OU and then straight back out of it? I know you said you don't want them to be in the upper OU long enough for GPOs linked to that OU to apply to them, but why move them in there at all?
Because the Global Policy is being applied at the lower level so I need to move the user up one level so the policy will become unapplied long enough for the application to configure the computer, once the application has configuered the computer correctly it will verify everything is correct move the user back down and reboot the computer.
Why can't the application configure the computer while the "global policy" is applied to it? What does the application need to do that the GPO is preventing? The idea of moving a user to another OU temporarily just so that something can work is a good sign of pretty bad GPO design. If the application needs to do something on the client computer that the user does not have permission to do (due to the GPO applying to that user) then it should not be running as that user.
Also you would have to do a gpupdate on the client computer for it to detect that the user has moved to a new OU as well, otherwise it could be up to 90 minutes between you moving the user in AD and the client machine actually picking up that change and applying (or removing in this case) the new GPOs it should apply.
The environment was configuered like this when I got here. I am just trying to work with what I have.
The application configures the laptop in 3 steps:
1. Renames it and Adds it to the domain and drops the pc in the proper OU.
Reboots
2. Moves the user up, Configures the laptop with the proper registry edits, etc.
Reboots
3. Adds the printers Moves the user back down
Reboots
Its complete.
So I need to be able to move a user up and down one level each way.
You still haven't answered the question of what exactly the application CAN'T do whilst the normal GPOs are applying. Registry edits should work fine regardless of what GPO settings you've got configured... if they didn't then half the programs your users use wouldn't work.
The Group Policy completly locks the computer down from ther user. And that is the user that has to be logged in when configuring the laptop becuase alot of the config changes we make are user based changes not global changes to the laptop. For example, Tabbed browsing, adding printers, Verifying if adobe is installed if it is installing it, things like that.
Yes but Group Policy generally just disables the GUI for changing something so that the user cannot change it, but a program changing it would still work most of the time. Have you actually tried running your program while the user is in the regular OU?
Hi Chris,
First of all I'd like to thank you for the code.
Further I have written a small recursive routine to populate the tree and since I have seen in one of the threads that you intended to do this I thought I'd post it.
If you have already done this then ignore this thread.
P.S. I have re-used some of your code to do this.
Code:
Private Sub btnConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnConnect.Click
m_userDomainName = ActiveDirectoryHelper.GetUserDomainName
m_domainController = ActiveDirectoryHelper.GetSingleDomainController(m_userDomainName, m_domainUserName, m_domainUserPassword)
Dim DomainObject As Domain = Domain.GetDomain(ActiveDirectoryHelper.GetDomainContext(m_userDomainName, m_domainUserName, m_domainUserPassword))
Dim RootNode As New TreeNode
Using RootDirectoryEntry As DirectoryEntry = DomainObject.GetDirectoryEntry
RootNode.Name = CStr(RootDirectoryEntry.Properties("distinguishedName").Value).Replace("/", "\/")
RootNode = adTree.Nodes.Add(RootNode.Name)
CreateTree(RootDirectoryEntry, RootNode)
End Using
End Sub
Private Sub CreateTree(ByVal dirEntry As DirectoryEntry, ByVal parentNode As TreeNode)
For Each ChildObj As DirectoryEntry In dirEntry.Children
'add it first
Dim childNode As New TreeNode
childNode.Name = CStr(ChildObj.Properties("distinguishedName").Value).Replace("/", "\/")
childNode = parentNode.Nodes.Add(childNode.Name)
'do the recursive call
CreateTree(ChildObj, childNode)
Next
End Sub
Dim SelectContainerDialog As New Cjwdev.ActiveDirectory.Dialogs.SelectAdContainerDialog
SelectContainerDialog.Title = ("Choose the department OU to search for Job Role groups.")
SelectContainerDialog.ShowDialog()
Chris the new dialog is too cool. Thank you. but, Is there a way to defalut to an expanded view? At least the first domain node. Oh the Mouse wheel scroll doesn't seem to work for me. How do I progamatically size the control?
Chris,
I'm using your code in a C# project. Do you have any idea why I'd be able to load the main now, obtain the distinguished name, but not be able to load the child nodes? When I expand the very first node, it just says loading. When I debug it, it goes through the steps just like it does in the VB project.
I can show how I'm implementing it of course, but wanted to see if you knew anything off the top of your head.
EDIT: It's throwing an exception in the LoadAdNodes method via LoadFinished.
But I've verified that the domain, domain controller, user, and password are all correct.Quote:
Logon failure: unknown user name or bad password
How are you specifying the username? Try using DOMAIN\Username if you were just using Username on its own
Or did it work fine with the same username and password in my VB example?
I got it to work. I pasted something wrong. When I deleted everything and repasted, it worked.
But, it won't work if I leave the password as an empty string. I can't look through your code right now, but I thought if a password wasn't passed, it would still work if the current user has access. Which I remember it doing, but if I pass a an empty string to it in my project, it fails. I have to pass the password to it.
Chris,
Also, is there an easy way to get the attributes for each container? For the attributes you see in ADSI Edit.
EDIT: Never mind. I figure it out. You were already populating this data via ChildObject. So I created a property for AdTreeNode that is a List<string>. Then I iterated through the property list and added each one to the AdTreeNodes property list.
Oh. Ok. That should be easy. One more thing, if you don't mind. This is the code I'm using to get the OU/Container attributes and their values.
C# Code:
foreach (PropertyValueCollection p in ChildObject.Properties) { ActiveDirectoryAttributeList ADAList = new ActiveDirectoryAttributeList(); ADAList.Attribute = p.PropertyName; ADAList.AttributeValue = p.Value.ToString(); ChildNode.Attributes.Add(ADAList); }
Your code defines ChildObject as a DirectoryEntry, with which you use to loop through the RootDirectory.Children. I then loop through all of the properties for the ChildObject to get the attributes and attribute values. I add those two data points to a list of a custom class called ActiveDirectoryAttributeList. I then add them to a property of AdTreeNode I added, called Attributes. All of that works just fine. I can see the attributes and their values during runtime, but it only pulls some attributes. Not all of them.
ADSI Edit for Engineering OU:
http://i.imgur.com/idF03.png
My app on that same OU:
http://i.imgur.com/LsiR1.png
Is it only pulling the ones that actually have values in them? (<Not Set> in ADSI edit means NULL)
Nice piece of code. I tried to use some of the code in my app.
"Using SelectContainerDialog As New cjwdev.ActiveDirectory.Dialogs.SelectAdContainerDialog"
but I get this error:
"Type 'cjwdev.ActiveDirectory.Dialogs.SelectAdContainerDialog' is not defined."
What am I missing?
Thanks
the WPF version of this would be fantastic...