・スタートアップの種類の取得/変更
まず、VBA - API編 の時と同様にエラー対応のために定数を定義します。ヘッダファイルCWinService.h に下記の列挙定数(SVC_STARTMODE)を定義してください。
//CWinService.h//////////////////////////////////////////// enum SVC_STATUS { //・ //・ }; enum SVC_ACTION { //・ //・ }; enum SVC_STARTMODE { SVC_BOOT_START = 0x00000000, SVC_SYSTEM_START = 0x00000001, SVC_AUTO_START = 0x00000002, SVC_DEMAND_START = 0x00000003, SVC_DISABLED = 0x00000004, SVC_NON = 0x00000010 }; class CWinService { // ・ // ・ }; |
実はスタートアップの種類の取得と変更のコードは共通する部分が多いので、 冗長になるのを防ぐためアクセス制御がprotectedな共通のコードを先に作成して、 取得と変更を行うpublicな関数からその関数を呼び出す形にします。それでは共通のメンバ関数を メンバ関数の追加ウィザードから以下のように 作成してください。
戻値の型 : DWORD 関数名 : ExecStartMode パラメータの型 : bool パラメータ名 : bChange (追加ボタン) パラメータの型 : SVC_STARTMODE パラメータ名 : eMode (追加ボタン) アクセス制御 : protected
宣言ができたら以下のコードを入力してください。
//CWinService.cpp /////////////////////////////////////////// //スタートモード取得/変更 //引数 bChange : 取得の場合 FALSE 変更の場合 TRUE // eMode : bChange==TRUE の時変更するスタートアップの種類 // bChange==FALSE の時無視 //戻値 bChange==TRUE スタートアップが変更できた場合0以外 失敗の場合0 // bChange==FALSE 現在のスタートアップの種類 DWORD CWinService::ExecStartMode(bool bChange, SVC_STARTMODE eMode) { SC_HANDLE hSCManager = NULL; SC_HANDLE hService = NULL; DWORD dwSize = 0; LPQUERY_SERVICE_CONFIG pQscBuf = NULL; DWORD dwRet = bChange ? 0 : (DWORD)SVC_NON; while (TRUE) { //サービス制御マネージャとの接続 hSCManager = ::OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if (!hSCManager) break; //サービスOpen hService = ::OpenService(hSCManager, m_pSvcName, SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG); if (!hService) break; //QUERY_SERVICE_CONFIG構造体のサイズ取得 if ((!::QueryServiceConfig(hService, NULL, 0, &dwSize)) && //(A) (::GetLastError() != ERROR_INSUFFICIENT_BUFFER)) break; //QUERY_SERVICE_CONFIG構造体用メモリ確保 pQscBuf = (LPQUERY_SERVICE_CONFIG)::HeapAlloc(::GetProcessHeap(), //(B) HEAP_ZERO_MEMORY, dwSize); if (!pQscBuf) break; while (TRUE) { //構造体に現在のQUERY_SERVICE_CONFIG結果受取 if (!::QueryServiceConfig(hService, pQscBuf, dwSize, &dwSize)) //(C) break; if (!bChange) //StartMode取得の場合 { dwRet = pQscBuf->dwStartType; //(D) } else //StartMode変更の場合 { if (pQscBuf->dwStartType == (DWORD)eMode) break; if (!::ChangeServiceConfig(hService, //(E) SERVICE_NO_CHANGE, (DWORD)eMode, SERVICE_NO_CHANGE, NULL, NULL, NULL, NULL, NULL, NULL, pQscBuf->lpDisplayName)) break; dwRet = 1; } break; } //ヒープ解放 ::HeapFree(::GetProcessHeap(), 0, pQscBuf); //(F) break; } if (hService) ::CloseServiceHandle(hService); if (hSCManager) ::CloseServiceHandle(hSCManager); return dwRet; } |
VBA - API編の時とロジックは同じですが、VBAの時よりも逆に簡単?になります。(A)で QUERY_SERVICE_CONFIG構造体のサイズを取得したら、VBAではバイト型の配列を作成しましたが、 VC++では、(B)のようにHeapAllocでヒープにメモリを確保して型をLPQUERY_SERVICE_CONFIG (QUERY_SERVICE_CONFIG構造体へのポインタ)にキャストして、(B)で作ったヒープへのポインタを(C)で 再度QueryServiceConfigに渡すだけです。VBAの時のように変にややこしいメモリ操作する必要もないです。
現在のスタートアップの種類の取得の場合は(D)でQUERY_SERVICE_CONFIG構造体の
dwStartTypeメンバを戻値に返し、変更の場合は(E)でChangeServiceConfigで変更する種類の値を
eModeから第三引数に渡します。
最後に(F)で確保したメモリ領域を解放して各ハンドルをCloseして終了です。
それでは、上記の関数を使って外部に公開する取得/変更の関数を作成します。取得用メンバ関数 2つをメンバ関数の追加ウィザードから以下のように作成してください。
戻値の型 : STARTMODE 関数名 : StartMode アクセス制御 : public
// スタートモード取得
SVC_STARTMODE CWinService::StartMode()
{
return (SVC_STARTMODE)ExecStartMode(FALSE, SVC_NON);
}
|
戻値の型 : BOOL 関数名 : ChangeStartMode パラメータの型 : SVC_STARTMODE パラメータ名 : eMode アクセス制御 : public
// スタートモード変更
BOOL CWinService::ChangeStartMode(SVC_STARTMODE eMode)
{
return ExecStartMode(TRUE, eMode);
}
|
以上でCWinServiceクラスの実装は終わりましたので、プロジェクトの作成時に できているmain関数でテストしてみます。
・テスト
main関数があるTestWinService.cpp 実装ファイルを開いてください。CWinServiceクラスを使うため、 ここ で作成したStdWinService.hヘッダファイルを インクルードしてください。
#include "stdafx.h" //定義済み #include "StdWinService.h" //WinService用インクルードファイル |
メイン関数に以下の実装をしてください。
int _tmain(int argc, _TCHAR* argv[]) { CWinService svc(argv[1]); //CWinServiceインスタンス SVC_ACTION status = (SVC_ACTION)_tstol(argv[2]); SVC_STARTMODE mode = (SVC_STARTMODE)_tstol(argv[3]); _tprintf(_T("Status : %d\n"), svc.Status()); _tprintf(_T("ChangeStatus : %d\n"), svc.ChangeServiceStatus(status)); _tprintf(_T("Status : %d\n\n"), svc.Status()); _tprintf(_T("StartMode : %d\n"), svc.StartMode()); _tprintf(_T("ChangeStartMode : %d\n"), svc.ChangeStartMode(mode)); _tprintf(_T("StartMode : %d\n"), svc.StartMode()); return 0; } |
ビルドを実行して実行ファイルができたら、Visual Studioから離れて スタートメニューからファイル名を指定して実行(R)でcmdと 入力してコンソールを起動してください。実行ファイル(TestWinService.exe) があるフォルダに移動して
>TestWinService.exe "MSSQL$SQLEXPRESS" 4 2 [リターン]
とコマンドを入力してください。上記の場合はSQL Server 2005 Expressの
サービスでサービスの状態を「再開」に変更、スタートアップの種類を「自動」に
変更しようとしています。実行結果が以下のように出力されると思います。
(停止させちゃまずいサービスもあるので気をつけてやってください!!)
以上でVC++ 2005での実装を終わりますが、あとVC++ではATL + WMIでの方法も ありますがVC++をつかってわざわざ重い処理をすることに疑問を感じますので また機会があれば書いていきたいと思います。