Identifying process using TCP port in .NET

Currently I am working on a application  which uses third-party component for remote diagnostics.  This third party component internally uses 8081 tcp port for communication between the process and port was not configurable , which forced our application installer to make sure that no other process in the system is using port 8081 during install. We had to warn user during application install if any process is using required tcp port.

In .Net you can use System.Net.NetworkInformation.IPGlobalProperties to find out whether tcp port in use. But this API does not tell you which process using the port. We wanted to show user not only tcp port is in use , also provide information  about process , such as name , id and path .  We also had to make sure that it works from Windows XP to Windows 7 .

From MSDN documentation, I found that only to get the process id of the process using the tcp port is to use Win32 API . This means I had to use platform invoke(pinvoke) and carefully declare lot of Win32 structure in C#.

Other alternative is to use the netstat command , that provides the same details with appropriate command line switches. I used this technique various times ,when I was working on Linux systems. I decided to do the same here.

I wrote simple helper class which uses netstat and WMI to get the details about the process using the tcp port.
Here is how you use the sample

int port = 8081;
TcpHelperUtil tcpHelper = new TcpHelperUtil();
var details = tcpHelper.GetPortDetails(port);
if (details.Item1)
{
    Console.WriteLine("Port {0} in Use",port);
    Console.WriteLine(details.Item2.ToString());
}else
{
    Console.WriteLine("Port {0} is free ",port);
}

Here is the source code.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;

namespace ConsoleApp2
{

    internal class PortDetail
    {
        public int Port { get; set; }
        public int ProcessID { get; set; }
        public string ProcessName { get; set; }
        public string Path { get; set; }
        public override string ToString()
        {
            return string.Format(@" Process Name: {0} ,Process ID: {1} ,
                                    Port: {2} ,\nPath : {3}", ProcessName,
                                    ProcessID, Port, Path);
        }

    }
    /// <summary>
    /// Usage:
    /// int port = 8081
    /// TcpHelperUtil tcpHelper = new TcpHelperUtil();
    /// var details = tcpHelper.GetPortDetails(port);
    /// if (details.Item1)
    /// {
    ///     Console.WriteLine("Port {0} in Use",port);
    ///     Console.WriteLine(details.Item2.ToString());
    /// }else
    /// {
    ///     Console.WriteLine("Port {0} is free ",port);
    /// }
    ///
    /// </summary>
    class TcpHelperUtil
    {
        private const short MINIMUM_TOKEN_IN_A_LINE = 5;
        private const string COMMAND_EXE = "cmd";

        public TcpHelperUtil()
        {

        }

        public Tuple<bool, PortDetail> GetPortDetails(int port)
        {
            PortDetail PortDetail = new PortDetail();
            Tuple<bool, PortDetail> result = Tuple.Create(false, PortDetail);

            // execute netstat command for the given port
            string commandArgument = string.Format("/c netstat -an -o -p tcp|findstr \":{0}.*LISTENING\"", port);

            string commandOut = ExecuteCommandAndCaptureOutput(COMMAND_EXE, commandArgument);
            if (string.IsNullOrEmpty(commandOut))
            {
                // port is not in use
                return result;
            }

            var stringTokens = commandOut.Split(default(Char[]), StringSplitOptions.RemoveEmptyEntries);
            if (stringTokens.Length < MINIMUM_TOKEN_IN_A_LINE)
            {
                return result;
            }

            // split host:port
            var hostPortTokens = stringTokens[1].Split(new char[] { ':' });
            if (hostPortTokens.Length < 2)
            {
                return result;
            }

            int portFromHostPortToken = 0;
            if (!int.TryParse(hostPortTokens[1], out portFromHostPortToken))
            {
                return result;
            }
            if (portFromHostPortToken != port)
            {
                return result;
            }

            PortDetail.Port = port;
            PortDetail.ProcessID = int.Parse(stringTokens[4].Trim());
            Tuple<string, string> processNameAndPath = null;
            try
            {
                processNameAndPath = GetProcessNameAndCommandLineArgs(PortDetail.ProcessID);
                PortDetail.ProcessName = processNameAndPath.Item1;
                PortDetail.Path = processNameAndPath.Item2;
                result = Tuple.Create(true, PortDetail);
            }
            catch (Exception exp)
            {
                Console.WriteLine(exp.ToString());

            }

            return result;

        }
        /// <summary>
        /// Using WMI API to get process name and path instead of
        /// Process.GetProcessById, because if calling process ins
        /// 32 bit and given process id is 64 bit, caller will not be able to
        /// get the process name
        /// </summary>
        /// <param name="processID"></param>
        /// <returns></returns>
        private Tuple<string, string> GetProcessNameAndCommandLineArgs(int processID)
        {
            Tuple<string, string> result = Tuple.Create(string.Empty, string.Empty);
            string query = string.Format("Select Name,ExecutablePath from Win32_Process WHERE ProcessId='{0}'", processID);
            try
            {
                ObjectQuery wql = new ObjectQuery(query);
                ManagementObjectSearcher searcher = new ManagementObjectSearcher(wql);
                ManagementObjectCollection results = searcher.Get();

                // interested in first result.
                foreach (ManagementObject item in results)
                {
                    result = Tuple.Create<string, string>(Convert.ToString(item["Name"]),
                    Convert.ToString(item["ExecutablePath"]));
                    break;

                }
            }
            catch (Exception)
            {

                throw;
            }

            return result;

        }

        /// <summary>
        /// Execute the given command and captures the output
        /// </summary>
        /// <param name="commandName"></param>
        /// <param name="arguments"></param>
        /// <returns></returns>
        private string ExecuteCommandAndCaptureOutput(string commandName, string arguments)
        {
            string commandOut = string.Empty;
            Process process = new Process();
            process.StartInfo.FileName = commandName;
            process.StartInfo.Arguments = arguments;
            process.StartInfo.UseShellExecute = false;
            process.StartInfo.CreateNoWindow = true;
            process.StartInfo.RedirectStandardOutput = true;
            process.StartInfo.RedirectStandardError = true;
            process.Start();

            commandOut = process.StandardOutput.ReadToEnd();
            string errors = process.StandardError.ReadToEnd();
            try
            {
                process.WaitForExit(TimeSpan.FromSeconds(2).Milliseconds);
            }
            catch (Exception exp)
            {

                Console.WriteLine(exp.ToString());
            }
            return commandOut;
        }
    }
}

Monitor process startup/shutdown using WMI & PowerShell

Recently I was working on a project, where I needed to monitor process for startup/shutdown events,  after searching internet  found that it can be easily done using WMI and PowerShell .

WMI (Windows Management Instrumentation) is a technology built in to Windows since Windows 2000, that provides standard interface to manage windows systems. For example it can used to find software products installed in single or multiple machines in network or to verify necessary OS service pack is installed on all machines in the network.

PowerShell provides easy way of accessing WMI functionality using simple scripting language.

Here is how you can monitor for process startup using PowerShell & WMI events

1. Launch PowerShell and enter following command. This command will register for process started event and prints statement in console whenever notepad.exe is executed.

Register-WMIEvent -query “SELECT * FROM Win32_ProcessStartTrace WHERE ProcessName=’notepad.exe'” -SourceIdentifier “testevent” -action { $e = $Event.SourceEventArgs.NewEvent

Write-Host $e.ProcessName,” started ” }

2. Now launch notepad and observer that console has message confirming the same.

3. Once you are done, unregister the event by entering following command

Unregister-Event testevent

4. You can also see list of event subscribers with “Get-EventSubscriber” command.

Some more examples of using WMI & PowerShell

1.Monitor process stop events

Register-WMIEvent -query “SELECT * FROM Win32_ProcessStopTrace WHERE ProcessName=’notepad.exe'” -SourceIdentifier “testevent” -action { $e = $Event.SourceEventArgs.NewEvent

Write-Host $e.ProcessName,” stopped ” }

2. Monitor Windows Service stop/start status. Replace “TargetInstance.Name” value with your service name

Register-WMIEvent -query “Select * From __InstanceOperationEvent within 1 Where TargetInstance ISA ‘Win32_Service’ and TargetInstance.Name=’Fax'” -sourceIdentifier “MyServMonitor” -action { Write-host “Service Name :”,$EventArgs.NewEvent.TargetInstance.Name ,” Service State :”, $EventArgs.NewEvent.TargetInstance.State }

image

You can also do the same event monitoring using WMI & C#


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Management;

namespace WmiEventTest
{
    class ServiceStatusMonitor
    {
        static void Main(string[] args)
        {

            if (args.Length < 1)
            {
                Console.WriteLine("Usage: ServiceStatusMonitor  ");
                Environment.Exit(0);
            }
            string WMI_EVENT_QUERY = @"SELECT * FROM __InstanceModificationEvent
                WITHIN 1 WHERE TargetInstance ISA 'Win32_Service'";

            string WMI_EVENT_QUERY_WITH_SERVICE_NAME = WMI_EVENT_QUERY
                    + " and TargetInstance.Name = '{0}'";
            WqlEventQuery serviceModificationEvent =
                    new WqlEventQuery(string.Format(WMI_EVENT_QUERY_WITH_SERVICE_NAME, args[0]));
            ManagementEventWatcher eventWatcher =
                    new ManagementEventWatcher(serviceModificationEvent);
            eventWatcher.EventArrived +=
                    new EventArrivedEventHandler(Watcher_EventArrived);
            Console.WriteLine("Waiting for service status change events ...");
            eventWatcher.Start();
            Console.ReadLine();
        }

        static void Watcher_EventArrived(object sender, EventArrivedEventArgs e)
        {
            string eventType = e.NewEvent.ClassPath.ClassName;

            switch (eventType)
            {
                case "__InstanceCreationEvent":

                    Console.BackgroundColor = ConsoleColor.Blue;
                    Console.WriteLine("'{0}' Service created ....",
                            Environment.GetCommandLineArgs()[1]);
                    Console.ResetColor();
                    break;
                case "__InstanceDeletionEvent":

                    Console.BackgroundColor = ConsoleColor.Green;
                    Console.WriteLine("'{0}' Service deleted ....",
                        Environment.GetCommandLineArgs()[1]);
                    Console.ResetColor();
                    break;

                case "__InstanceModificationEvent":

                    Console.BackgroundColor = ConsoleColor.Blue;
                    ManagementBaseObject obj = (ManagementBaseObject)e.NewEvent["TargetInstance"];
                    Console.WriteLine("'{0}' Service Modified ( {1} )",
                        Environment.GetCommandLineArgs()[1], obj["State"]);
                    Console.ResetColor();
                    break;
            }

        }
    }
}

Resources

1. Introduction to WMI

2. Receiving WMI Events

3. PowerShell for event monitoring.