Click here to Skip to main content
13,861,323 members
Click here to Skip to main content
Add your own
alternative version


35 bookmarked
Posted 21 Aug 2008
Licenced CPOL

ADSI Hunter

, 26 Oct 2010
Rate this:
Please Sign up or sign in to vote.
Active Directory lookup utility


Yes, this image has been doctored so that the domain names are not visible. When the application starts, it will automatically populate the Domains window with all known (to the system) domains.


If you work on a Microsoft network, chances are you're using Active Directory (AD). Active Directory stores information about network resources for a domain. This information requires specific authority for update, but is typically open to authenticated users for query.

I developed this tool to allow for exactly these queries. It provides a list of known (to the network) domains, and allows the user to view groups, group membership, users, and user details without the need to dive into LDAP queries. In short, it's easy to use, quick, and provides more information than the typical user really needs.

This tool was developed using the .NET Framework 2.0 only. There are no interop assemblies or Win32 API calls involved in ADSI operations, there is one Win32 API called for the About box animation. This is a .NET 2.0 Windows Forms application.


Many of the organizations that I work for utilize AD to manage application and resource access by groups. Unfortunately for me (and others), many of these organizations do not permit access to the Microsoft Active Directory tools, so verifying that a particular user has been given membership to a particular group can be a bit of a pain. Hence, this tool was born.

Using the Code

The UI itself is pretty straightforward. Just a typical .NET Windows Forms application. The meat of the application is located in the ADLookup class. This class performs all of the AD activities used to populate the lists in the UI. Perusing the source will provide you with an introduction (possibly a rude one) to the world of AD searches in the .NET environment.

If you look at the image above, the arrows indicate that a selection in a list will trigger the automatic update of the list being pointed to. In addition, if a user is selected from the Users in Group list, that user will be selected in the Domain Users list as well, triggering subsequent updates. Likewise, a selection in the Groups for User list will select that group in the Groups in Domain list, triggering subsequent updates. The numbers in parenthesis above each list indicate how many elements are in the list. This gives an at-a-glance answer to one of the most common AD questions: "How many users are in group xx?"


To utilize the search, you need to select a search option from the Search menu, or you can right-click on either the Groups in Domain, Users in Group, or Users in Domain lists. When you select one, a pop-up window will display for you to enter your search data. The search data you enter is used as a Regular Expression to evaluate against the data in the selected list, so feel free to use .NET Regular Expressions to perform your fuzzy search.

Only the first match of your search criteria is selected. When it is selected, the appropriate lists will be updated in their content as well.

Points of Interest

There are three methods in the ADLookup class that deserve a little attention here. These three methods are used to decode arrays of bytes that are returned from the AD query in the user properties collection.

First, the easy one - SIDToString:

/// <summary>

/// Convert a binary SID to a string.
/// </summary>
/// <param name="sidBinary">SID to convert.</param>
/// <returns>String representation of a SID.</returns>
private string SIDToString(byte[] sidBinary)
    SecurityIdentifier sid = new SecurityIdentifier(sidBinary, 0);
    return sid.ToString();

The best part of this method is that there's virtually nothing to converting a Windows SID (security identifier) bit array to a human readable string.

The next one is a Registry lookup used to determine the currently active time bias on the system. This is a value used by the system to convert from Greenwich Mean Time (GMT) to the local time.

/// <summary>
/// Retrieve the current machine ActiveTimeBias.
/// </summary>
/// <returns>an integer representing the ActiveTimeBias in hours.</returns>

private int GetActiveBias()
    // Open the TimeZone key
    RegistryKey key = 
      Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet" + 
    if (key == null)
        return 0;

    // Pick up the time bias
    int Bias = (int)key.GetValue("ActiveTimeBias");

    // Close the parent key

    // return the result adjusted for hours (instead of minutes)
    return (Bias / 60);

This value is always subtracted from GMT to arrive at the local time. Where I live, we use daylight savings time as well as standard time, so my ActiveTimeBias value will be either 7 (Pacific Daylight Time [PDT]) or 8 (Pacific Standard Time [PST]).

The last method we will visit here is called DecodeLoginHours. Within the properties collection for a user in AD, there exists the ability to limit the hours that a user can log in to a system. This property consists of an array of 21 bytes, where each bit represents a one hour span beginning with Midnight Sunday GMT. Note that I said GMT. This is where the ActiveTimeBias comes in. By performing the subtraction, we're able to re-align the bit-array to machine time. Obviously, this bit-array is not friendly to humans, so we decode it into something that we can easily read. Within the UI, it will show up in the Properties for User list as Login Hours: > Click to view <. Naturally, the user needs to click the item in the list to get the following display:


/// <summary>
/// Translate the hours into something readable.
/// </summary>
/// <param name="HoursValue">Hours to convert.</param>
/// <returns>A string indicating the hours of availability.</returns>

private string DecodeLoginHours(byte[] HoursValue)
    // See if we have anything
    if (HoursValue.Length < 1)
        return string.Empty;

    // Pick up the time zone bias
    int Bias = GetActiveBias();

    // Convert the HoursValue array into a character array of 1's and 0's.
    // That's a really simple statement for a bit of a convoluted process:
    //  The HoursValue byte array consists of 21 elements (21 bytes) where
    //  each bit represents a specified login hour in Universal Time
    //  Coordinated (UTC). These bits must be reconstructed into an array
    //  that we can display (using 1's and 0's) and associated correctly to
    //  each of the hour increments by using the machines current timezone
    //  information.

    // Load the HoursValue byte array into a BitArray
    //   This little trick also allows us to read through the array from 
    //   left to right, rather than from right to left for each of the 21
    //   elements of the Byte array.
    BitArray ba = new BitArray(HoursValue);

    // This is the adjusted bit array (accounting for the ActiveTimeBias)
    BitArray bt = new BitArray(168);

    // Actual index in target array
    int ai = 0;

    // Copy the source bit array to the target bit array with offset
    for (int i = 0; i < ba.Length; i++)
        // Adjust for the ActiveTimeBias
        ai = i - Bias;
        if (ai < 0)
            ai += 168;

        // Place the value
        bt[ai] = ba[i];

    // Time to construct the output
    int colbump = 0;
    int rowbump = 0;
    int rowcnt = 0;
    StringBuilder resb = new StringBuilder();
    resb.Append("      ------- Hour of the Day -------");
    resb.Append("      M-3 3-6 6-9 9-N N-3 3-6 6-9 9-M");
    for (int i = 0; i < bt.Length; i++)
        // Put in a 0 or a 1
        resb.Append((bt[i]) ? "1" : "0");

        // After 24 elements are written, start the next line
        if (rowbump == 24)
            // Make sure we're not on the last element
            if (i < (bt.Length - 1))
                rowbump = 0;
                colbump = 0;
            // Insert a space after every 3 characters
            // unless we've gone to a new line
            if (colbump == 3)
                resb.Append(" ");
                colbump = 0;

    // Return the result
    return resb.ToString();


  • Version - Initial release to everyone
  • Version - Fixed decoding of the LoginHours when the TimeBias value is negative


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Software Developer (Senior) Department of Social and Health Services
United States United States
I am a Senior Technical Consultant (or any other silly title that you'd like) for a small consulting firm in Washington State. As of this writing (2007) I've been programming professionally for just shy of 26 years and programming in general for just shy of 34 years. I first started programming when I was 10 (yes, there were computers back then, just not personal computers) and I have experience in 8 dialects of BASIC, 3 generations of FORTRAN, 4 dialects of COBOL, C, C++, C#, FOCUS, SQL, a whole slew of scripting languages, Mark IV, 6502 and 80x86 assembly, BAL (both with and without ASSIST), and a few others ranging on systems from home-built on up to IBM mainframes (I've been around the block more than once).

Now .Net pays the bills and I'm having even more fun than when I got started!

You may also be interested in...

Comments and Discussions

SuggestionAbility to copy Pin
DoctorKennyG12-Jan-16 6:36
memberDoctorKennyG12-Jan-16 6:36 
GeneralMy vote of 5 Pin
Michael Haephrati10-Jan-13 10:50
mvpMichael Haephrati10-Jan-13 10:50 
GeneralRe: My vote of 5 Pin
KChandos10-Jan-13 11:31
professionalKChandos10-Jan-13 11:31 
GeneralTime Zone Issue Pin
Bernhard Hiller23-Mar-10 4:07
memberBernhard Hiller23-Mar-10 4:07 
GeneralRe: Time Zone Issue Pin
KChandos23-Mar-10 5:40
professionalKChandos23-Mar-10 5:40 
GeneralCant see login hours in GUI... Pin
DarkWhisperer8-Jan-09 5:11
memberDarkWhisperer8-Jan-09 5:11 
GeneralRe: Cant see login hours in GUI... Pin
KChandos8-Jan-09 7:40
professionalKChandos8-Jan-09 7:40 
AnswerRe: Cant see login hours in GUI... --My fixing suggestion [modified] Pin
Shlomo Ben-Ari17-Mar-10 21:20
memberShlomo Ben-Ari17-Mar-10 21:20 
GeneralRe: Cant see login hours in GUI... --My fixing suggestion Pin
KChandos22-Mar-10 7:56
professionalKChandos22-Mar-10 7:56 
GeneralIt cannot enumerate domains Pin
Win32nipuh8-Nov-08 6:56
memberWin32nipuh8-Nov-08 6:56 
GeneralRe: It cannot enumerate domains Pin
KChandos8-Nov-08 14:19
professionalKChandos8-Nov-08 14:19 
GeneralRe: It cannot enumerate domains Pin
Win32nipuh9-Nov-08 0:55
memberWin32nipuh9-Nov-08 0:55 
GeneralRe: It cannot enumerate domains Pin
KChandos9-Nov-08 12:00
professionalKChandos9-Nov-08 12:00 
GeneralDevelopment request. Pin
ADHD1-Oct-08 15:48
memberADHD1-Oct-08 15:48 
AnswerRe: Development request. Pin
KChandos1-Oct-08 17:46
professionalKChandos1-Oct-08 17:46 
NewsClient AD Schema Mapping Tool Pin
ADHD10-Sep-08 5:53
memberADHD10-Sep-08 5:53 
Generalcool! Pin
radioman.lt22-Aug-08 0:54
memberradioman.lt22-Aug-08 0:54 
GeneralSource Code is missing Pin
TBermudez21-Aug-08 12:33
memberTBermudez21-Aug-08 12:33 
GeneralRe: Source Code is missing Pin
KChandos21-Aug-08 13:33
professionalKChandos21-Aug-08 13:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190214.1 | Last Updated 26 Oct 2010
Article Copyright 2008 by KChandos
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid