Windowsサービスの開始/停止/再起動

解説


 Windows NT/2000/XP系に備わっているWindowsサービスを起動したり停止する方法。

 APIのOpenSCManagerでサービスコントローラを開き、OpenServiceでサービスにアクセスし、 サービスの開始はStartServiceを、停止はControlServiceを利用する。



使用例


 Windowsに備わっているサービス「Help and Support」(helpsvc)が起動していれば 停止、停止していれば開始する。


bool	Test()
{
	bool		ret;
	CDnpService	cService;

	if(cService.IsServiceRunning(_T("helpsvc")))
		ret = cService.EasyStop(_T("helpsvc"));
	else
		ret = cService.EasyStart(_T("helpsvc"));

	return	ret;
}



ソースコード


 スレッド上でサービスの起動/停止をするクラスからスレッド操作部分を削除しただけなので かなり汚いソースコードになっています。

#pragma once

#include "atlstr.h"

class	CDnpService
{
	//
	//	サービスの起動/停止用スレッドクラス
	//
	class CServiceThread
	{
	public:
		CServiceThread()
		{
			_bCancel = false;
		}

	private:

		bool					_bCancel;			//サービスの起動/停止処理中断用変数、trueなら中断開始
		CComAutoCriticalSection	_secbCancel;		//サービスの起動/停止処理中断用クリティカルセクション

	public:

		//
		//	サービスの起動/停止処理中断用関数
		//
		//	中断したい場合はIsCancel(true,true)を呼び出す
		//
		bool	IsCancel(bool bSave=false,bool bNewValue=false)
		{
			bool	ret;

			_secbCancel.Lock();
				if(bSave)
				{
					_bCancel = bNewValue;
					ret = true;
				}
				else
					ret = _bCancel;
			_secbCancel.Unlock();

			return	ret;
		}



		//
		//	サービスの簡易コントロール
		//
		//	そのまま呼び出すとサービスが起動/停止するまで無限ループで待機する。
		//	スレッド中で呼び出し、IsCancel()を利用することで無限ループに陥らない
		//	コントロールが可能。
		//
		bool	EasyStartStop(LPCTSTR pszName,bool bStart)
		{
			bool			ret;
			BOOL			bRet;
			SC_HANDLE		hManager;
			SC_HANDLE		hService;
			SERVICE_STATUS	sStatus;

			ret = false;
			hManager = NULL;
			hService = NULL;
			while(1)			//無限ループではない!
			{
				hManager = ::OpenSCManager(NULL,NULL,GENERIC_EXECUTE);
				if(hManager == NULL)
					break;

				if(bStart)
					hService = ::OpenService(hManager,pszName,SERVICE_START | SERVICE_QUERY_STATUS);
				else
					hService = ::OpenService(hManager,pszName,SERVICE_STOP | SERVICE_QUERY_STATUS);
				if(hService == NULL)
					break;

				::ZeroMemory(&sStatus,sizeof(SERVICE_STATUS));
				bRet = ::QueryServiceStatus(hService,&sStatus);
				if(bRet == FALSE)
					break;

				if(bStart && sStatus.dwCurrentState == SERVICE_RUNNING)
				{
					//既にサービスは動いている
					ret = true;
					break;
				}
				if((bStart == false) && sStatus.dwCurrentState == SERVICE_STOPPED)
				{
					//既にサービスは止まっている
					ret = true;
					break;
				}

				if(bStart)
				{
					////////////////////////////
					//	サービス開始
					//

					if(sStatus.dwCurrentState == SERVICE_STOPPED)
					{
						//サービス開始要求
						bRet = ::StartService(hService,NULL,NULL);
						if(bRet == FALSE)
							break;

						//開始まで無限ループで待機
						//IsCancelを利用すれば無限ループからの脱出が可能
						while(::QueryServiceStatus(hService,&sStatus))
						{
							if(sStatus.dwCurrentState != SERVICE_RUNNING)
							{
								ret = true;
								break;
							}

							if(IsCancel())
								break;

							//::Sleep(sStatus.dwWaitHint);
							//本来ならばdwWaitHitだけSleepをかけるが、中断処理を入れるため
							//500msecを上限にSleepする
							::Sleep((sStatus.dwWaitHint > 500) ? 500 : sStatus.dwWaitHint);
							continue;
						}
					}
					break;
				}


				////////////////////////////
				//	サービス停止
				//

				if(sStatus.dwCurrentState == SERVICE_RUNNING)
				{
					//サービス停止要求
					bRet = ::ControlService(hService,SERVICE_CONTROL_STOP,&sStatus);
					if(bRet == FALSE)
						break;

					//停止まで無限ループで待機
					//IsCancelを利用すれば無限ループからの脱出が可能
					while(::QueryServiceStatus(hService,&sStatus))
					{
						if(sStatus.dwCurrentState == SERVICE_STOPPED)
						{
							ret = true;
							break;
						}

						if(IsCancel())
							break;

						//::Sleep(sStatus.dwWaitHint);
						//本来ならばdwWaitHitだけSleepをかけるが、中断処理を入れるため
						//500msecを上限にSleepする
						::Sleep((sStatus.dwWaitHint > 500) ? 500 : sStatus.dwWaitHint);
						continue;
					}
				}

				break;		//必須!この行がないと無限ループになるかも
			}

			ATLASSERT(ret);

			if(hService)
				::CloseServiceHandle(hService);
			if(hManager)
				::CloseServiceHandle(hManager);

			return	ret;
		}
	};


public:


	//
	//	サービスの簡易コントロール
	//
	//	サービスが起動/停止するまで無限ループで待機する。
	//
	bool	EasyStartStop(LPCTSTR pszName,bool bStart)
	{
		CServiceThread	cThread;

		return	cThread.EasyStartStop(pszName,bStart);
	}


	//
	//	サービスの簡易起動
	//
	//	サービスが起動するまで無限ループで待機する。
	//
	bool	EasyStart(LPCTSTR pszName)
	{
		return	EasyStartStop(pszName,true);
	}

	//
	//	サービスの簡易停止
	//
	//	サービスが停止するまで無限ループで待機する。
	//
	bool	EasyStop(LPCTSTR pszName)
	{
		return	EasyStartStop(pszName,false);
	}


	//
	//	サービスの簡易再起動
	//
	//	サービスが再起動するまで無限ループで待機する。
	//
	bool	EasyRestart(LPCTSTR pszName)
	{
		bool			ret;
		CServiceThread	cThread;

		ret = cThread.EasyStartStop(pszName,false);
		if(ret)
			ret = cThread.EasyStartStop(pszName,true);

		return	ret;
	}



	//
	//	指定するサービスが動いているかのチェック
	//
	//	falseの場合は"停止"とは限らない。サービスが存在しない場合などもfalseとなる。
	//
	bool	IsServiceRunning(LPCTSTR pszName)
	{
		bool			ret;
		BOOL			bRet;
		SC_HANDLE		hManager;
		SC_HANDLE		hService;
		SERVICE_STATUS	sStatus;

		ret = false;
		hManager = NULL;
		hService = NULL;
		while(1)			//無限ループではない!
		{
			hManager = ::OpenSCManager(NULL,NULL,GENERIC_EXECUTE);
			ATLASSERT(hManager);
			if(hManager == NULL)
				break;

			hService = ::OpenService(hManager,pszName,SERVICE_QUERY_STATUS);
			ATLASSERT(hService);
			if(hService == NULL)
				break;

			::ZeroMemory(&sStatus,sizeof(SERVICE_STATUS));
			bRet = ::QueryServiceStatus(hService,&sStatus);
			ATLASSERT(bRet);
			if(bRet == FALSE)
				break;

			if(sStatus.dwCurrentState == SERVICE_RUNNING)
				ret = true;

			break;		//必須
		}

		if(hService)
			::CloseServiceHandle(hService);
		if(hManager)
			::CloseServiceHandle(hManager);

		return	ret;
	}
};

カテゴリー「VC++ TIPS」 のエントリー