在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;
}