EternalWindows
サービス / サービスの列挙

これまで作成したSCPは、制御対象となるサービスが1つだけでしたが、 任意のサービスを選択できるユーザーインターフェースを備えていた方が、 より優秀なSCPといえるでしょう。 システムに存在するサービスは、EnumServicesStatusで列挙することができます。

BOOL EnumServicesStatus(
  SC_HANDLE hSCManager,
  DWORD dwServiceType,
  DWORD dwServiceState,
  LPENUM_SERVICE_STATUS lpServices,
  DWORD cbBufSize,
  LPDWORD pcbBytesNeeded,
  LPDWORD lpServicesReturned,
  LPDWORD lpResumeHandle
);

hSCManagerは、SC_MANAGER_ENUMERATE_SERVICEアクセス権を割り当てたハンドルです。 dwServiceTypeは、SERVICE_WIN32を指定することになります。 dwServiceStateは、どの状態にあるサービスを列挙するのかを指定します。 lpServicesは、ENUM_SERVICE_STATUS構造体の配列を格納するバッファへのポインタです。 cbBufSizeは、バッファのサイズをバイト数で指定します。 pcbBytesNeededは、バッファを格納するのに必要なバイト数が返ります。 lpServicesReturnedは、ENUM_SERVICE_STATUS構造体の配列の要素数が返ります。 lpResumeHandleは、NULLを指定して構いません。

dwServiceStateは、次の定数のいずれかを指定します。

定数 意味
SERVICE_ACTIVE 次のいずれかの状態になっているサービスを列挙する。 SERVICE_START_PENDING、SERVICE_STOP_PENDING、 SERVICE_RUNNING、SERVICE_CONTINUE_PENDING、 SERVICE_PAUSE_PENDING、SERVICE_PAUSED
SERVICE_INACTIVE SERVICE_STOPPED状態のサービスを列挙する。
SERVICE_STATE_ALL SERVICE_ACTIVEとSERVICE_INACTIVEを組み合わせた状態のサービスを列挙する。

サービスの制御を目的とする場合、停止状態のサービスを再開できなければ ならないので、SERVICE_STATE_ALLを指定することになるでしょう。 ステータスを取得するつもりだけなら、停止中のサービスを対象としても仕方ないので、 このようなときはSERVICE_ACTIVEが役に立つと思われます。

ENUM_SERVICE_STATUS構造体は、以下のように定義されています。

typedef struct _ENUM_SERVICE_STATUS {
  LPTSTR lpServiceName;
  LPTSTR lpDisplayName;
  SERVICE_STATUS ServiceStatus;
} ENUM_SERVICE_STATUS, *LPENUM_SERVICE_

この構造体はSERVICE_STATUS構造体を含んでいますが、 目的のサービスの現在のステータスを常に正確に知りたいような場合は、 lpServiceNameを引数としてOpenServiceを呼び出し、 その後QueryServiceStatusを呼び出してください。 というのも、ENUM_SERVICE_STATUS構造体のSERVICE_STATUS構造体は、 EnumServicesStatusを呼び出した時点でのスタータスであるため、 常に最新の情報を反映しているとは限らないからです。

今回のプログラムは、EnumServicesStatusで取得した表示名を 左のリストボックスへと列挙していきます。 このリストボックスの項目を選択すると、右のリストボックスに サービスの構成情報(ChangeServiceConfigで指定できる情報)が表示されます。

#include <windows.h>

void AddFailureActions(HWND hwndListBox, LPSERVICE_FAILURE_ACTIONS lpFailureActions);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND                  hwndListBoxLeft = NULL;
	static HWND                  hwndListBoxRight = NULL;
	static SC_HANDLE             hSCManager = NULL;
	static LPENUM_SERVICE_STATUS lpss = NULL;

	switch (uMsg) {

	case WM_CREATE: {
		DWORD i;
		DWORD dwSize;
		DWORD dwServiceCount;
		
		hwndListBoxLeft = CreateWindowEx(WS_EX_DLGMODALFRAME, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndListBoxRight = CreateWindowEx(WS_EX_DLGMODALFRAME, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 0, 0, 0, 0, hwnd, (HMENU)2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		
		hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
		if (hSCManager == NULL) {
			MessageBox(NULL, TEXT("SCMデータベースのオープンに失敗しました。"), NULL, MB_ICONWARNING);
			return -1;
		}
		
		EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &dwSize, &dwServiceCount, NULL);
		lpss = (LPENUM_SERVICE_STATUS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
		EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpss, dwSize, &dwSize, &dwServiceCount, NULL);

		for (i = 0; i < dwServiceCount; i++)
			SendMessage(hwndListBoxLeft, LB_ADDSTRING, 0, (LPARAM)lpss[i].lpDisplayName);
	
		return 0;
	}

	case WM_COMMAND: {
		SC_HANDLE                 hService;
		TCHAR                     szBuf[256];
		int                       nIndex;
		DWORD                     dwSize;
		LPQUERY_SERVICE_CONFIG    lpServiceConfig;
		LPSERVICE_FAILURE_ACTIONS lpFailureActions;

		if ((HWND)lParam == hwndListBoxRight || HIWORD(wParam) != LBN_SELCHANGE)
			return 0;
		
		SendMessage(hwndListBoxRight, LB_RESETCONTENT, 0, 0);
	
		nIndex = (int)SendMessage(hwndListBoxLeft, LB_GETCURSEL, 0, 0);
		
		hService = OpenService(hSCManager, lpss[nIndex].lpServiceName, SERVICE_QUERY_CONFIG);
		if (hService == NULL) {
			MessageBox(NULL, TEXT("サービスのオープンに失敗しました。"), NULL, MB_ICONWARNING);	
			return -1;
		}
		
		QueryServiceConfig(hService, NULL, 0, &dwSize);
		lpServiceConfig = (LPQUERY_SERVICE_CONFIG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
		QueryServiceConfig(hService, lpServiceConfig, dwSize, &dwSize);

		QueryServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &dwSize);
		lpFailureActions = (LPSERVICE_FAILURE_ACTIONS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
		QueryServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)lpFailureActions, dwSize, &dwSize);

		wsprintf(szBuf, TEXT("lpServiceName: %s"), lpss[nIndex].lpServiceName);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)TEXT(""));

		wsprintf(szBuf, TEXT("lpBinaryPathName: %s"), lpServiceConfig->lpBinaryPathName);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)TEXT(""));

		wsprintf(szBuf, TEXT("lpLoadOrderGroup: %s"), lpServiceConfig->lpLoadOrderGroup);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)TEXT(""));

		wsprintf(szBuf, TEXT("lpServiceStartName: %s"), lpServiceConfig->lpServiceStartName);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)TEXT(""));
		
		wsprintf(szBuf, TEXT("FailureActions: (dwResetPeriod %d)"), lpFailureActions->dwResetPeriod);
		SendMessage(hwndListBoxRight, LB_ADDSTRING, 0, (LPARAM)szBuf);
		AddFailureActions(hwndListBoxRight, lpFailureActions);

		CloseServiceHandle(hService);
		HeapFree(GetProcessHeap(), 0, lpServiceConfig);
		HeapFree(GetProcessHeap(), 0, lpFailureActions);

		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBoxLeft, 0, 0, LOWORD(lParam) / 3, HIWORD(lParam), TRUE);
		MoveWindow(hwndListBoxRight, LOWORD(lParam) / 3, 0, LOWORD(lParam) - LOWORD(lParam) / 3, HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		if (hSCManager != NULL)
			CloseServiceHandle(hSCManager);
		if (lpss != NULL)
			HeapFree(GetProcessHeap(), 0, lpss);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

void AddFailureActions(HWND hwndListBox, LPSERVICE_FAILURE_ACTIONS lpFailureActions)
{
	DWORD i;
	TCHAR szBuf[512];
	TCHAR szAction[256];

	for (i = 0; i < lpFailureActions->cActions; i++) {
		switch (lpFailureActions->lpsaActions[i].Type) {
		case SC_ACTION_NONE:
			lstrcpy(szAction, TEXT("何もしない"));
			break;
		case SC_ACTION_REBOOT:
			wsprintf(szAction, TEXT("コンピュータの再起動 (%s)"), lpFailureActions->lpRebootMsg);
			break;
		case SC_ACTION_RESTART:
			lstrcpy(szAction, TEXT("サービスの再開"));
			break;
		case SC_ACTION_RUN_COMMAND:
			wsprintf(szAction, TEXT("プログラムの実行 (%s)"), lpFailureActions->lpCommand);
			break;
		default:
			break;
		}

		wsprintf(szBuf, TEXT("No %d Delay %d - %s"), i, lpFailureActions->lpsaActions[i].Delay, szAction);
		SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)szBuf);
	}

}

左のリストボックスに列挙されているサービスは、そのステータスを問いません。 つまり、全てのサービスを列挙します。

EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &dwSize, &dwServiceCount, NULL);
lpss = (LPENUM_SERVICE_STATUS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
EnumServicesStatus(hSCManager, SERVICE_WIN32, SERVICE_STATE_ALL, lpss, dwSize, &dwSize, &dwServiceCount, NULL);

全てのサービスを列挙するため、第3引数はSERVICE_STATE_ALLとします。 EnumServicesStatusの1回目の呼び出しでは、 バッファに必要なサイズの取得に専念します。 そして、取得したサイズ分のメモリを確保し、再度EnumServicesStatusを呼び出します。 このときは、第4引数にバッファへのポインタを指定します。 バッファは、ENUM_SERVICE_STATUS構造体の配列で構成されているため、 添え字によって簡単に参照することができます。

for (i = 0; i < dwServiceCount; i++)
	SendMessage(hwndListBoxLeft, LB_ADDSTRING, 0, (LPARAM)lpss[i].lpDisplayName);

サービスの表示名を左のリストボックスに追加します。 サービス名を追加してもよいのですが、サービスは一般に表示名で識別されるため、 ここではそれに倣うことにしました。

左のリストボックスの項目が選択されたら、 そのセルをENUM_SERVICE_STATUS構造体の要素数としてOpenServiceを呼び出し、 選択されたサービスのハンドルを取得します。 そして、QueryServiceConfigで構成情報を取得し、 さらにQueryServiceConfig2でオプション情報も取得します。

QueryServiceConfig(hService, NULL, 0, &dwSize);
lpServiceConfig = (LPQUERY_SERVICE_CONFIG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
QueryServiceConfig(hService, lpServiceConfig, dwSize, &dwSize);

QueryServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, NULL, 0, &dwSize);
lpFailureActions = (LPSERVICE_FAILURE_ACTIONS)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize);
QueryServiceConfig2(hService, SERVICE_CONFIG_FAILURE_ACTIONS, (LPBYTE)lpFailureActions, dwSize, &dwSize);

QueryServiceConfigの第2引数はQUERY_SERVICE_CONFIG構造体のアドレスですが、 ここに直接構造体のアドレスを指定するわけにはいきません。 この構造体には文字列が含まれていることからサイズが可変であるため、 文字列を考慮したQUERY_SERVICE_CONFIG構造体のサイズを取得し、 再びQueryServiceConfigを呼び出さなければなりません。 QueryServiceConfig2もこの要領と同じとなります。 この関数の第2引数にSERVICE_CONFIG_DESCRIPTIONを指定すると、 サービスの説明文を取得することができます。


戻る