Link to home
Start Free TrialLog in
Avatar of thready
thready

asked on

ports

Hi Experts,

What's the quickest Windows API call to figure out which process (and/or which logged in user) in Windows, has a given open port?

Thanks,
Mike
Avatar of thready
thready

ASKER

I think I'd likely have to start by getting the process id and then figure out which user owns that process id...
Avatar of thready

ASKER

Anyone?
Avatar of jkr
You are going to hate me for that, but it's going to be much complicated than just a single API call. Actually, it's SNMP that does the job here. Mark Russinovich (now Sysinternals, acquired by MS a couple of years ago) had written a 'netstatp' application that was published along with the full source code back then. While the code was taken down after MS had taken over, I still have it archived. Here we go:

//------------------------------------------------------------
//
// Netstatp
//
// Copyright (C) 1998 Mark Russinovich
// Systems Internals
// http://www.sysinternals.com
//
// This program implements a subset of the Netstat program's
// functionality. Specifically, it enumerates and displays
// information about all UDP and TCP endpoints.
//
//------------------------------------------------------------
#include "windows.h"
#include "stdio.h"
#include "snmp.h"
#include "winsock.h"

#define HOSTNAMELEN 256
#define PORTNAMELEN 256
#define ADDRESSLEN HOSTNAMELEN+PORTNAMELEN

typedef struct _tcpinfo {
	struct _tcpinfo		*prev;
	struct _tcpinfo		*next;
	UINT				state;
	UINT				localip;
	UINT				localport;
	UINT				remoteip;
	UINT				remoteport;
} TCPINFO, *PTCPINFO;


BOOL (__stdcall *SnmpExtensionInit)(
    IN  DWORD               dwTimeZeroReference,
    OUT HANDLE              *hPollForTrapEvent,
    OUT AsnObjectIdentifier *supportedView);

BOOL (__stdcall *SnmpExtensionQuery)(
    IN BYTE                   requestType,
    IN OUT RFC1157VarBindList *variableBindings,
    OUT AsnInteger            *errorStatus,
    OUT AsnInteger            *errorIndex);

//
// Possible TCP endpoint states
//
static char TcpState[][32] = {
	"???",
	"CLOSED",
	"LISTENING",
	"SYN_SENT",
	"SEN_RECEIVED",
	"ESTABLISHED",
	"FIN_WAIT",
	"FIN_WAIT2",
	"CLOSE_WAIT",
	"CLOSING",
	"LAST_ACK",
	"TIME_WAIT"
};
	
//
// Lists of endpoints
//
TCPINFO		TcpInfoTable;
TCPINFO		UdpInfoTable;


//------------------------------------------------------------
//
// GetPortName
//
// Translate port numbers into their text equivalent if 
// there is one
//
//------------------------------------------------------------
char *GetPortName( UINT port, char *proto, char *name, int namelen ) 
{
	struct servent *psrvent;

	if( psrvent = getservbyport( htons( (USHORT) port ), proto )) {

		strcpy( name, psrvent->s_name );

	} else {

		sprintf(name, "%d", port);

	}		
	return name;
}


//------------------------------------------------------------
//
// GetIpHostName
//
// Translate IP addresses into their name-resolved form
// if possible.
//
//------------------------------------------------------------
char *GetIpHostName( BOOL local, UINT ipaddr, char *name, int namelen ) 
{
	struct hostent			*phostent;
	UINT					nipaddr;

	nipaddr = htonl( ipaddr );
	if( !ipaddr  ) {

		if( !local ) {

			sprintf( name, "%d.%d.%d.%d", 
				(nipaddr >> 24) & 0xFF,
				(nipaddr >> 16) & 0xFF,
				(nipaddr >> 8) & 0xFF,
				(nipaddr) & 0xFF);

		} else {

			gethostname(name, namelen);
		}

	} else if( ipaddr == 0x0100007f ) {

		if( local ) {

			gethostname(name, namelen);
		} else {

			strcpy( name, "localhost" );
		}

	} else if( phostent = gethostbyaddr( (char *) &ipaddr,
		sizeof( nipaddr ), PF_INET )) {

		strcpy( name, phostent->h_name );

	} else {

		sprintf( name, "%d.%d.%d.%d", 
			(nipaddr >> 24) & 0xFF,
			(nipaddr >> 16) & 0xFF,
			(nipaddr >> 8) & 0xFF,
			(nipaddr) & 0xFF);
	}
	return name;
}


//------------------------------------------------------------
//
// LoadInetMibEntryPoints
//
// Load the TCP/IP SNMP extension DLL and locate the entry
// points we will use.
//
//------------------------------------------------------------
BOOLEAN LoadInetMibEntryPoints()
{
	HINSTANCE	hInetLib;

	if( !(hInetLib = LoadLibrary( "inetmib1.dll" ))) {

		return FALSE;
	}

	if( !(SnmpExtensionInit = (void *) GetProcAddress( hInetLib,
			"SnmpExtensionInit" )) ) {

		return FALSE;
	}

	if( !(SnmpExtensionQuery = (void *) GetProcAddress( hInetLib,
			"SnmpExtensionQuery" )) ) {

		return FALSE;
	}
	
	return TRUE;
}


//------------------------------------------------------------
//
// Main
//
// Do it all. Load and initialize the SNMP extension DLL and
// then build a table of TCP endpoints and UDP endpoints. After
// each table is built resolve addresses to names and print
// out the information
//
//------------------------------------------------------------
int main( int argc, char *argv[] )
{
	HANDLE					hTrapEvent;
	AsnObjectIdentifier		hIdentifier;
	RFC1157VarBindList		bindList;
	RFC1157VarBind			bindEntry;
	UINT					tcpidentifiers[] = { 1,3,6,1,2,1,6,13,1,1};
	UINT					udpidentifiers[] = { 1,3,6,1,2,1,7,5,1,1};
	AsnInteger				errorStatus, errorIndex;
	TCPINFO					*currentEntry, *newEntry;
	UINT					currentIndex;
	WORD					wVersionRequested;
	WSADATA					wsaData;
	char					localname[HOSTNAMELEN], remotename[HOSTNAMELEN];
	char					remoteport[PORTNAMELEN], localport[PORTNAMELEN];
	char					localaddr[ADDRESSLEN], remoteaddr[ADDRESSLEN];

	//
	// Initialize winsock
	//
	wVersionRequested = MAKEWORD( 1, 1 );
	if( WSAStartup(  wVersionRequested, &wsaData ) ) {

		printf("Could not initialize Winsock.\n");
		return 1;
	}

	//
	// Locate and initialize INETMIB1
	//
	if( !LoadInetMibEntryPoints()) {

		printf("Could not load extension DLL.\n");
		return 1;
	}

	if( !SnmpExtensionInit( GetCurrentTime(), &hTrapEvent, &hIdentifier )) {

		printf("Could not initialize extension DLL.\n");
		return 1;
	}

	//
	// Initialize the query structure once
	//
	bindEntry.name.idLength = 0xA;
	bindEntry.name.ids = tcpidentifiers;
	bindList.list = &bindEntry;
	bindList.len  = 1;

	TcpInfoTable.prev = &TcpInfoTable;
	TcpInfoTable.next = &TcpInfoTable;

	//
	// Roll through TCP connections
	//
	currentIndex = 1;
	currentEntry = &TcpInfoTable;
	while(1) {

		if( !SnmpExtensionQuery( ASN_RFC1157_GETNEXTREQUEST,
			&bindList, &errorStatus, &errorIndex )) {

			return 1;
		}

		//
		// Terminate when we're no longer seeing TCP information
		//
		if( bindEntry.name.idLength < 0xA ) break;

		//
		// Go back to start of table if we're reading info
		// about the next byte
		//
		if( currentIndex != bindEntry.name.ids[9] ) {

			currentEntry = TcpInfoTable.next;
			currentIndex = bindEntry.name.ids[9];
		}

		//
		// Build our TCP information table 
		//
		switch( bindEntry.name.ids[9] ) {

		case 1:
			
			//
			// Always allocate a new structure
			//
			newEntry = (TCPINFO *) malloc( sizeof(TCPINFO ));
			newEntry->prev = currentEntry;
			newEntry->next = &TcpInfoTable;
			currentEntry->next = newEntry;
			currentEntry = newEntry;

			currentEntry->state = bindEntry.value.asnValue.number;
			break;

		case 2:

			currentEntry->localip = 
				*(UINT *) bindEntry.value.asnValue.address.stream;
			currentEntry = currentEntry->next;
			break;

		case 3:
			
			currentEntry->localport = 
				bindEntry.value.asnValue.number;
			currentEntry = currentEntry->next;
			break;

		case 4:

			currentEntry->remoteip = 
				*(UINT *) bindEntry.value.asnValue.address.stream;
			currentEntry = currentEntry->next;
			break;

		case 5:

			currentEntry->remoteport = 
				bindEntry.value.asnValue.number;
			currentEntry = currentEntry->next;
			break;
		}

	}
	
	//
	// Now print the connection information
	//
	printf("%7s %-30s %-30s %s\n", "Proto", "Local", "Remote", "State" );
	currentEntry = TcpInfoTable.next;
	while( currentEntry != &TcpInfoTable ) {

		sprintf( localaddr, "%s:%s", 
			GetIpHostName( TRUE, currentEntry->localip, localname, HOSTNAMELEN), 
			GetPortName( currentEntry->localport, "tcp", localport, PORTNAMELEN ));

		sprintf( remoteaddr, "%s:%s",
			GetIpHostName( FALSE, currentEntry->remoteip, remotename, HOSTNAMELEN), 
			currentEntry->remoteip ? 
				GetPortName( currentEntry->remoteport, "tcp", remoteport, PORTNAMELEN ):
				"0" );

		printf("%7s %-30s %-30s %s\n", "TCP", 
			localaddr, remoteaddr,
			TcpState[currentEntry->state]);
		
		currentEntry = currentEntry->next;
	}
	printf("\n");

	//
	// Initialize the query structure once
	//
	bindEntry.name.idLength = 0xA;
	bindEntry.name.ids = udpidentifiers;
	bindList.list = &bindEntry;
	bindList.len  = 1;

	UdpInfoTable.prev = &UdpInfoTable;
	UdpInfoTable.next = &UdpInfoTable;

	//
	// Roll through UDP endpoints
	//
	currentIndex = 1;
	currentEntry = &UdpInfoTable;
	while(1) {

		if( !SnmpExtensionQuery( ASN_RFC1157_GETNEXTREQUEST,
			&bindList, &errorStatus, &errorIndex )) {

			return 1;
		}

		//
		// Terminate when we're no longer seeing TCP information
		//
		if( bindEntry.name.idLength < 0xA ) break;

		//
		// Go back to start of table if we're reading info
		// about the next byte
		//
		if( currentIndex != bindEntry.name.ids[9] ) {

			currentEntry = UdpInfoTable.next;
			currentIndex = bindEntry.name.ids[9];
		}

		//
		// Build our TCP information table 
		//
		switch( bindEntry.name.ids[9] ) {

		case 1:
			
			//
			// Always allocate a new structure
			//
			newEntry = (TCPINFO *) malloc( sizeof(TCPINFO ));
			newEntry->prev = currentEntry;
			newEntry->next = &UdpInfoTable;
			currentEntry->next = newEntry;
			currentEntry = newEntry;

			currentEntry->localip = 
				*(UINT *) bindEntry.value.asnValue.address.stream;
			break;

		case 2:
			
			currentEntry->localport = 
				bindEntry.value.asnValue.number;
			currentEntry = currentEntry->next;
			break;
		}
	}
	
	//
	// Now print the connection information
	//
	currentEntry = UdpInfoTable.next;
	while( currentEntry != &UdpInfoTable ) {

		printf("%7s %s:%s\n", "UDP",
				GetIpHostName( TRUE, currentEntry->localip, localname, HOSTNAMELEN), 
				GetPortName( currentEntry->localport, "udp", localport, PORTNAMELEN ) );
		
		currentEntry = currentEntry->next;
	}
	printf("\n");
}

Open in new window


That way, you have a list of the open ports. Now for the hard part, that is getting the associated PID, for which you will need to use Windows' Native API (http://netcode.cz/img/83/nativeapi.html) , in particular get a copy of the global handle table by calling 'ZwQuerySystemInformation(SystemHandleInformation,...);' and checking the referenced handles /PIDs. The following code does this, the the structure definitions were taken from Gary Nebbett' book "Native API Reference" (which also contains samples from which this snippet is derived). All you need to do is compiling this and link with ntdll.lib, which comes with the DDK. Alternatively, you could load the Zw* functions dynamically.

#define UNICODE
#define _UNICODE
#include <tchar.h>
#include <stdio.h>

#pragma warning ( disable: 4768)
#include <map>

typedef enum _SYSTEM_INFORMATION_CLASS
{
    SystemHandleInformation = 16,

} SYSTEM_INFORMATION_CLASS;

typedef	struct	_SYSTEM_HANDLE_INFORMATION
{
    ULONG		ProcessId;
    UCHAR		ObjectTypeNumber;
    UCHAR		Flags;
    USHORT		Handle;
    PVOID		Object;
    ACCESS_MASK	GrantedAccess;

}	SYSTEM_HANDLE_INFORMATION,	*PSYSTEM_HANDLE_INFORMATION;

typedef enum _OBJECT_INFORMATION_CLASS
{
    ObjectBasicInformation,
    ObjectNameInformation,
    ObjectTypeInformation,
    ObjectAllTypesInformation,
    ObjectHandleInformation
}	OBJECT_INFORMATION_CLASS;

typedef struct	_OBJECT_BASIC_INFORMATION
{
    ULONG			Attributes;
    ACCESS_MASK		GrantedAccess;
    ULONG			HandleCount;
    ULONG			PointerCount;
    ULONG			PagedPoolUsage;
    ULONG			NonPagedPoolUsage;
    ULONG			Reserved	[	3];
    ULONG			NameInformationLength;
    ULONG			TypeInformationLength;
    ULONG			SecurityDescriptorLength;
    LARGE_INTEGER	CreateTime;

}	OBJECT_BASIC_INFORMATION,	*POBJECT_BASIC_INFORMATION;	

typedef	struct	_OBJECT_TYPE_INFORMATION
{
    UNICODE_STRING	Name;
    ULONG			ObjectCount;
    ULONG			HandleCount;
    ULONG			Reserved1	[	4];
    ULONG			PeakObjectCount;
    ULONG			PeakHandleCount;
    ULONG			Reserved2	[	4];
    ULONG			InvalidAttributes;
    GENERIC_MAPPING	GenericMapping;
    ULONG			ValidAccess;
    UCHAR			Unknown;
    BOOLEAN			MaintainHandleDatabase;
    NT::POOL_TYPE	PoolType;
    ULONG			PagedPoolUsage;
    ULONG			NonPagedPoolUsage;

}	OBJECT_TYPE_INFORMATION,	*POBJECT_TYPE_INFORMATION;

typedef	struct	_OBJECT_NAME_INFORMATION
{
    UNICODE_STRING	Name;
}	OBJECT_NAME_INFORMATION,	*POBJECT_NAME_INFORMATION;


typedef std::map<ULONG, ULONG> pid_map;


extern "C"
NTSTATUS
__stdcall
ZwQuerySystemInformation	(	SYSTEM_INFORMATION_CLASS,
                                PVOID,
                                ULONG,
                                PULONG
                            );

extern "C"
NTSTATUS
__stdcall
ZwQueryObject				(	HANDLE,
                                OBJECT_INFORMATION_CLASS,
                                PVOID,
                                ULONG,
                                PULONG
                            );

extern "C"
NTSTATUS
__stdcall
ZwDuplicateObject			(	HANDLE,
                                HANDLE,
                                HANDLE,
                                PHANDLE,
                                ACCESS_MASK,
                                ULONG,
                                ULONG
                            );

POBJECT_NAME_INFORMATION	
GetObjectNameInformation	(	HANDLE	__hObject, wchar_t* __pwszTypeFilter)
{
    NTSTATUS					_ntStatus;
    OBJECT_BASIC_INFORMATION	_obi;
    POBJECT_TYPE_INFORMATION	_poti;
    POBJECT_NAME_INFORMATION	_poni;
    ULONG						_ul;
    
    ZwQueryObject	(	__hObject,
                        ObjectBasicInformation,
                        &_obi,
                        sizeof	(	_obi),
                        &_ul
                    );

    _ul	=		_obi.TypeInformationLength	+	2;

    _poti	=	( POBJECT_TYPE_INFORMATION)	new	char	[	_ul];

    _ntStatus	=	ZwQueryObject	(	__hObject,
                                        ObjectTypeInformation,
                                        _poti,
                                        _ul,
                                        &_ul
                                    );

    if	(	__pwszTypeFilter	&&	!wcscmp	(	_poti->Name.Buffer, __pwszTypeFilter))
        {
            delete	[]	_poti;

            return	NULL;
        }

    _ul	=		!_obi.NameInformationLength	
            ?	MAX_PATH * sizeof ( WCHAR)
            :	_obi.NameInformationLength;

    _poni	=	( POBJECT_NAME_INFORMATION)	new	char	[	_ul];

    _ntStatus	=	ZwQueryObject	(	__hObject,
                                        ObjectNameInformation,
                                        _poni,
                                        _ul,
                                        &_ul
                                    );

/*
    wprintf	(	L"Obj 0x%8.8x %.*s %.*s\n",	
                __hObject,
                _poni->Name.Length / 2,
                _poni->Name.Buffer,
                _poti->Name.Length / 2,
                _poti->Name.Buffer
            );
*/

    delete	[]	_poti;

    return	(	_poni);
}

void	PrintObjectUsers	(	wchar_t*	__pwszObj,	wchar_t*	__pwszTypeFilter)
{

    pid_map						_pid_map;
    ULONG						_ulPID;
    NTSTATUS					_ntStatus;

    HANDLE						_hObject;
    HANDLE						_hProcess;

    PSYSTEM_HANDLE_INFORMATION	_pshi;
    POBJECT_NAME_INFORMATION	_poni;

    ULONG						_ul		=	0x1000;
    PULONG						_pul	=	new	ULONG	[	_ul];

//	hProcess	=	OpenProcess	(	PROCESS_DUP_HANDLE,	FALSE,	dwPID);

    while	(	STATUS_INFO_LENGTH_MISMATCH	==	ZwQuerySystemInformation	(	SystemHandleInformation,
                                                                                _pul,
                                                                                _ul	*	sizeof	(	ULONG),
                                                                                0
                                                                            )
            )	delete	[]	_pul,	_pul	=	new	ULONG	[	_ul	*=	2];

    _pshi	=	( PSYSTEM_HANDLE_INFORMATION)	( _pul	+	1);

    for	(	ULONG	_i	=	0;	_i	<	*_pul;	_i++)
        {
            _ulPID		=	_pshi	[	_i].ProcessId;

            _hObject	=	NULL;

            _hProcess	=	OpenProcess	(	PROCESS_DUP_HANDLE,	
                                            FALSE,	
                                            _ulPID
                                        );

            if	(	!_hProcess)
                {
                    //wprintf	(	L"FAILED to 'OpenProcess()' for PID %d\n",	_ulPID);
                    continue;
                }

            ZwDuplicateObject	(	_hProcess,
                                    ( HANDLE)	_pshi	[	_i].Handle,
                                    NtCurrentProcess	(),
                                    &_hObject,
                                    0,
                                    0,
                                    DUPLICATE_SAME_ATTRIBUTES
                                );

            if	(	!_hObject)
                {
                    wprintf	(	L"FAILED to 'ZwDuplicateObject()' for PID %d\n",	_ulPID);
                    continue;
                }

            if	(	_hObject)
                {
                    _poni	=	GetObjectNameInformation	(	_hObject, __pwszTypeFilter);

                    if	(	!_poni)	continue;

                    if	(	!_poni->Name.Length)	continue;

                    if	(	wcsstr	(	_poni->Name.Buffer,	__pwszObj))
                        {
                            pid_map::iterator	_i_map	=	_pid_map.find	(	_ulPID);

//							if	(	_i_map	==	_pid_map.end	())
                                {
                                    wprintf	(	L"PID: %d\tNAME: %s\n",
                                                _ulPID,
                                                _poni->Name.Buffer
                                            );

//									_pid_map.insert	(	pid_map::value_type	(	_ulPID,	_ulPID));
                                }
                        }

                    delete	[]	_poni;

                }

            CloseHandle	(	_hObject);
            CloseHandle	(	_hProcess);

        }

    delete	[]	_pul;
}


int	wmain	(	int			__wargc,
                wchar_t**	__wargv
            )
{
    wchar_t*	__pwc;


    if	(	2	!=	__wargc)
            return	(	-1);

    PrintObjectUsers	(	*( __wargv	+	1),	NULL);

    return	(	0);

}
                                          

Open in new window


(see also https://www.experts-exchange.com/questions/28326769/How-do-I-know-who-is-locking-the-file-in-my-C-codes.html et. al.)

Once you have associated the ports and PIDs, getting the user name is a piece of cake ;o)

Or, in code:

#include <windows.h>
#include <lmcons.h>

#include <stdio.h>

#pragma comment (   lib,    "advapi32.lib")
#pragma comment (   lib,    "user32.lib")

int GetProcessOwner    (   HANDLE   hProcess,    char*   acBuffer,   ULONG   ulSize)
{
    PSECURITY_DESCRIPTOR    psd;
    PSID                    psid;
    DWORD                   dwSDLength;
    DWORD                   dwNeeded;

    DWORD                   dwErr;

    DWORD                   dwNameSize;

    char                    acReferencedDomain  [   LM20_DNLEN  +   1];
    DWORD                   dwDomainBufSize     =   sizeof  (   acReferencedDomain);

    SID_NAME_USE            eUse;
    BOOL                    bDefaulted;
    SECURITY_INFORMATION    si = OWNER_SECURITY_INFORMATION;
    dwNameSize  =   ulSize;

    if  (   !GetKernelObjectSecurity   (   hProcess,
                                           si,
                                           NULL,
                                           0,
                                           &dwNeeded
                                       )
        )
        {
            dwErr   =   GetLastError    ();

            if  (   ERROR_INSUFFICIENT_BUFFER   !=  dwErr)
                {
                    return  (   dwErr);
                }

            dwSDLength  =   dwNeeded;

            psd =   ( PSECURITY_DESCRIPTOR) LocalAlloc  (   LPTR,
                                                            dwSDLength
                                                        );

            __try
            {
                if  (   !GetKernelObjectSecurity     (   hProcess,
                                                       si,
                                                       psd,
                                                       dwSDLength,
                                                       &dwNeeded
                                                    )
                    )
                    {
                        dwErr   =   GetLastError    ();

                        __leave;
                    }

                if  (   !GetSecurityDescriptorOwner (   psd,
                                                        &psid,
                                                        &bDefaulted
                                                    )
                    )
                    {
                        dwErr   =   GetLastError    ();

                        __leave;
                    }
                                                    
                //  lookup clear text name of the owner
                if  (   !LookupAccountSid   (   NULL,
                                                psid,
                                                acBuffer,
                                                &dwNameSize,
                                                acReferencedDomain,
                                                &dwDomainBufSize,
                                                &eUse
                                            )
                    )
                    {
                        dwErr   =   GetLastError    ();

                        __leave;
                    }

                dwErr   =   0;

            }
            __finally
            {
                LocalFree   (   psd);
            }
        }

    return  (   dwErr);
}

BOOL    EnableDebugPriv  (   BOOL    bEnable) 
{
   HANDLE           hToken;
   TOKEN_PRIVILEGES tp;

   if   (   !OpenProcessToken   (   GetCurrentProcess   (), 
                                    TOKEN_ADJUST_PRIVILEGES, 
                                    &hToken
                                )
        )   return  (   FALSE);


   tp.PrivilegeCount    =   1;
   
   LookupPrivilegeValue (   NULL, 
                            SE_DEBUG_NAME, 
                            &tp.Privileges  [   0].Luid
                        );

   tp.Privileges    [   0].Attributes   =       bEnable 
                                            ?   SE_PRIVILEGE_ENABLED
                                            :   0;

   AdjustTokenPrivileges    (   hToken,
                                FALSE, 
                                &tp, 
                                sizeof  (   tp), 
                                NULL, 
                                NULL
                            );

   return   (   GetLastError()  ==   ERROR_SUCCESS);
}

void    main    (   int argc,   char**  argv)
{
    HANDLE  hProcess;
    char    acBuf   [   256];
    DWORD   dwErr;

    if  (   2   !=  argc)
            return;

    DWORD dwPID = atoi(*(  argv    +   1));

    printf  (   "\nPID:\t%d\n",    dwPID);

    EnableDebugPriv  ( TRUE);

    hProcess = OpenProcess ( PROCESS_ALL_ACCESS, FALSE, dwPID);

    dwErr   =   GetProcessOwner    (   hProcess,    acBuf,  256);

    if  (   !dwErr)
            printf  (   "\nowner:\t%s\n",   acBuf);
     else
            printf  (   "\nerror:\t%d\n",   dwErr);

    CloseHandle ( hProcess);

}

Open in new window

Avatar of thready

ASKER

Holy crap!  That's a lot of code!  Thanks jkr!

Although, I just found something that looks smaller - a call to GetExtendedTcpTable, followed by a call to GetProcessOwner (int processId), found here:
http://stackoverflow.com/questions/777548/how-do-i-determine-the-owner-of-a-process-in-c
what do you think?
ASKER CERTIFIED SOLUTION
Avatar of jkr
jkr
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of thready

ASKER

GetExtendedTcpTable is awesome!