Windows关闭然后启动后c++服务不会启动

标签 c++ windows winapi service

我想在 C++ 中创建一个 Windows 服务,以便在用户每次登录时以管理员身份启动我的程序而不弹出 UAC 窗口 因为这是我第一次这样做,所以我使用了这里的项目:https://code.msdn.microsoft.com/windowsapps/CppWindowsService-cacf4948/view/SourceCode

我将 CppWindowsService.cpp 中的第 74 行编辑为:

InstallService(
        SERVICE_NAME,               // Name of service
        SERVICE_DISPLAY_NAME,       // Name to display
        SERVICE_AUTO_START,         // Service start type
        SERVICE_DEPENDENCIES,       // Dependencies
        0,            // Service running account
        SERVICE_PASSWORD            // Password of the account
        );

并在 SampleService.cpp 第 101 行的工作线程中添加了一些代码,变成这样:

 void CSampleService::ServiceWorkerThread(void)
{
// Periodically check if the service is stopping.
PSID gpSidMIL_High;
ConvertStringSidToSidW(L"S-1-16-12288", &gpSidMIL_High);
DWORD userSessionID = WTSGetActiveConsoleSessionId();
HANDLE hToken, hToken2;


if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) WriteEventLogEntry(L"OpenProcessToken failed error", EVENTLOG_ERROR_TYPE);
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2)) WriteEventLogEntry(L"DuplicateTokenEx error", EVENTLOG_ERROR_TYPE);

if (!SetTokenInformation(hToken2, TokenSessionId, &userSessionID, sizeof(userSessionID))) WriteEventLogEntry(L"SetTokenInformation 1 error", EVENTLOG_ERROR_TYPE);

DWORD dwUIAccess = 1;
if (!SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess))) WriteEventLogEntry(L"SetTokenInformation 2 error", EVENTLOG_ERROR_TYPE);

//Set "high" mandatory integrity level
TOKEN_MANDATORY_LABEL tml = { 0 };
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = gpSidMIL_High;

if (!SetTokenInformation(hToken2, TokenIntegrityLevel, &tml, sizeof(TOKEN_MANDATORY_LABEL) + ::GetSidLengthRequired(1))) WriteEventLogEntry(L"SetTokenInformation 3 error", EVENTLOG_ERROR_TYPE);
LPVOID pEnv = 0;
if (!CreateEnvironmentBlock(&pEnv, hToken2, FALSE)) WriteEventLogEntry(L"CreateEnvironmentBlock error", EVENTLOG_ERROR_TYPE);

if (!ImpersonateLoggedOnUser(hToken2)) WriteEventLogEntry(L"ImpersonateLoggedOnUser error", EVENTLOG_ERROR_TYPE);

while (!m_fStopping)
{
     STARTUPINFO stinfo = { 0 };
   PROCESS_INFORMATION pinfo = { 0 };
   stinfo.cb = sizeof(stinfo);
   stinfo.lpDesktop = L"winsta0\\default";
   if (!CreateProcessAsUserW(hToken2, L"path to exe that shows a message box", 0, 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT|CREATE_BREAKAWAY_FROM_JOB, pEnv, L"cwd of the exe file", &stinfo, &pinfo))
   {
// after debugging I found that the error is coming from here 
       std::wstring error = L"CreateProcessAsUserW failed with error : ";
       error += std::to_wstring(GetLastError());
       WriteEventLogEntry(wcsdup(error.c_str()), EVENTLOG_ERROR_TYPE);
       Sleep(10000);
   }
   while (!m_fStopping && pinfo.hProcess)
   {
       if(WaitForSingleObject(pinfo.hProcess, 1000) != WAIT_TIMEOUT) break;
   }
   // ::Sleep(2000);  // Simulate some lengthy operations.
}

问题是此服务在重新启动 Windows 或通过 sc.exe 或服务控制管理器手动启动它后工作得很好,但在从之前的关机启动后就不行了 当我关闭然后启动计算机时,我可以在任务管理器中看到正在运行的服务的 exe,所以我知道该服务正在运行并且有一个错误来自一个函数,我使用了 Windows 事件并记录了错误,我终于找到了该错误来自 CreateProcessAsUser,它返回错误 5(访问被拒绝) 不知道是哪里出了问题,重启后或者手动启动服务都很好

最佳答案

ServiceMain 不会在快速启动期间被调用。使用快速启动时, session 0(包括内核、驱动程序、所有服务)未终止。当您关闭并继续运行时,它会休眠 session 0,就像使用用户注销挂断 VirtualMachine 一样。因此您的服务会保持状态而不是重新开始。

一个简单的方法来处理这个: 处理SERVICE_ACCEPT_SESSIONCHANGE控制代码。

这是一个例子。

SERVICE_STATUS gSvcStatus;
DWORD WINAPI HandleCtrlEx(
    DWORD dwControl,
    DWORD dwEventType,
    LPVOID lpEventData,
    LPVOID lpContext
)
{
    // Handle the requested control code. 
    switch (dwControl)
    {
        case SERVICE_CONTROL_SHUTDOWN:
        case SERVICE_CONTROL_STOP:
            ReportSvcStatus(SERVICE_STOP_PENDING, NO_ERROR, 0);

            // Signal the service to stop.

            SetEvent(ghSvcStopEvent);
            ReportSvcStatus(gSvcStatus.dwCurrentState, NO_ERROR, 0);

            break;

        case SERVICE_CONTROL_SESSIONCHANGE:
            if (dwEventType == WTS_SESSION_LOGON) {
                DWORD *data = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DWORD));
                *data = ((PWTSSESSION_NOTIFICATION)lpEventData)->dwSessionId;
                HANDLE hThread = CreateThread(NULL, 0, RunProgram, data, 0, NULL);// remember to free memory in RunProgram
                if (!hThread) {
                    PrintLogA(LOG"can't create thread,error:%u", GetLastError());
                }
                else {
                    CloseHandle(hThread);
                }
            }
            break;

        default:
            break;
    }
    return NO_ERROR;
}

VOID ReportSvcStatus(DWORD dwCurrentState,
    DWORD dwWin32ExitCode,
    DWORD dwWaitHint)
{
    static DWORD dwCheckPoint = 1;

    // Fill in the SERVICE_STATUS structure.

    gSvcStatus.dwCurrentState = dwCurrentState;
    gSvcStatus.dwWin32ExitCode = dwWin32ExitCode;
    gSvcStatus.dwWaitHint = dwWaitHint;

    if (dwCurrentState == SERVICE_START_PENDING)
        gSvcStatus.dwControlsAccepted = 0;
    else {
        gSvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
        gSvcStatus.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;// <--- remember add this.
    }

    if ((dwCurrentState == SERVICE_RUNNING) ||
        (dwCurrentState == SERVICE_STOPPED))
        gSvcStatus.dwCheckPoint = 0;
    else gSvcStatus.dwCheckPoint = dwCheckPoint++;

    // Report the status of the service to the SCM.
    SetServiceStatus(gSvcStatusHandle, &gSvcStatus);
}

VOID SvcInit(DWORD dwArgc, LPTSTR *lpszArgv)
{
    ghSvcStopEvent = CreateEvent(
        NULL,    // default security attributes
        TRUE,    // manual reset event
        FALSE,   // not signaled
        NULL);   // no name

    if (ghSvcStopEvent == NULL)
    {
        ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
        return;
    }

    // Report running status when initialization is complete.
    ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0);

    RunProgram();

    // Check whether to stop the service.
    WaitForSingleObject(ghSvcStopEvent, INFINITE);
    ReportSvcStatus(SERVICE_STOPPED, NO_ERROR, 0);
    return;
}

VOID WINAPI SvcMain(DWORD dwArgc, LPTSTR *lpszArgv){
    // Register the handler function for the service
    gSvcStatusHandle = RegisterServiceCtrlHandlerEx(
        SVCNAME,
        HandleCtrlEx,
        NULL);
    if (!gSvcStatusHandle)
    {
        SvcReportEventA(LOG"RegisterServiceCtrlHandler failed");
        return;
    }

    // These SERVICE_STATUS members remain as set here

    gSvcStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
    gSvcStatus.dwServiceSpecificExitCode = 0;
    // Report initial status to the SCM

    ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 3000);

    // Perform service-specific initialization and work.

    SvcInit(dwArgc, lpszArgv);
}

关于Windows关闭然后启动后c++服务不会启动,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49958801/

相关文章:

c++ - 引用类型返回函数 : how to return (optional) object

将 C 代码编译成与 WinXP 和 Win7 兼容的单个可执行文件

windows - 如何让我的 ActiveX 控件显示在 Visual Studio(和其他)的工具箱中

C# 如何为 Windows 服务创建安装项目以自动安装它?

c++ - 键盘钩子(Hook)仅在调用过程中起作用

c++ - 仅使用 Windows SDK 7.1 编译 ATL 项目

c++ - Windows API全局钩子(Hook)的无限范围

c++ - 浮点值可以加到零之和吗?

c++ - 从 C++ 中的链表中删除节点

c++ - 返回 const 引用和右值引用之间的区别