65.9K
CodeProject is changing. Read more.
Home

Retrieve a List of Network Computers and the Full Names of Connected Users

Jul 23, 2012

CPOL

3 min read

viewsIcon

29475

downloadIcon

1833

When you're in an environment that has multiple virtual machines and you would like to quickly know which machines have extra connection available or if the machine is free.

Introduction

When you're in an environment that has multiple virtual machines with each machine having RDP account connection limitation (e.g., only x maximum concurrent terminal service users) or will be too slow for more than one user, and you would like to quickly know which machines have extra connection available or if a machine is free, it is a waste of time to connect to each one.

On average, an RDP connection attempt takes 30 seconds and, on top of that, checking if another user(s) is connected will take around 3 minutes. Using Terminal Services Manager can cut this time down but still you'll have to spend 10 to 15 seconds checking each known machine...but what if you want to scan including the newly added machines that you're not aware of?

What if you want to have a bird's eye-view of the network domain and its current users and also be able to print (print screen for now) this view? What if you need the Full Name of each connected user (disconnected/timed-out users are not displayed) instead of their usually unintelligible login names?

What if you want to know if a user is unreasonably using a lot of machines?

Background

Note that this solution is not limitless as it can only scan virtual machines that are exposed to the current machine's network domain where you will run this tool. I know some guys especially network administrators can have a better explanation about this so let's leave it to them.

Also note that this project took 1.5 hours to create on a staggered basis covering three days (I have a day job and a personal life like everyone else) so for a moment please brush aside the "this project needs refactoring!" thought running in your mind. Consider this as a very quick 1-hour project if done continuously Big Grin

Using the Code

My approach is to briefly give you an explanation of the source code and how it is mainly structured. Feel free to look at the project source code for the rest of the functionalities not mentioned here.

  1. Note the following references:
    using System.Windows.Forms;
    using ListNetworkComputers;
    using System.Security.Principal;
    using Cassia;
    using System.DirectoryServices;

    We reference the ListNetworkComputers project only (quickest approach) just to get access to the NetworkBrowser class. The ListNetworkComputers project was created by Sacha Barber with the link at the end of this post.

    The Cassia .Net library is "for accessing the native Windows Terminal Services API (now the Remote Desktop Services API)" and is from Google Code with link at the end of this post.

  2. The main functionality is presented below, which is where I integrated all the pre-existing functionalities for my purpose:
    //create a new NetworkBrowser object, and get the
    //list of network computers it found, and add each
    //entry to the combo box on this form
    try
    {
       NetworkBrowser nb = new NetworkBrowser(); //We reuse Sacha Barber's project
       ArrayList al = nb.getNetworkComputers(); //Will contain all the network computers/VMs
       ITerminalServicesManager manager = new TerminalServicesManager();
       for (int x = 0; x < al.Count; x++)
       {
           ...
           panel1.Controls.Add(lblComputer); //Add the computer on the form
           try
           {
               using (ITerminalServer server = manager.GetRemoteServer(al[x] as string))
               {
                   server.Open();
                   int locationCounter = 0;
                   //Get the connected user(s)
                   foreach (ITerminalServicesSession session in server.GetSessions())
                   {
                       NTAccount account = session.UserAccount;
                       if (account != null)
                       {
                           Label lblUser = new Label();
                           string filter = string.Format("(&(ObjectClass={0})(sAMAccountName={1}))",
                                                    "person", session.UserName);
                                                    //Format string for Active Directory purposes
                           DirectoryEntry adRoot = new DirectoryEntry("LDAP://" + session.DomainName,
                                                    null, null, AuthenticationTypes.Secure);
                           DirectorySearcher searcher = new DirectorySearcher(adRoot);
                           searcher.SearchScope = SearchScope.Subtree;
                           searcher.ReferralChasing = ReferralChasingOption.All;
                           searcher.PropertiesToLoad.AddRange(new string[] { "fullname" });
                                                  //Give me the Full Name of the user
                           searcher.Filter = filter;
    
    
                           SearchResult result = searcher.FindOne();
                           DirectoryEntry directoryEntry = result.GetDirectoryEntry();
    
    
                           //not sure if this check is necessary but let it pass
                           if (directoryEntry != null)
                           {
                               lblUser.Text = directoryEntry.Properties["displayName"][0].ToString(); //Got the Full Name
                           }
                           else
                           {
                               lblUser.Text = account.Value; //Just give me the Account Name
                           }
                           ...
                       }
                   } 
               } 
           }
           ...
       }
    }
    ...

Points of Interest

Please do note that I am in no way attempting plagiarism or claiming something that is not mine. The list below are the exact sources/references of the source codes as applicable...what I only did is to create a (very quick) structure of functionality that will merge all of the useful parts of these codes for my intended purpose. There are tons of improvements (modularity, interactivity, efficiency, late-binding, etc. etc.) to be done here so feel free to improvise. Please refer to the following links to get details on the specific libraries and classes that I used.

Licenses: The third-party components/libraries/codes used in this project may have respective copyrights/licenses of their own. Refer to the third-party sites and observe their copyrights/licenses. Codes that I have created in this project are under CPOL License.