harry’s memorandum

おれおれメモ

プロセスの優先度を強制的に変更

仕事で少し必要になったので得意じゃないVCを。

OpenProcess()でプロセスのハンドラを取得してSetPriorityClass()で優先度を変更するだけと思っていたのですが、一部のプロセスはアクセス拒否で弾かれてしまいました。サービスプロセスやSYSTEMユーザで起動しているプロセスは変更できないのですね。

でもSysinternal社のProcess Explorer*1だとお構いなしに優先度を変更できるので少し調べたところ、こんな感じでいけるのではなかろうかと。(あってるのか?)

  1. AdjustTokenPrivileges()で特権をつけて
  2. OpenProcess()でハンドラを取得して
  3. SetPriorityClass()で優先度変更
  4. AdjustTokenPrivileges()の特権を無効

とりあえずゴミコード。ここのMicrosoftサポートオンラインをまるパクリ。

#include "stdafx.h"
#include <windows.h>
#include <stdio.h>

#define RTN_OK 0
#define RTN_USAGE 1
#define RTN_ERROR 13

BOOL SetPrivilege(
  HANDLE hToken,          // token handle
  LPCTSTR Privilege,      // Privilege to enable/disable
  BOOL bEnablePrivilege   // TRUE to enable.  FALSE to disable
);
void DisplayError(LPTSTR szAPI);

int _tmain(int argc, char **argv)
{
  DWORD priority_class = BELOW_NORMAL_PRIORITY_CLASS;
  DWORD pid;
  DWORD result = true;
  HANDLE hProcess;
  HANDLE hToken;

  if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken))
  {
    if (GetLastError() == ERROR_NO_TOKEN)
    {
      if (!ImpersonateSelf(SecurityImpersonation))
        return RTN_ERROR;

      if(!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &hToken)){
        DisplayError("OpenThreadToken");
        return RTN_ERROR;
      }
  } else
    return RTN_ERROR;
  }

  // enable SeDebugPrivilege
  if(!SetPrivilege(hToken, SE_DEBUG_NAME, TRUE))
  {
    DisplayError("SetPrivilege");
    CloseHandle(hToken);
    return RTN_ERROR;
  }

  pid = atoi(argv[1]);
  if ((hProcess = OpenProcess (PROCESS_SET_INFORMATION, FALSE, pid)) == NULL)
  {
    DisplayError("OpenProcess");
    CloseHandle(hToken);
    return false;
  }

  if (!SetPriorityClass(hProcess, priority_class))
  {
    DisplayError("SetPriorityClass");
    result = false;
  }
  else
  {
    fprintf(stdout,"SetPriorityClass() Success: %d\n", priority_class);
  }
  
  // disable SeDebugPrivilege
  SetPrivilege(hToken, SE_DEBUG_NAME, FALSE);

  CloseHandle (hProcess);
  CloseHandle (hToken);

  return true;
}

BOOL SetPrivilege( 
  HANDLE hToken,  // token handle 
  LPCTSTR Privilege,  // Privilege to enable/disable 
  BOOL bEnablePrivilege  // TRUE to enable. FALSE to disable 
) 
{ 
  TOKEN_PRIVILEGES tp = { 0 }; 
  // Initialize everything to zero 
  LUID luid; 
  DWORD cb=sizeof(TOKEN_PRIVILEGES); 
  if(!LookupPrivilegeValue( NULL, Privilege, &luid ))
    return FALSE; 
  tp.PrivilegeCount = 1; 
  tp.Privileges[0].Luid = luid; 
  if(bEnablePrivilege) { 
    tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; 
  } else { 
    tp.Privileges[0].Attributes = 0; 
  } 
  AdjustTokenPrivileges( hToken, FALSE, &tp, cb, NULL, NULL ); 
  if (GetLastError() != ERROR_SUCCESS) 
    return FALSE; 

  return TRUE;
}

void DisplayError(
  LPTSTR szAPI    // pointer to failed API name
)
{
  LPTSTR MessageBuffer;
  DWORD dwBufferLength;

  fprintf(stderr,"%s() error!\n", szAPI);

  if(dwBufferLength=FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |
    FORMAT_MESSAGE_FROM_SYSTEM,
    NULL,
    GetLastError(),
    GetSystemDefaultLangID(),
    (LPTSTR) &MessageBuffer,
    0,
    NULL
    ))
  {
    DWORD dwBytesWritten;

    // Output message string on stderr
    WriteFile(GetStdHandle(STD_ERROR_HANDLE), MessageBuffer, dwBufferLength, &dwBytesWritten, NULL);

    // free the buffer allocated by the system
    LocalFree(MessageBuffer);
  }
}

いちおう出来てるっぽいかな。

プロセスの優先順位クラスを備忘録としてメモ。

REALTIME_PRIORITY_CLASS リアルタイム(R)
HIGH_PRIORITY_CLASS 高(H)
ABOVE_NORMAL_PRIORITY_CLASS 通常以上(A)
NORMAL_PRIORITY_CLASS 通常(N)
BELOW_NORMAL_PRIORITY_CLASS 通常以下(B)
IDLE_PRIORITY_CLASS 低(L)

詳細はMSDNこのあたりで。

*1:sysinternalのツールさいこー