5

如何从 C++ 向 Windows 服务发送命令?等效的 .NET 代码是:

ServiceController sc = new ServiceController("MyService");
sc.ExecuteCommand(255);
4

3 回答 3

3

您使用ControlService,请参阅服务控制请求

于 2009-10-20T07:15:58.770 回答
3

在原生 C++ 中,您需要:

  1. 打开服务控制管理器的句柄,
  2. 使用服务控制管理器获取要控制的服务的服务句柄,
  3. 向服务发送一个或多个控制代码,以及
  4. 关闭在步骤 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())和我想要控制的服务。请注意,OpenService()的 dwDesiredAccess 参数必须包含我要发送的每个控件的权限,否则相关的控件功能将失败。

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;

为了停止服务,我使用ControlService()发送 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;
        }
    }

指示服务停止后,我等待服务管理器报告服务实际上已停止。此代码有两个潜在错误,您可能希望针对生产代码进行更正:

  1. Sleep(1000) 将暂停此线程上的消息循环,因此如果此函数将在 UI 线程上运行,您应该使用另一种方法来延迟执行。您可以使用MsgWaitForMultipleObjectsEx()构造一个合适的 sleep-with-message-loop 。
  2. GetTickCount()返回的 DWORD最终会回绕为零;如果它在此函数等待时回绕,则等待可能会比我预期的更早放弃。

    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;
}
于 2009-10-20T13:24:28.367 回答
1

这是一个小程序,它将连接到名为“MYSERVICE”的服务,然后发送命令 141(由服务定义)

// 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;
}
于 2018-04-27T21:03:00.290 回答