process started from service with CreateProcessWithLogonW terminates right away
在测试框架中,进程A必须使用
手动启动进程A(如_glagolig),一切正常。但是,当进程A由测试框架服务(在指定的测试框架的用户帐户_test_framework下运行)启动时,将无法正常工作。
我想了解根本原因。目前尚不清楚是什么使测试框架服务的桌面/窗口站对象与手动登录的用户的对象不同。
我还想找到一种解决方法,同时保持整体方案不变(帐户_test_framework下的测试框架的服务必须在_limited_user下启动进程B)。
附录:根据文档,如果您不希望新流程与用户进行交互,则应该无需执行这些步骤即可使用CreateProcessAsUser。我尚未对此进行测试,但是假设它是正确的,那么对于许多情况来说,这将是一个简单得多的解决方案。
事实证明,Microsoft已经以在C中启动交互式客户端进程的标题提供了示例代码来操纵window station和桌面访问权限。从Windows Vista开始,在默认窗口站中启动子进程已不足以允许该子进程与用户进行交互,但是它确实允许该子进程使用备用用户凭据运行。
我应该注意,Microsoft的代码使用
我写了一个最少的服务来演示如何使用Microsoft的示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | /*******************************************************************/ #define _WIN32_WINNT 0x0501 #include <windows.h> /*******************************************************************/ // See http://msdn.microsoft.com/en-us/library/windows/desktop/aa379608%28v=vs.85%29.aspx //"Starting an Interactive Client Process in C++" BOOL AddAceToWindowStation(HWINSTA hwinsta, PSID psid); BOOL AddAceToDesktop(HDESK hdesk, PSID psid); BOOL GetLogonSID (HANDLE hToken, PSID *ppsid); VOID FreeLogonSID (PSID *ppsid); BOOL StartInteractiveClientProcess ( LPTSTR lpszUsername, // client to log on LPTSTR lpszDomain, // domain of client's account LPTSTR lpszPassword, // client's password LPTSTR lpCommandLine // command line to execute ); /*******************************************************************/ const wchar_t displayname[] = L"Demo service for CreateProcessWithLogonW"; const wchar_t servicename[] = L"demosvc-createprocesswithlogonw"; DWORD dwWin32ExitCode = 0, dwServiceSpecificExitCode = 0; /*******************************************************************/ #define EXCEPTION_USER 0xE0000000 #define FACILITY_USER_DEMOSVC 0x0001 #define EXCEPTION_USER_LINENUMBER (EXCEPTION_USER | (FACILITY_USER_DEMOSVC << 16)) HANDLE eventloghandle; /*******************************************************************/ wchar_t subprocess_username[] = L"harry-test1"; wchar_t subprocess_domain[] = L"scms"; wchar_t subprocess_password[] = L"xyzzy916"; wchar_t subprocess_command[] = L"cmd.exe /c dir"; void demo(void) { if (!StartInteractiveClientProcess(subprocess_username, subprocess_domain, subprocess_password, subprocess_command)) { const wchar_t * strings[] = {L"Creating subprocess failed."}; DWORD err = GetLastError(); ReportEventW(eventloghandle, EVENTLOG_ERROR_TYPE, 0, 2, NULL, _countof(strings), sizeof(err), strings, &err); return; } { const wchar_t * strings[] = {L"Creating subprocess succeeded!"}; ReportEventW(eventloghandle, EVENTLOG_INFORMATION_TYPE, 0, 1, NULL, _countof(strings), 0, strings, NULL); } return; } /*******************************************************************/ CRITICAL_SECTION service_section; SERVICE_STATUS service_status; // Protected by service_section SERVICE_STATUS_HANDLE service_handle = 0; // Constant once set, so can be used from any thread static DWORD WINAPI ServiceHandlerEx(DWORD control, DWORD eventtype, LPVOID lpEventData, LPVOID lpContext) { if (control == SERVICE_CONTROL_INTERROGATE) { EnterCriticalSection(&service_section); if (service_status.dwCurrentState != SERVICE_STOPPED) { SetServiceStatus(service_handle, &service_status); } LeaveCriticalSection(&service_section); return NO_ERROR; } return ERROR_CALL_NOT_IMPLEMENTED; } static VOID WINAPI ServiceMain(DWORD argc, LPTSTR * argv) { SERVICE_STATUS status; EnterCriticalSection(&service_section); service_handle = RegisterServiceCtrlHandlerEx(argv[0], ServiceHandlerEx, NULL); if (!service_handle) RaiseException(EXCEPTION_USER_LINENUMBER | __LINE__, EXCEPTION_NONCONTINUABLE, 0, NULL); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = 0; service_status.dwWin32ExitCode = STILL_ACTIVE; service_status.dwServiceSpecificExitCode = 0; service_status.dwCheckPoint = 0; service_status.dwWaitHint = 500; SetServiceStatus(service_handle, &service_status); LeaveCriticalSection(&service_section); /************** service main function **************/ { const wchar_t * strings[] = {L"Service started!"}; ReportEventW(eventloghandle, EVENTLOG_INFORMATION_TYPE, 0, 2, NULL, _countof(strings), 0, strings, NULL); } demo(); /************** service shutdown **************/ EnterCriticalSection(&service_section); status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; status.dwCurrentState = service_status.dwCurrentState = SERVICE_STOPPED; status.dwControlsAccepted = 0; status.dwCheckPoint = 0; status.dwWaitHint = 500; status.dwWin32ExitCode = dwWin32ExitCode; status.dwServiceSpecificExitCode = dwServiceSpecificExitCode; LeaveCriticalSection(&service_section); SetServiceStatus(service_handle, &status); /* NB: SetServiceStatus does not return here if successful, so any code after this point will not normally run. */ return; } int wmain(int argc, wchar_t * argv[]) { const static SERVICE_TABLE_ENTRY servicetable[2] = { {(wchar_t *)servicename, ServiceMain}, {NULL, NULL} }; InitializeCriticalSection(&service_section); eventloghandle = RegisterEventSource(NULL, displayname); if (!eventloghandle) return GetLastError(); { const wchar_t * strings[] = {L"Executable started!"}; ReportEventW(eventloghandle, EVENTLOG_INFORMATION_TYPE, 0, 2, NULL, _countof(strings), 0, strings, NULL); } if (StartServiceCtrlDispatcher(servicetable)) return 0; return GetLastError(); } |
这必须与Microsoft的示例代码链接。然后可以使用
1 | sc create demosvc-createprocesswithlogonw binPath= c:\\path\\demosvc.exe DisplayName="Demo service for CreateProcessWithLogonW" |
我得到了以下解决方法。我配置了另一种以_limited_user身份运行的服务,并按需启动。然后,测试框架可以启动和停止受限的用户服务。而且受限的用户服务可以运行我的测试所需的过程。
解决方法起作用。因此,我的进程不需要交互式桌面(即使它们加载了user32.dll)。显然,可以在非交互式上下文中加载user32.dll。但是存在一些未知的微妙之处,当使用CreateProcessWithLogonW从测试框架服务直接启动时,该过程无法运行。