Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / Win32

Running multiple instances of Task Manager

Rate me:
Please Sign up or sign in to vote.
4.84/5 (18 votes)
28 Feb 2010CPOL2 min read 65.4K   1.8K   25   23
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:

C++
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.

C++
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.

C++
// 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.

C++
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)


Written By
Software Developer
Serbia Serbia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCouldn't get this to work, but did find a solution Pin
Ron Nov202125-Feb-24 8:45
Ron Nov202125-Feb-24 8:45 
QuestionKind Request from C++ to VBNET Pin
Mikloo22-Sep-15 9:04
Mikloo22-Sep-15 9:04 
QuestionWindows 8 Pin
Member 1013314629-Jun-13 23:36
Member 1013314629-Jun-13 23:36 
AnswerRe: Windows 8 Pin
Mladen Janković1-Jul-13 7:44
Mladen Janković1-Jul-13 7:44 
GeneralRe: Windows 8 Pin
Member 1072155013-Mar-14 7:06
Member 1072155013-Mar-14 7:06 
AnswerRe: Windows 8.1 Pro 32. Pin
Member 476859324-Jan-14 19:54
Member 476859324-Jan-14 19:54 
QuestionIt doesn't actually work, at least not in Win7x64 Pin
Member 100981997-Jun-13 5:40
Member 100981997-Jun-13 5:40 
AnswerRe: It doesn't actually work, at least not in Win7x64 Pin
Mladen Janković7-Jun-13 9:04
Mladen Janković7-Jun-13 9:04 
GeneralExcellent! Pin
SouzaRM4-Nov-12 16:54
SouzaRM4-Nov-12 16:54 
GeneralCool stuff. Pin
Fredrik Bornander9-Mar-10 5:23
professionalFredrik Bornander9-Mar-10 5:23 
GeneralSysinternals process explorer Pin
owillebo2-Mar-10 1:48
owillebo2-Mar-10 1:48 
GeneralRe: Sysinternals process explorer Pin
Mladen Janković2-Mar-10 2:51
Mladen Janković2-Mar-10 2:51 
GeneralGJ Pin
lepipele1-Mar-10 4:55
lepipele1-Mar-10 4:55 
GeneralRe: GJ Pin
Mladen Janković1-Mar-10 6:26
Mladen Janković1-Mar-10 6:26 
General5 bre! Pin
Nemanja Trifunovic1-Mar-10 4:46
Nemanja Trifunovic1-Mar-10 4:46 
GeneralRe: 5 bre! Pin
Mladen Janković1-Mar-10 6:25
Mladen Janković1-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? Pin
Mike Diack1-Mar-10 0:14
Mike 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? Pin
Mladen Janković1-Mar-10 2:42
Mladen Janković1-Mar-10 2:42 
AnswerRe: Interesting though this is, I don't quite see what the point is - can someone tell me what I'm missing? Pin
Member 1215814917-Apr-23 16:07
Member 1215814917-Apr-23 16:07 
GeneralWell Done! Pin
Richard Andrew x6428-Feb-10 9:50
professionalRichard Andrew x6428-Feb-10 9:50 
GeneralRe: Well Done! Pin
Mladen Janković1-Mar-10 2:43
Mladen Janković1-Mar-10 2:43 
GeneralMy vote of 5 Pin
Nish Nishant28-Feb-10 7:35
sitebuilderNish Nishant28-Feb-10 7:35 
GeneralRe: My vote of 5 Pin
Mladen Janković28-Feb-10 8:24
Mladen Janković28-Feb-10 8:24 

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.