在服务中以当前用户身份启动一个程序

在CodeProject上看到一个Demo, 在服务中以当前用户身份启动一个程序.

跟帖的人指出了一些bug, 我整理了一下, 将跟帖人指出的bug在工程中修正.

他提供的类, 也有一个小bug, 没有被跟帖的人指出, 被我发现并修正.

这个Demo整理后, 被我用在项目中, 用起来效果还不错.

以当前登录用户的身份运行一个程序的好处: 

* 可以避免权限问题. e.g. 文件建立后, 当前用户打不开.

* 有些程序或部件运行, 是需要Windows窗口消息的, 不能直接在服务中运行.


工程下载点: src_bk_2015_0722_1601_prj_run_cur_user_prog_on_service.zip

编译环境: vs2010 vc++


备注 :  

 /// @todo ls 服务启动停止时, 检测服务是否已经在运行或停止的处理要加上, 提高效率.

/// 如果硬生生的启动停止服务时, 还要启动停止桌面上的程序, 在没有检测服务状态时, 要花费的时间多些.


效果图:



工程预览:

// lsServiceForTest.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include <process.h>
#include "lsServiceForTest.h"
#include "ProcessStarter.h"

#define SERVICE_VER_W L"1, 0, 0, 1"
#define PROJECT_MODIFY_TIME L"2015-0722-1426"

VOID WINAPI ServiceMain(DWORD dwArgc, LPWSTR *lpszArgv);
SERVICE_TABLE_ENTRYW lpServiceStartTable[] = 
{
    {SERVICE_NAME_W, ServiceMain},
    {NULL, NULL}
};

SERVICE_STATUS_HANDLE g_hServiceCtrlHandler = NULL; 
SERVICE_STATUS g_ServiceStatus;
std::wstring g_strPathNameMe = L"";
std::wstring g_strCmdLine = L"";
ns_base::CThreadManager g_ThreadManager;

VOID ServiceMainProc();
static UINT WINAPI ThreadProcWorker(void* pParam);
BOOL ThreadProcStart_Worker();
BOOL ThreadProcStop_Worker();

BOOL GetObjProgInfo(DWORD dwSessionId, OUT std::wstring& strObjPathName, OUT std::wstring& strCmdLine);

VOID ExecuteAsService();
VOID WINAPI ServiceHandler(DWORD fdwControl);

int APIENTRY _tWinMain(HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPTSTR    lpCmdLine,
    int       nCmdShow)
{
    std::wstring strLogFilePathName = L"";

    ns_base::GetFilePathName_Me(g_strPathNameMe);
    g_strCmdLine = (NULL != lpCmdLine) ? lpCmdLine : L"";

    strLogFilePathName = ns_business::GetLogPathName_lsServiceForTest().c_str();
    SetLogFilePathName(strLogFilePathName.c_str());

    ServiceMainProc();
    return 0;
}

VOID ServiceMainProc()
{
    WriteLogEx(L">> ServiceMainProc() [%s][%s][%s]", SERVICE_NAME_W, SERVICE_VER_W, PROJECT_MODIFY_TIME);

    if (ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-i")
        || ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-I"))
    {
        ns_base::ServiceInstall(g_strPathNameMe.c_str(), SERVICE_NAME_W);
    }
    else if (ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-s")
        || ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-S"))
    {
        ns_base::ServiceStart(SERVICE_NAME_W);
    }
    else if (ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-k")
        || ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-K"))
    {
        ns_base::ServiceStop(SERVICE_NAME_W);
    }
    else if (ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-u")
        || ns_base::StringCompare_equ(g_strCmdLine.c_str(), L"-U"))
    {
        ns_base::ServiceUnInstall(SERVICE_NAME_W);
    }
    else
        ExecuteAsService();

    WriteLogEx(L"<< ServiceMainProc() [%s][%s][%s]", SERVICE_NAME_W, SERVICE_VER_W, PROJECT_MODIFY_TIME);
}

VOID ExecuteAsService()
{
    WriteLogEx(L">> ExecuteAsService");

    if(!ThreadProcStart_Worker())
    {
        WriteLogEx(L"ThreadProcStart_Worker failed[%d]", GetLastError());
    }

    if(!StartServiceCtrlDispatcherW(lpServiceStartTable))
    {
        WriteLogEx(L"StartServiceCtrlDispatcher failed[%d]", GetLastError());
    }

    WriteLogEx(L"<< ExecuteAsService");
}

VOID WINAPI ServiceMain(DWORD dwArgc, LPWSTR* lpszArgv)
{
    WriteLogEx(L">> ServiceMain(%d, lpszArgv)", dwArgc);
    do 
    {
        g_ServiceStatus.dwServiceType = SERVICE_WIN32; 
        g_ServiceStatus.dwCurrentState = SERVICE_START_PENDING; 
        g_ServiceStatus.dwControlsAccepted = 
            SERVICE_ACCEPT_STOP
            | SERVICE_ACCEPT_PAUSE_CONTINUE
            | SERVICE_ACCEPT_SHUTDOWN
            | SERVICE_ACCEPT_PARAMCHANGE
            | SERVICE_ACCEPT_NETBINDCHANGE
            | SERVICE_ACCEPT_HARDWAREPROFILECHANGE
            | SERVICE_ACCEPT_POWEREVENT
            | SERVICE_ACCEPT_SESSIONCHANGE
            | SERVICE_ACCEPT_PRESHUTDOWN
            | SERVICE_ACCEPT_TIMECHANGE
            | SERVICE_ACCEPT_TRIGGEREVENT;

        g_ServiceStatus.dwWin32ExitCode = 0; 
        g_ServiceStatus.dwServiceSpecificExitCode = 0;
        g_ServiceStatus.dwCheckPoint = 0; 
        g_ServiceStatus.dwWaitHint = 0; 

        g_hServiceCtrlHandler = RegisterServiceCtrlHandlerW(SERVICE_NAME_W, ServiceHandler); 
        if (NULL == g_hServiceCtrlHandler) 
        {
            ns_base::NotifyFailed_RegisterServiceCtrlHandler(GetLastError(), SERVICE_NAME_W);
            break;
        } 

        // Initialization complete - report running status 
        g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
        g_ServiceStatus.dwCheckPoint = 0; 
        g_ServiceStatus.dwWaitHint = 0;  
        if (!SetServiceStatus(g_hServiceCtrlHandler, &g_ServiceStatus)) 
        { 
            ns_base::NotifyFailed_SetServiceStatus(GetLastError(), SERVICE_NAME_W);
        } 
    } while (0);

    WriteLogEx(L"<< ServiceMain(%d, lpszArgv)", dwArgc);
}

VOID WINAPI ServiceHandler(DWORD fdwControl)
{
    int iIndex = 0;

    WriteLogEx(L">> ServiceHandler(%d)", fdwControl);
    switch(fdwControl) 
    {
    case SERVICE_CONTROL_STOP:
    case SERVICE_CONTROL_SHUTDOWN:
        g_ThreadManager.StopThread(TRUE, L"g_ThreadManager");
        g_ServiceStatus.dwWin32ExitCode = 0; 
        g_ServiceStatus.dwCurrentState  = SERVICE_STOPPED; 
        g_ServiceStatus.dwCheckPoint    = 0; 
        g_ServiceStatus.dwWaitHint      = 0;

        // terminate all processes started by this service before shutdown
        ns_business::StopAndKill_dlgNotify();
        break; 
    case SERVICE_CONTROL_PAUSE:
        g_ServiceStatus.dwCurrentState = SERVICE_PAUSED; 
        break;
    case SERVICE_CONTROL_CONTINUE:
        g_ServiceStatus.dwCurrentState = SERVICE_RUNNING; 
        break;
    default:
        WriteLogEx(L"Unrecognized opcode %d\n", fdwControl);
    };

    if (!SetServiceStatus(g_hServiceCtrlHandler,  &g_ServiceStatus)) 
    { 
        ns_base::NotifyFailed_SetServiceStatus(GetLastError(), SERVICE_NAME_W);
    }

    WriteLogEx(L"<< ServiceHandler(%d)", fdwControl);
}

static UINT WINAPI ThreadProcWorker(void* pParam)
{
    BOOL bLog = TRUE;
    DWORD dwSessionIdPrev = -1;
    DWORD dwSessionId = -1;
    size_t nSleepTotal = 0;
    UINT uRc = S_FALSE;

    std::wstring strObjPathName = L"";
    std::wstring strCmdLine = L"";

    ns_base::TAG_THREAD_MANAGER_PARAM ThreadManagerParam;
    ns_base::TAG_THREAD_MANAGER_PARAM* pThreadManagerParam = NULL;
    CProcessStarter ProcessStarter;

    WriteLogEx(L">> lsServiceForTest ThreadProcWorker");
    do 
    {
        if (NULL == pParam)
            break;

        pThreadManagerParam = (ns_base::TAG_THREAD_MANAGER_PARAM*)pParam;
        ThreadManagerParam.copy((ns_base::TAG_THREAD_MANAGER_PARAM*)pParam);
        SAFE_DELETE(pThreadManagerParam);
        if (NULL == ThreadManagerParam.pThreadManager)
            break;

        while(!ThreadManagerParam.pThreadManager->IsNeedQuitThread())
        {
            if (!ns_base::SleepContinueEx(2000, 100, nSleepTotal))
                continue;

            /// 保证 只有在SessionId变化的时候, 才打印 FindActiveSessionId 的日志
            if (!ProcessStarter.FindActiveSessionId(dwSessionId, bLog)
                || (dwSessionIdPrev == dwSessionId))
            {
                if (bLog)
                    bLog = FALSE;

                continue;
            }

            if (!bLog)
                bLog = TRUE;

            /// 用户每次切换一次桌面, 我们就启动一次子程序
            do 
            {
                dwSessionIdPrev = dwSessionId;
                if (GetObjProgInfo(dwSessionId, strObjPathName, strCmdLine))
                {
                    if (!ns_base::IsFileExist(strObjPathName.c_str()))
                    {
                        WriteLogEx(L"[error] !ns_base::IsFileExist(%s)", strObjPathName.c_str());
                        break;
                    }

                    /// 确保在多个SessionId的环境下, 也只有当前SessionId上运行唯一一个dlgNotify
                    ns_business::StopAndKill_dlgNotify();

                    ProcessStarter.Run(
                        strObjPathName.c_str(),
                        strCmdLine.c_str());
                }
            } while (0);

            continue;
        }

        uRc = S_OK;
    } while (0);
    WriteLogEx(L"<< FzAppService ThreadProcWorker");

    return uRc;
}

BOOL ThreadProcStart_Worker()
{
    BOOL bRc = FALSE;
    ns_base::TAG_THREAD_MANAGER_PARAM* pThreadManagerParam = NULL;

    if (!g_ThreadManager.IsNeedQuitThread()
        && !g_ThreadManager.IsThreadRunning())
    {
        do 
        {
            pThreadManagerParam = new ns_base::TAG_THREAD_MANAGER_PARAM;
            if (NULL == pThreadManagerParam)
                break;

            pThreadManagerParam->pThreadManager = &g_ThreadManager;
            g_ThreadManager.SetThreadHandle(
                (HANDLE)_beginthreadex(
                NULL, 
                0, 
                &ThreadProcWorker, 
                (void*)pThreadManagerParam, 
                0, 
                NULL));

            bRc = TRUE;
        } while (0);
    }

    return bRc;
}

BOOL ThreadProcStop_Worker()
{
    g_ThreadManager.StopThread(TRUE, L"g_ThreadManager");
    return TRUE;
}

BOOL GetObjProgInfo(DWORD dwSessionId, OUT std::wstring& strObjPathName, OUT std::wstring& strCmdLine)
{
    ns_base::GetPathName_Me(strObjPathName);
    strObjPathName += FILE_NAME_ObjProgInfo;

    strCmdLine = ns_base::StringFormatV(L"sessionId-%d", dwSessionId);

    return TRUE;
}

CProcessStarter 实现, 负责以当前用户身份启动一个程序.

#ifndef _PROCESS_STARTER_H_
#define _PROCESS_STARTER_H_

#include "stdafx.h"

class CProcessStarter
{
public:
    CProcessStarter();

    /// 如果没有找到"已经激活的SessionId", 说明还没有进入桌面
    BOOL FindActiveSessionId(OUT DWORD& dwSessionId, BOOL bNeedLog);
    BOOL Run(LPCWSTR pcProcessPathName, LPCWSTR pcCmdLine);
    
private:
    HANDLE GetCurrentUserToken();

private:
    std::wstring m_strProcessPathName;
    std::wstring m_strCmdLine;
};

#endif //_PROCESS_STARTER_H_

#include "stdafx.h"
#include "ProcessStarter.h"

#include <userenv.h>
#pragma comment(lib, "Userenv.lib")

#include <wtsapi32.h>
#pragma comment(lib, "Wtsapi32.lib")

CProcessStarter::CProcessStarter()
    : m_strProcessPathName(L""), 
    m_strCmdLine(L"")
{
}

BOOL CProcessStarter::FindActiveSessionId(OUT DWORD& dwSessionId, BOOL bNeedLog)
{
    BOOL bFindActiveSession = FALSE;
    DWORD dwIndex = 0;

    PWTS_SESSION_INFO pWtsSessionInfo = NULL;
    DWORD dwCntWtsSessionInfo = 0;

    if (bNeedLog)
        WriteLogEx(L">> CProcessStarter::FindActiveSessionId()");
    do 
    {
        dwSessionId = (DWORD)(-1);

        if ((!WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, &pWtsSessionInfo, &dwCntWtsSessionInfo))
            || (NULL == pWtsSessionInfo))
        {
            if (bNeedLog)
                WriteLogEx(L"break 0 CProcessStarter::FindActiveSessionId()");

            break;
        }

        for (dwIndex = 0; dwIndex < dwCntWtsSessionInfo; dwIndex++)
        {
            if (WTSActive == pWtsSessionInfo[dwIndex].State)
            {
                dwSessionId = pWtsSessionInfo[dwIndex].SessionId;
                bFindActiveSession = TRUE;
                break;
            }
        }

        WTSFreeMemory(pWtsSessionInfo);

        if (!bFindActiveSession)
        {
            if (bNeedLog)
                WriteLogEx(L"break 1 CProcessStarter::FindActiveSessionId()");

            break;
        }
    } while (0);

    if (bNeedLog)
    {
        WriteLogEx(L"<< CProcessStarter::FindActiveSessionId(), bFindActiveSession = [%s], dwSessionId = %d", 
            bFindActiveSession ? L"TRUE" : L"FALSE", 
            dwSessionId);
    }

    return bFindActiveSession;
}

HANDLE CProcessStarter::GetCurrentUserToken()
{
    DWORD dwSessionId = 0;
    HANDLE hCurrentToken = NULL;
    HANDLE hPrimaryToken = NULL;

    WriteLogEx(L">> CProcessStarter::GetCurrentUserToken()");
    do 
    {
        if (!FindActiveSessionId(dwSessionId, TRUE))
        {
            WriteLogEx(L"break 0 CProcessStarter::GetCurrentUserToken()");
            break;
        }

        if (!WTSQueryUserToken(dwSessionId, &hCurrentToken)
            || (NULL == hCurrentToken))
        {
            WriteLogEx(L"break 2 CProcessStarter::GetCurrentUserToken()");
            break;
        }

        if (!DuplicateTokenEx(hCurrentToken, TOKEN_ASSIGN_PRIMARY | TOKEN_ALL_ACCESS, 0, SecurityImpersonation, TokenPrimary, &hPrimaryToken))
        {
            WriteLogEx(L"break 3 CProcessStarter::GetCurrentUserToken()");
            break;
        }

    } while (0);

    WriteLogEx(L"<< CProcessStarter::GetCurrentUserToken(), hCurrentToken = 0x%p, hPrimaryToken = 0x%p", 
        hCurrentToken,
        hPrimaryToken);

    SAFE_CLOSE_HANDLE(hCurrentToken);
    return hPrimaryToken;
}

BOOL CProcessStarter::Run(LPCWSTR pcProcessPathName, LPCWSTR pcCmdLine)
{
    BOOL bRc = FALSE;
    BOOL bTmp = FALSE;
    HANDLE hPrimaryToken = NULL;
    STARTUPINFOA StartupInfo = {0};
    PROCESS_INFORMATION processInfo = {0};
    std::wstring command = L"";
    LPVOID lpEnvironment = NULL;

    WriteLogEx(L">> CProcessStarter::Run");

    do 
    {
        if ((NULL == pcProcessPathName) || (!ns_base::IsFileExist(pcProcessPathName)))
        {
            WriteLogEx(L"break 0 CProcessStarter::Run");
            break;
        }

        this->m_strProcessPathName = pcProcessPathName;
        this->m_strCmdLine = (NULL != pcCmdLine) ? pcCmdLine : L"";

        hPrimaryToken = GetCurrentUserToken();
        if (NULL == hPrimaryToken)
        {
            WriteLogEx(L"break 1 CProcessStarter::Run");
            break;
        }

        StartupInfo.cb = sizeof(STARTUPINFO);
        command = L"\"";
        command += m_strProcessPathName.c_str();
        command += L"\"";
        if (m_strCmdLine.length() != 0)
        {
            command += L" ";
            command += m_strCmdLine.c_str();
        }

        WriteLogEx(L"command = [%s]", command.c_str());

        if (!CreateEnvironmentBlock(&lpEnvironment, hPrimaryToken, TRUE))
        {
            WriteLogEx(L"!CreateEnvironmentBlock by hPrimaryToken");
        }

        bTmp = CreateProcessAsUserA(
            hPrimaryToken, 
            0, 
            (LPSTR)ns_base::W2Aex(command.c_str()).c_str(), 
            NULL, 
            NULL, 
            FALSE, 
            NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT,
            lpEnvironment, // __in_opt    LPVOID lpEnvironment,
            0, 
            &StartupInfo, 
            &processInfo);

        if (NULL != lpEnvironment)
            DestroyEnvironmentBlock(lpEnvironment);

        WriteLogEx(L"CreateProcessAsUserA = %s", bTmp ? L"TRUE" : L"FALSE");
        if (!bTmp)
            break;

        bRc = TRUE;
    } while (0);

    SAFE_CLOSE_HANDLE(hPrimaryToken);
    WriteLogEx(L"<< CProcessStarter::Run, bRc = [%s]", bRc ? L"TRUE" : L"FALSE");

    return bRc;
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值