Click here to Skip to main content
Click here to Skip to main content
Go to top

Running multiple instances of Task Manager

, 28 Feb 2010
Rate this:
Please Sign up or sign in to vote.
A simple application that allows the user to run more then one instance of the Task Manager.

Introduction

As we all know, Task Manager, whatever the reasons are, does not allow the user to run multiple instances. This article explains a simple application that allows the user to start as many instances of the Task Manager as he/she wishes to. The technique described here is only valid for Windows 7 (and maybe Vista); XP's Task Manager uses a different technique.

Background

To do so, it employs simple tactics. When it begins execution, it tries to create a named kernel object (in this case, a mutex), and if there is already an object with the name Task Manager, it assumes that another instance of it is already active and it closes the current process.

The nme of the offending mutex is TASKMGR.879e4d63-6c0e-4544-97f2-1244bd3f6de0.

Here's an example of how it can be done:

if( ::CreateMutex( NULL, FALSE, 
  TEXT( "TASKMGR.879e4d63-6c0e-4544-97f2-1244bd3f6de0" ) ) == NULL )
{
  if( ::GetLastError() == ERROR_ALREADY_EXISTS )
  {
    // task manager is already running - close current process
  }
}

// active task manage wasn't detected - continue

Code

But first, we must find out the full name of the mutex, which is in a format like this: \Sessions\<SESSION_ID>\BaseNamedObjects\TASKMGR.879e4d63-6c0e-4544-97f2-1244bd3f6de0, and we can obtain the session ID using the ProcessIdToSessionId API call.

DWORD sid;
if( !::ProcessIdToSessionId( ::GetCurrentProcessId(), &sid ) )
{
  wprintf( L"Unable to get session ID. Error code: %d\n", ::GetLastError() );
  return 2;
}

WCHAR mutexName[ MAX_PATH ];
mutexName[ 0 ] = 0;

// full name of the mutex (we want to mess only with session of the current user)
wcscat( mutexName, L"\\Sessions\\" );
_itow( sid, mutexName + wcslen( mutexName ), 10 );
wcscat( mutexName, L"\\BaseNamedObjects\\" );
wcscat( mutexName, MUTEX_NAME );

The basic idea is to destroy the mutex object before we start another instance of the Task Manager. To destroy it, we need to find all the handles to the object and close them. In the process, we need to use these undocumented API calls: NtQuerySystemInformation, NtQueryObject, and DuplicateHandle.

We use NtQuerySystemInformation to get the list of all open handles in the system, and then we iterate through the list, duplicating handles, to gain access to the object, and call NtQueryObject to get the name of the object. When we find the handle, we make a duplicate using the DuplicateHandle API call, but this time, we pass the DUPLICATE_CLOSE_SOURCE flag that instructs the system to close the original handle after the copy is made (effectively taking the ownership of the object), and immediately after that, we also close the new handle, and the end result is destroying the object.

// Searches for handle to an object with specified name 
// Returns -1 if it cannot obtain list of handles, 
// 0 if there's no handles to the object or 
// 1 if the object if found and handle is closed
INT SeekAndDestory(WCHAR* handleName)
{
  INT found = 0;

  // get list of opened handles
  DWORD size = 0;
  PSYSTEM_HANDLE_INFORMATION handles = 
    (PSYSTEM_HANDLE_INFORMATION)malloc( sizeof( SYSTEM_HANDLE_INFORMATION ) );
  if( !NT_SUCCESS( ::pNtQuerySystemInformation(
    (SYSTEM_INFORMATION_CLASS)SystemHandleInformation, handles,
    sizeof( SYSTEM_HANDLE_INFORMATION ), &size ) ) )
  {
    free( handles );

    if( size == 0 )
      return -1;

    DWORD newSize = size + sizeof(HANDLE) * 512;
    handles = (PSYSTEM_HANDLE_INFORMATION)malloc( newSize );
    if( !NT_SUCCESS( ::pNtQuerySystemInformation( 
      (SYSTEM_INFORMATION_CLASS)SystemHandleInformation, handles,
      newSize, &size ) ) )
    {
      free( handles );
      return -1;
    }
  }

  for( DWORD i = 0; i < handles->dwCount; i++ )
  {
    HANDLE process = ::OpenProcess( PROCESS_ALL_ACCESS, FALSE, 
      handles->Handles[ i ].dwProcessId );
    if( process )
    {
      HANDLE myHandle;

      if( ::DuplicateHandle( process, 
        (HANDLE)handles->Handles[ i ].wValue, ::GetCurrentProcess(),
        &myHandle, DUPLICATE_SAME_ACCESS, FALSE, 0 ) )
      {
        // get object name
        PPUBLIC_OBJECT_TYPE_INFORMATION nameInfo =
          (PPUBLIC_OBJECT_TYPE_INFORMATION)malloc( 
          sizeof( PUBLIC_OBJECT_TYPE_INFORMATION ) );
        if( !NT_SUCCESS( pNtQueryObject( myHandle, ObjectNameInformation,
          nameInfo, sizeof( PUBLIC_OBJECT_TYPE_INFORMATION ), &size ) ) )
        {
          free( nameInfo );

          if( (int)size <= 0 )
          {
            ::CloseHandle( myHandle );
            continue;
          }

          DWORD newSize = size;
          nameInfo = (PPUBLIC_OBJECT_TYPE_INFORMATION)malloc( newSize );
          if( !NT_SUCCESS( pNtQueryObject( myHandle, ObjectNameInformation,
            nameInfo, newSize, &size ) ) )
          {
            ::CloseHandle( myHandle );
            continue;
          }
        }

        ::CloseHandle( myHandle );

        if( lstrcmp( handleName, nameInfo->TypeName.Buffer ) == 0 )
        {
          // take ownership of the handle
          // (copy handle and close original and then close the copy)
          if( ::DuplicateHandle( process, 
            (HANDLE)handles->Handles[ i ].wValue, ::GetCurrentProcess(),
            &myHandle, 0, FALSE,
            DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE ) )
          {
            ::CloseHandle( myHandle );
            found = 1;
          }
        }

        free( nameInfo );
      }

      ::CloseHandle( process );
    }
  }

  free( handles );
  return found;
}

Note: To get access to NtQuerySystemInformation and NtQueryObject, we need to use GetProcAddress because they are not available to the linker during the building process. The NtLib.h file contains the definitions required for using the undocumented API.

pfnNtQuerySystemInformation pNtQuerySystemInformation = NULL;
pfnNtQueryObject pNtQueryObject = NULL;

BOOL LoadNtLib()
{
  pNtQuerySystemInformation = (pfnNtQuerySystemInformation)::GetProcAddress(
    GetModuleHandle( TEXT( "ntdll" ) ), "NtQuerySystemInformation" );
  pNtQueryObject = (pfnNtQueryObject)::GetProcAddress(
    GetModuleHandle( TEXT( "ntdll" ) ), "NtQueryObject" );

  return pNtQuerySystemInformation && pNtQueryObject;
}

Some more information about NtQuerySystemInformation can be found in Naveen's article: Listing Used Files.

License

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

Share

About the Author

Mladen Janković
Software Developer
Serbia Serbia
No Biography provided

Comments and Discussions

 
GeneralGJ PinmemberPredrag Tomasevic1-Mar-10 4:55 
GeneralRe: GJ PinmemberMladen Jankovic1-Mar-10 6:26 
General5 bre! PinmemberNemanja Trifunovic1-Mar-10 4:46 
GeneralRe: 5 bre! PinmemberMladen Jankovic1-Mar-10 6:25 
QuestionInteresting though this is, I don't quite see what the point is - can someone tell me what I'm missing? PinmemberMike Diack1-Mar-10 0:14 
AnswerRe: Interesting though this is, I don't quite see what the point is - can someone tell me what I'm missing? PinmemberMladen Jankovic1-Mar-10 2:42 
GeneralWell Done! PinmemberRichard Andrew x6428-Feb-10 9:50 
GeneralRe: Well Done! PinmemberMladen Jankovic1-Mar-10 2:43 
GeneralMy vote of 5 PinmvpNishant Sivakumar28-Feb-10 7:35 
GeneralRe: My vote of 5 PinmemberMladen Jankovic28-Feb-10 8:24 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140922.1 | Last Updated 28 Feb 2010
Article Copyright 2010 by Mladen Janković
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid