【问题标题】:c++ restart service in windows as adminc ++以管理员身份在Windows中重新启动服务
【发布时间】:2019-12-11 22:24:18
【问题描述】:

我正在尝试在 Windows 10 上的 C++ 应用程序中重新启动“postgres”服务,但我在访问权限方面遇到了一些问题(我的猜测)。我尝试以具有本地管理员权限的域用户和本地管理员的身份运行代码,但这些似乎都不起作用。我可以通过 services.msc 手动重启服务。代码在返回 NULL 的 OpenSCManager 处已经失败。我也尝试过其他访问权限,但 OpenService 失败了。这是我的代码

auto showError = []()
{
    std::ostringstream os;
    os << GetLastError();

    qDebug() << "Restart PostgreSQL service failed : " << QString::fromStdString( os.str());
};

SERVICE_STATUS Status;

SC_HANDLE SCManager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_ALL_ACCESS );

if(SCManager == NULL)
    showError();

SC_HANDLE SHandle = OpenService(SCManager, L"postgres", SERVICE_ALL_ACCESS );

if(SHandle == NULL)
    showError();

if(!ControlService(SHandle, SERVICE_CONTROL_STOP, &Status))
    showError();

do
{
    QueryServiceStatus(SHandle, &Status);
    qDebug() << "Checking Service Status...\n";
}while(Status.dwCurrentState != SERVICE_STOPPED);


if(!StartService(SHandle, 0, NULL))
    showError();

std::cin.sync();
std::cin.ignore();

CloseServiceHandle(SCManager);
CloseServiceHandle(SHandle);

【问题讨论】:

  • GetLastError 的结果是什么?是访问被拒绝还是其他原因?
  • 错误 5 访问被拒绝

标签: c++ windows api service


【解决方案1】:

您要求的权限过多,而您实际上并不需要。不要使用SC_MANAGER_ALL_ACCESSSERVICE_ALL_ACCESS。永远不要要求比你真正需要的更多的权限。在这种情况下,您真正​​需要的只是SC_MANAGER_CONNECT 用于OpenSCManager()SERVICE_STOPSERVICE_STARTSERVICE_QUERY_STATUS 用于OpenService()

如果您在代码中修复此问题后仍然收到“拒绝访问”错误,则该服务确实需要您的帐户具有启动/停止服务的权限。因此,要么在提升的管理员进程中运行您的代码,要么至少为您的用户帐户配置适当的权限。

仅供参考,您的查询循环过于简约。您忽略了ControlService() 报告的Status,并且您没有考虑服务进入挂起状态或拒绝停止的可能性。您需要检查初始状态,只有当它处于挂起状态时才进入查询循环,直到它不再处于挂起状态,然后在尝试启动服务之前检查最终状态是否已停止。还要确保在尝试停止服务时检查服务是否挂起。

请参阅 MSDN 上的 Stopping a Service

试试这样的:

auto showError = []()
{
    DWORD err = GetLastError();
    qDebug() << "Restart PostgreSQL service failed. Error: " << err << "\n";
};

SERVICE_STATUS Status;

SC_HANDLE SCManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
if (!SCManager)
{
    showError();
    return;
}

SC_HANDLE SHandle = OpenService(SCManager, L"postgres", SERVICE_STOP | SERVICE_START | SERVICE_QUERY_STATUS);
if (!SHandle)
{
    showError();
    CloseServiceHandle(SCManager);
    return;
}

if (!QueryServiceStatus(SHandle, &Status))
{
    showError(); 
    CloseServiceHandle(SHandle);
    CloseServiceHandle(SCManager);
    return;
}

if (Status.dwCurrentState != SERVICE_STOPPED)
{
    qDebug() << "Stopping PostgreSQL service...\n";

    if (!ControlService(SHandle, SERVICE_CONTROL_STOP, &Status))
    {
        showError();
        CloseServiceHandle(SHandle);
        CloseServiceHandle(SCManager);
        return;
    }

    DWORD dwStartTime = GetTickCount();
    DWORD dwTimeout = 30000; // 30-second time-out
    DWORD dwWaitTime;

    while (Status.dwCurrentState == SERVICE_STOP_PENDING)
    {
        qDebug() << "Waiting for PostgreSQL service to stop...\n";

        dwWaitTime = Status.dwWaitHint / 10;
        if (dwWaitTime < 1000)
            dwWaitTime = 1000;
        else if (dwWaitTime > 10000)
            dwWaitTime = 10000;

        Sleep(dwWaitTime);

        if (!QueryServiceStatus(SHandle, &Status))
        {
            showError();
            CloseServiceHandle(SHandle);
            CloseServiceHandle(SCManager);
            return;
        }

        if (Status.dwCurrentState != SERVICE_STOP_PENDING)
            break;

        if (GetTickCount() - dwStartTime > dwTimeout)
        {
            qDebug() << "Stop of PostgreSQL service timed out.\n";
            CloseServiceHandle(SHandle);
            CloseServiceHandle(SCManager);
            return;
        }
    }

    if (Status.dwCurrentState != SERVICE_STOPPED)
    {
        qDebug() << "Restart PostgreSQL service failed. Service did not stop.\n";
        CloseServiceHandle(SHandle);
        CloseServiceHandle(SCManager);
        return;
    }

    qDebug() << "PostgreSQL service stopped successfully.\n";
}

if (!StartService(SHandle, 0, NULL))
    showError();

CloseServiceHandle(schService); 
CloseServiceHandle(schSCManager);

【讨论】:

  • 感谢您更正代码的最后一部分,但我仍然收到拒绝访问错误,您能告诉我如何或在哪里可以找到有关您建议的解决方案之一的信息吗?
  • 正如我在回答中所说:“如果您在代码中修复此问题后仍然收到“拒绝访问”错误,那么该服务确实需要您的帐户具有启动/停止权限服务。”所以这变成了权限问题,而不是编码问题。如果你想在代码中解决这个问题,你可以将你的重启代码分离到它自己的函数中,然后如果它因“拒绝访问”而失败并且你还没有运行提升然后使用ShellExecute/Ex("runas")运行你的新提升副本EXE 传递给它一个命令行参数,告诉它只调用该函数然后退出。
  • 我尝试在网上阅读一些文章,但我不明白为什么我的帐户可以手动重启服务但不能通过代码。 ShellExecute/Ex("runas") 不会触发 uac 吗?
  • 显然,本机 SCM 应用程序正在使用一个用户帐户运行,该用户帐户比您的用户帐户在运行您的应用程序时拥有的权限更多。因此,找出您的用户帐户缺少所需权限的原因。或者只是确保您的应用程序从一开始就以提升的管理员用户身份运行。是的,ShellExecute/Ex("runas") 会触发 UAC,这就是重点 - 启动一个新进程,该进程在由缺少权限的进程启动时具有提升的权限。
  • 这似乎工作CreateProcessWithLogonW(L"Administrator",L"MyPC",L"Password",LOGON_WITH_PROFILE,NULL,szCmdline,CREATE_NEW_CONSOLE, NULL, NULL, &amp;siStartInfo, &amp;processInfo) 所以我当前的帐户是域帐户和本地管理员的成员,它不起作用,但使用内置管理员帐户它可以工作。当我从 services.msc 重新启动服务而没有任何我可以观察到的 uac 或提升时,你或其他人能否向我解释为什么它可以与我的域帐户一起使用
猜你喜欢
  • 2015-09-26
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 1970-01-01
  • 1970-01-01
  • 2018-03-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多