【问题标题】:Can't shutdown a process in a user session from a service无法从服务关闭用户会话中的进程
【发布时间】:2017-08-07 17:28:20
【问题描述】:

该架构是一个服务可以在具有系统特权的用户会话中启动进程组内的进程,代码 sn -p 1。

当服务需要自行停止时,我想向子进程发送一个信号,让它有机会优雅地关闭,代码 sn -p 2。

问题是它似乎根本没有发送信号而没有任何明显的错误代码。我已经测试了从命令提示符运行的子进程,ctrl+break 工作正常。

代码 sn-p 1

PROCESS_INFORMATION processInfo;
ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION));

STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.lpDesktop = "winsta0\\Default";
si.dwFlags |= STARTF_USESTDHANDLES;

LPVOID environment;
BOOL createRet = CreateEnvironmentBlock(&environment, userToken, FALSE);
if (!createRet) {
    throw std::runtime_error("Failed to create environment block");
}

DWORD creationFlags =
    NORMAL_PRIORITY_CLASS |
    CREATE_NO_WINDOW |
    CREATE_UNICODE_ENVIRONMENT |
    CREATE_NEW_PROCESS_GROUP; // this create a process in a group

// launch a process in the user session
// the toke is a system privilege toke within the user session
createRet = CreateProcessAsUser(
    userToken, NULL, LPSTR(command.c_str()),
    sa, NULL, TRUE, creationFlags,
    environment, NULL, &si, &processInfo);

if (!createRet) {
    throw std::runtime_error("Failed to create the service in user session");
}

m_serviceGroupId = processInfo.dwProcessId;

DestroyEnvironmentBlock(environment);
CloseHandle(userToken);

代码 sn-p 2

if (m_serviceGroupId == 0) {
    return;
}

HANDLE process;
const UINT kExitCode = 0;
const UINT kShutdownTimeout = 3000;
if (findProcessInSession(kServiceProcess, &process, getActiveSession())) {
    BOOL r = GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, m_serviceGroupId);
    // this won't success if I don't specifically call AllocConsole() in the constructor
    if (!r) {
        writeEventErrorLog(GetLastErrorAsString().c_str());
    }

    DWORD exitCode = WaitForSingleObject(process, kShutdownTimeout);
    if (exitCode != WAIT_OBJECT_0) {
        // always fall into forceful shutdown
        writeEventErrorLog("Forcefully shutdown synergy service");
        // GetLastErrorAsString returns empty string
        writeEventErrorLog(GetLastErrorAsString().c_str());
        if (!TerminateProcess(process, kExitCode)) {
            writeEventErrorLog("Failed to shutdown synergy service");
        }
    }
}

m_serviceGroupId = 0;

【问题讨论】:

  • GenerateConsoleCtrlEvent 远程调用csrss。但是您的服务附加到另一个csrss 进程 - 每个会话都有它自己的csrss。所以GenerateConsoleCtrlEvent不能用于在另一个会话中发送控制事件
  • @RbMm 谢谢。有什么方法可以管理子进程吗? RPC 调用?
  • 您需要做的就是从服务向孩子发送一次性消息,因此命名事件对象可能是最合适的选择。在更复杂的情况下,您可能会使用命名管道。还有其他选择。
  • GenerateConsoleCtrlEvent 请求控制台(即 conhost.exe)请求会话的 Windows 服务器(即 csrss.exe)向其他进程发送控制事件(Ctrl+C 或 Ctrl+break)附加到同一个控制台,按进程组过滤,它通过在每个目标进程中创建一个从kernelbase!CtrlRoutine 开始的线程来实现。附加到控制台仅限于与控制台在同一会话中的进程。此外,服务甚至不使用附加的控制台运行,即服务控制器使用标志 DETACHED_PROCESS 创建服务进程。

标签: windows service process signals


【解决方案1】:

您的方法不起作用,因为服务进程和子进程不在同一个会话中,因此请考虑 Windows 事件。看看CreateEventSetEvent

当你的服务进程启动时,调用CreateEvent(),例如:

HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\myevent");

当你的子进程启动时,调用 CreateEvent(),并启动一个线程来捕获信号:

//The lpName should be identical with that in service process
HANDLE hEvent = CreateEvent(NULL, FALSE, FALSE, "Global\\myevent");

std::thread mythread([hEvent] {
    WaitForSingleObject(hEvent, INFINITE);
    //then exit your child process gracefully
});

当您打算停止子进程时,只需在您的服务进程中调用 SetEvent():

SetEvent(hEvent);

将 bManualReset 设置为 TRUE,然后您的服务可以同时向不同的子进程发送信号。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 1970-01-01
    • 2019-08-16
    • 1970-01-01
    • 2020-11-22
    • 2014-02-20
    • 1970-01-01
    相关资源
    最近更新 更多