65.9K
CodeProject 正在变化。 阅读更多。
Home

运行任务管理器多个实例

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (16投票s)

2010年2月28日

CPOL

2分钟阅读

viewsIcon

68720

downloadIcon

1826

一个简单的应用程序,允许用户运行多个任务管理器实例。

引言

众所周知,任务管理器出于某种原因,不允许用户运行多个实例。本文介绍了一个简单的应用程序,允许用户启动任意数量的任务管理器实例。这里描述的技术仅适用于 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 调用:NtQuerySystemInformationNtQueryObjectDuplicateHandle

我们使用 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;
}

注意:要访问 NtQuerySystemInformationNtQueryObject,我们需要使用 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 的文章:列出已使用的文件

© . All rights reserved.