运行任务管理器多个实例






4.84/5 (16投票s)
一个简单的应用程序,允许用户运行多个任务管理器实例。
引言
众所周知,任务管理器出于某种原因,不允许用户运行多个实例。本文介绍了一个简单的应用程序,允许用户启动任意数量的任务管理器实例。这里描述的技术仅适用于 Windows 7(以及可能 Vista);XP 的任务管理器使用不同的技术。
背景
为此,它采用简单的策略。当它开始执行时,它会尝试创建一个命名的内核对象(在本例中,一个互斥锁),如果已经存在一个名为“任务管理器”的对象,它会假定已经有另一个实例在运行,并关闭当前进程。
该互斥锁的名称是 TASKMGR.879e4d63-6c0e-4544-97f2-1244bd3f6de0。
以下是如何实现的示例
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
代码
但首先,我们必须找出互斥锁的完整名称,其格式如下:\Sessions\<SESSION_ID>\BaseNamedObjects\TASKMGR.879e4d63-6c0e-4544-97f2-1244bd3f6de0,我们可以使用 ProcessIdToSessionId API 调用来获取会话 ID。
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 );
基本思路是在启动另一个任务管理器实例之前销毁互斥锁对象。要销毁它,我们需要找到对象的所有句柄并关闭它们。在此过程中,我们需要使用这些未公开的 API 调用:NtQuerySystemInformation、NtQueryObject 和 DuplicateHandle。
我们使用 NtQuerySystemInformation 获取系统中所有打开句柄的列表,然后遍历该列表,复制句柄以获得对该对象的访问权限,并调用 NtQueryObject 获取对象名称。当我们找到句柄时,我们使用 DuplicateHandle API 调用创建一个副本,但这次,我们传递 DUPLICATE_CLOSE_SOURCE 标志,指示系统在复制后关闭原始句柄(有效地获取对象的控制权),然后立即关闭新句柄,最终结果是销毁该对象。
// 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;
}
注意:要访问 NtQuerySystemInformation 和 NtQueryObject,我们需要使用 GetProcAddress,因为它们在构建过程中对链接器不可用。NtLib.h 文件包含使用未公开 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;
}
有关 NtQuerySystemInformation 的更多信息,请参阅 Naveen 的文章:列出已使用的文件。


