首页 > 解决方案 > 无法使 Windows 服务在 Windows Server 2012 上正确调用子进程

问题描述

我编写的以下函数应该由 Windows 服务调用以启动子进程(作为用户),因此子进程可以与用户交互,即具有 UI。它在 Windows 桌面上完美运行,但在 Windows Server 2012 上无法正常运行。UI 未显示(甚至不是控制台窗口),并且我们为卸载服务而注册的热键也无法正常工作。

以下代码:

void WINAPI Run(DWORD dwTargetSessionId, int desktop, LPTSTR lpszCmdLine)
{
    wprintf(_T("Run client start"));

    if (hPrevAppProcess != NULL)
    {
        TerminateProcess(hPrevAppProcess, 0);
        WaitForSingleObject(hPrevAppProcess, INFINITE);
    }


    HANDLE hToken = 0;
    WTS_SESSION_INFO *si;
    DWORD cnt = 0;
    WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &si, &cnt);
    for (int i = 0; i < (int)cnt; i++)
    {
        if (si[i].SessionId == 0)continue;
        wprintf(_T("Trying session id %i (%s) user admin token"), si[i].SessionId, si[i].pWinStationName);
        HANDLE userToken;
        if (WTSQueryUserToken(si[i].SessionId, &userToken))
        {
            wprintf(_T("WTSQueryUserToken succeced"));
            TOKEN_LINKED_TOKEN admin;
            DWORD len;
            if (GetTokenInformation(userToken, TokenLinkedToken, &admin, sizeof(TOKEN_LINKED_TOKEN), &len))
            {
                wprintf(_T("Success using user admin token"));
                hToken = admin.LinkedToken;
                break;
            }
            else
                wprintf(L"GetTokenInformation() failed");

            CloseHandle(userToken);
        }
        else
        {
            DWORD error = GetLastError();
            if (error)
            {
                LPVOID lpMsgBuf;
                DWORD bufLen = FormatMessage(
                    FORMAT_MESSAGE_ALLOCATE_BUFFER |
                    FORMAT_MESSAGE_FROM_SYSTEM |
                    FORMAT_MESSAGE_IGNORE_INSERTS,
                    NULL,
                    error,
                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                    (LPTSTR)&lpMsgBuf,
                    0, NULL);
                utils::WriteLogFile(L"Error '%s'\n", lpMsgBuf);
            }
        }

    }
    if (hToken == 0)
    {
        HANDLE systemToken;
        OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &systemToken);
        DuplicateTokenEx(systemToken, TOKEN_ALL_ACCESS, NULL, SecurityImpersonation, TokenPrimary, &hToken);
        CloseHandle(systemToken);
        int i;
        for (i = 0; i < (int)cnt; i++)
        {
            if (si[i].SessionId == 0)continue;
            if (SetTokenInformation(hToken, TokenSessionId, &si[i].SessionId, sizeof(DWORD)))
            {
                wprintf(_T("Success using system token with set user session id %i"), si[i].SessionId);
                break;
            }
        }
        if (i == cnt)
            wprintf(_T("No success to get user admin token nor system token with set user session id"));
    }
    WTSFreeMemory(si);


    STARTUPINFO startupInfo = {};
    startupInfo.cb = sizeof(STARTUPINFO);

    startupInfo.lpDesktop = _T("winsta0\\default");

    LPVOID pEnv = NULL;
    CreateEnvironmentBlock(&pEnv, hToken, TRUE);

    PROCESS_INFORMATION processInfo = {};
    PROCESS_INFORMATION processInfo32 = {};

    TCHAR szCurModule[MAX_PATH] = { 0 };
    GetModuleFileName(NULL, szCurModule, MAX_PATH);

    BOOL bRes = FALSE;

    std::wstring commandLine;
    commandLine.reserve(1024);

    commandLine += L"\"";
    commandLine += szCurModule;
    commandLine += L"\" \"";
    commandLine += SERVICE_COMMAND_LUNCHER;
    commandLine += L"\"";

    wprintf(_T("launch SG_WinService with CreateProcessAsUser ...  %s"), commandLine.c_str());

    bRes = CreateProcessAsUserW(hToken, NULL, &commandLine[0], NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS |
        CREATE_UNICODE_ENVIRONMENT | CREATE_NEW_CONSOLE | CREATE_DEFAULT_ERROR_MODE, pEnv,
        NULL, &startupInfo, &processInfo);

    if (bRes == FALSE)
    {
        DWORD   dwLastError = ::GetLastError();
        TCHAR   lpBuffer[256] = _T("?");
        if (dwLastError != 0)    // Don't want to see a "operation done successfully" error ;-)
            ::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,                 // It´s a system error
                NULL,                                      // No string to be formatted needed
                dwLastError,                               // Hey Windows: Please explain this error!
                MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),  // Do it in the standard language
                lpBuffer,              // Put the message here
                255,                     // Number of bytes to store the message
                NULL);
        (_T("CreateProcessAsUser(SG_WinService) failed - Error : %s (%i)"), lpBuffer, dwLastError);
    }
    else
    {
        wprintf(_T("CreateProcessAsUser(SG_WinService) success. New process ID: %i"), processInfo.dwProcessId);
    }
}

返回以下输出:

05.06.2019 04:25 5856:尝试会话 id 1(控制台)用户管理令牌

05.06.2019 04:25 5856:尝试会话 id 2 (RDP-Tcp#27) 用户管理令牌

05.06.2019 04:25 5856:尝试会话 id 65536 (RDP-Tcp) 用户管理令牌

05.06.2019 04:25 5856:成功使用系统令牌和设置的用户会话 id 1

05.06.2019 04:25 5856:使用 CreateProcessAsUser 启动 SG_WinService ...“C:\myservice\SG_WinService.exe”“ServiceIsLuncher”

05.06.2019 04:25 5856: CreateProcessAsUser(SG_WinService) 成功。新进程 ID:5580

更新:添加对 GetLastError() 的调用后,我得到:

09.06.2019 04:55 6008:尝试会话 id 1 用户管理令牌

09.06.2019 04:55 6008:错误“尝试引用不存在的令牌。

09.06.2019 04:55 6008:尝试会话 id 3 用户管理令牌

09.06.2019 04:55 6008:尝试会话 id 65536 用户管理令牌

09.06.2019 04:55 6008:错误“系统找不到指定的文件。

09.06.2019 04:55 6008:成功使用系统令牌和设置的用户会话 id 1

09.06.2019 04:55 6008:使用 CreateProcessAsUser 启动 SG_WinService ...

"C:\Users\Administrator\Desktop\src\SpyfiWebApp\SG_WinService.exe" "ServiceIsLuncher"

09.06.2019 04:55 6008: CreateProcessAsUser(SG_WinService) 成功。新进程 ID:7536

因此,它不是在 Session 1 上运行子进程,而是作为“SYSTEM”运行。

标签: windows-server-2012service

解决方案


推荐阅读