[英]c++ service doesn't start after windows shutdown and then boot
我想用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 。 使用快速啟動時,會話0(包括內核,驅動程序,所有服務)未終止。 當您關閉並繼續運行時,它會休眠會話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);
}
這些是要初始化的類的成員:
PSID gpSidMIL_High = 0;
DWORD userSessionID = 0;
HANDLE hToken = 0, hToken2 = 0;
DWORD dwUIAccess = 1;
TOKEN_MANDATORY_LABEL tml = { 0 };
LPVOID pEnv = 0;
STARTUPINFO stinfo = { 0 };
PROCESS_INFORMATION pinfo = { 0 };
在受保護的方法中,我做了以下方法:
void intialize();
這是功能:
void CSampleService::intialize() {
// the members may were used before , without assigning them to zero again all the functions below will fail , I don't know why ?
gpSidMIL_High = 0;
userSessionID = 0;
hToken = 0, hToken2 = 0;
dwUIAccess = 1;
tml = { 0 };
pEnv = 0;
stinfo = { 0 };
pinfo = { 0 };
WriteEventLogEntry(L"initializing", EVENTLOG_INFORMATION_TYPE);
ConvertStringSidToSidW(L"S-1-16-12288", &gpSidMIL_High);
userSessionID = WTSGetActiveConsoleSessionId();
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)) WriteErrorLogEntry(L"OpenProcessToken");
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hToken2)) WriteErrorLogEntry(L"DuplicateTokenEx error");
if (!SetTokenInformation(hToken2, TokenSessionId, &userSessionID, sizeof(userSessionID))) WriteErrorLogEntry(L"SetTokenInformation 1 error");
if (!SetTokenInformation(hToken2, TokenUIAccess, &dwUIAccess, sizeof(dwUIAccess))) WriteErrorLogEntry(L"SetTokenInformation 2");
//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))) WriteErrorLogEntry(L"SetTokenInformation 3");
LPVOID pEnv = 0;
if (!CreateEnvironmentBlock(&pEnv, hToken2, FALSE)) WriteErrorLogEntry(L"CreateEnvironmentBlock");
}
終於在工作線程中:
void CSampleService::ServiceWorkerThread(void)
{
WriteEventLogEntry(L"the thread of the service started", EVENTLOG_INFORMATION_TYPE);
intialize();
while (!m_fStopping)
{
stinfo.cb = sizeof(stinfo);
stinfo.lpDesktop = L"winsta0\\default";
// the service starts from here after fast boot
if (!CreateProcessAsUserW(hToken2, L"C:\\Users\\user\\Desktop\\ConEmuTrap\\test.exe", 0, 0, 0, FALSE, CREATE_UNICODE_ENVIRONMENT | CREATE_BREAKAWAY_FROM_JOB, pEnv, L"C:\\Users\\user\\Desktop\\ConEmuTrap", &stinfo, &pinfo))
{
WriteErrorLogEntry(L"CreateProcessAsUserW 1");
intialize();
}
while (!m_fStopping && pinfo.hProcess)
{
if(WaitForSingleObject(pinfo.hProcess, 1000) != WAIT_TIMEOUT) break;
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.