C++ からサービスにコマンドを送信する
-
22-09-2019 - |
質問
C++ から Windows サービスにコマンドを送信するにはどうすればよいですか?同等の .NET コードは次のとおりです。
ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
解決
ネイティブ C++ からは、次のことを行う必要があります。
- サービス コントロール マネージャーへのハンドルを開きます。
- サービス コントロール マネージャーを使用して、制御するサービスのサービス ハンドルを取得します。
- 制御コードをサービスに送信し、
- 手順1、2で開いたハンドルを閉じます。
たとえば、次のコードは時刻同期サービスを再起動します。まず、サービス ハンドルのラッパー クラスを作成し、ブロックを離れるときにサービス ハンドルを自動的に閉じるようにします。
class CSC_HANDLE
{
public:
CSC_HANDLE(SC_HANDLE h) : m_h(h) { }
~CSC_HANDLE() { ::CloseServiceHandle(m_h); }
operator SC_HANDLE () { return m_h; }
private:
SC_HANDLE m_h;
};
次に、サービス コントロール マネージャーを開きます (次を使用します)。 OpenSCManager()) と制御したいサービス。dwDesiredAccess パラメータが OpenService() 送信する各コントロールのアクセス許可を含める必要があります。そうしないと、関連するコントロール機能が失敗します。
BOOL RestartTimeService()
{
CSC_HANDLE hSCM(::OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, GENERIC_READ));
if (NULL == hSCM) return FALSE;
CSC_HANDLE hW32Time(::OpenService(hSCM, L"W32Time", SERVICE_START | SERVICE_STOP | SERVICE_QUERY_STATUS));
if (NULL == hW32Time) return FALSE;
サービスを停止するには、次を使用します コントロールサービス() SERVICE_CONTROL_STOP コードを送信し、戻り値をチェックしてコマンドが成功したことを確認します。ERROR_SERVICE_NOT_ACTIVE 以外のエラーが報告された場合、サービスの開始は成功しないと考えられます。
SERVICE_STATUS ss = { 0 };
::SetLastError(0);
BOOL success = ::ControlService(hW32Time, SERVICE_CONTROL_STOP, &ss);
if (!success)
{
DWORD le = ::GetLastError();
switch (le)
{
case ERROR_ACCESS_DENIED:
case ERROR_DEPENDENT_SERVICES_RUNNING:
case ERROR_INVALID_HANDLE:
case ERROR_INVALID_PARAMETER:
case ERROR_INVALID_SERVICE_CONTROL:
case ERROR_SERVICE_CANNOT_ACCEPT_CTRL:
case ERROR_SERVICE_REQUEST_TIMEOUT:
case ERROR_SHUTDOWN_IN_PROGRESS:
return FALSE;
case ERROR_SERVICE_NOT_ACTIVE:
default:
break;
}
}
サービスの停止を指示した後、サービス管理者から実際にサービスが停止した旨の報告が来るのを待ちます。このコードには 2 つの潜在的なバグがあり、製品コードでは修正する必要があるかもしれません。
- Sleep(1000) はこのスレッドのメッセージ ループを一時停止するため、この関数が UI スレッドで実行される場合は、別のメソッドを使用して実行を遅らせる必要があります。次を使用して、適切な sleep-with-message-loop を構築できます。 MsgWaitForMultipleObjectsEx().
から返された DWORD GetTickCount() 最終的にはゼロに戻ります。この関数の待機中にラップアラウンドすると、意図したよりも早く待機が中止される可能性があります。
DWORD waitstart(::GetTickCount()); while (true) { ZeroMemory(&ss, sizeof(ss)); ::QueryServiceStatus(hW32Time, &ss); if (SERVICE_STOPPED == ss.dwCurrentState) break; ::Sleep(1000); DWORD tick(::GetTickCount()); if ((tick < waitstart) || (tick > (waitstart + 30000))) return FALSE; }
最後に、サービスが停止状態にあることを認識して、次の呼び出しを行います。 StartService() もう一度実行してください。
success = ::StartService(hW32Time, 0, NULL);
if (!success) return FALSE;
return TRUE;
}
他のヒント
あなたが見る、のControlService の使用しますサービスコントロールはに要求します。
ここでは、その後(サービスによって定義される)コマンド141を送信する「MYSERVICE」と呼ばれるサービスに接続する小さなプログラムです。
// ServiceCommunicator.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;
int main()
{
SC_HANDLE managerHandle;
SC_HANDLE serviceHandle;
SERVICE_STATUS controlParms;
DWORD retStatus;
managerHandle = OpenSCManager(NULL, NULL, GENERIC_READ);
if (NULL != managerHandle)
{
serviceHandle = OpenService(managerHandle, L"MYSERVICE", SERVICE_USER_DEFINED_CONTROL | SERVICE_QUERY_STATUS);
if (NULL != serviceHandle)
{
cout << "connected to Service" << endl;
retStatus = ControlService(serviceHandle, 141, &controlParms);
if (retStatus)
{
//Get the return code from the service
cout << "For command 141, return code from service was " << controlParms.dwWin32ExitCode << endl;
}
else
cout << "Sending command 141 failed" << endl;
CloseServiceHandle(serviceHandle);
}
else
{
cout << "could not connect to Service" << endl;
}
CloseServiceHandle(managerHandle);
}
else
{
cout << "could not open service manager" << endl;
}
return 0;
}