开满天机
我看到您正在启动提升的流程。我将此添加到我的测试服务中,它仍然可以正常工作。但我认为问题可能出在这一行GetLinkedTokeIfRequiered():Marshal.Release(LINKED_TOKEN_INFO);那显然应该是:Marshal.FreeHGlobal(LINKED_TOKEN_INFO);解决这个问题,它可能会起作用。事实上,我很惊讶它没有崩溃。对我来说并不容易,挖掘这个。C# 互操作不是我的强项。为了 OP 的利益,我的测试服务的完整源代码是用 C++ 编写的,它可以工作:#include <windows.h>#include <wtsapi32.h>#include <userenv.h>#include <tchar.h>#include <stdio.h>#pragma comment (lib, "user32.lib")#pragma comment (lib, "wtsapi32.lib")#pragma comment (lib, "userenv.lib")#pragma comment (lib, "advapi32.lib")DWORD report_error (const char *operation){ DWORD err = GetLastError (); return err;}// Launch notepad as currently logged-on userDWORD LaunchProcess (DWORD SessionId, const char **failed_operation){ HANDLE hToken; BOOL ok = WTSQueryUserToken (SessionId, &hToken); if (!ok) return report_error (*failed_operation = "WTSQueryUserToken"); void *environment = NULL; ok = CreateEnvironmentBlock (&environment, hToken, TRUE); if (!ok) { CloseHandle (hToken); return report_error (*failed_operation = "CreateEnvironmentBlock"); } TOKEN_LINKED_TOKEN lto; DWORD nbytes; ok = GetTokenInformation (hToken, TokenLinkedToken, <o, sizeof (lto), &nbytes); if (ok) { CloseHandle (hToken); hToken = lto.LinkedToken; } STARTUPINFO si = { sizeof (si) } ; PROCESS_INFORMATION pi = { } ; si.lpDesktop = "winsta0\\default"; // Do NOT want to inherit handles here, surely DWORD dwCreationFlags = NORMAL_PRIORITY_CLASS | /* CREATE_NEW_CONSOLE | */ CREATE_UNICODE_ENVIRONMENT; ok = CreateProcessAsUser (hToken, "c:\\windows\\system32\\notepad.exe", NULL, NULL, NULL, FALSE, dwCreationFlags, environment, NULL, &si, &pi); DestroyEnvironmentBlock (environment); CloseHandle (hToken); if (!ok) return report_error (*failed_operation = "CreateProcessAsUser"); CloseHandle (pi.hThread); CloseHandle (pi.hProcess); return 0;}// Determine the session ID of the currently logged-on userDWORD GetCurrentSessionId (){ WTS_SESSION_INFO *pSessionInfo; DWORD n_sessions = 0; BOOL ok = WTSEnumerateSessions (WTS_CURRENT_SERVER, 0, 1, &pSessionInfo, &n_sessions); if (!ok) return 0; DWORD SessionId = 0; for (DWORD i = 0; i < n_sessions; ++i) { if (pSessionInfo [i].State == WTSActive) { SessionId = pSessionInfo [i].SessionId; break; } } WTSFreeMemory (pSessionInfo); return SessionId;}#define SERVICE_NAME __T ("demo_service")bool quit;// CtrlHandler callbackDWORD WINAPI CtrlHandler (DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext){ if (dwControl == SERVICE_CONTROL_STOP) quit = true; return NO_ERROR;}// SvcMain callbackVOID WINAPI SvcMain (DWORD dwArgc, LPTSTR *lpszArgv){ // Register for callbacks SERVICE_STATUS_HANDLE sh = RegisterServiceCtrlHandlerEx (SERVICE_NAME, CtrlHandler, NULL); // Tell the SCM that we are up and running SERVICE_STATUS ss = { }; ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ss.dwCurrentState = SERVICE_RUNNING; ss.dwControlsAccepted = SERVICE_ACCEPT_STOP; SetServiceStatus (sh, &ss); TCHAR buf [256]; const TCHAR *title = __T ("(c) 2018 Contoso Corporation"); while (!quit) { DWORD response = IDOK; DWORD SessionId = GetCurrentSessionId (); if (SessionId == 0) { Sleep (2000); continue; } // Pop-up a message on the screen of the currently logged-on user (session 1) _stprintf (buf, __T ("Ready to launch..., SessionId = %d"), SessionId); WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (TCHAR *) title, _tcslen (title), buf, _tcslen (buf), MB_OKCANCEL, 0, &response, TRUE); if (response == IDCANCEL) break; const char *failed_operation = ""; DWORD dwResult = LaunchProcess (SessionId, &failed_operation); // Report results _stprintf (buf, __T ("LaunchProcess returned %lx from %s"), dwResult, failed_operation); WTSSendMessage (WTS_CURRENT_SERVER_HANDLE, SessionId, (char *) title, _tcslen (title), buf, _tcslen (buf), MB_OK, 0, &response, TRUE); FILE *logfile = fopen ("g:\\temp\\service.log", "at"); if (logfile) { fprintf (logfile, "%s\n", buf); fclose (logfile); } } // Tell the SCM we are going away and exit ss.dwCurrentState = SERVICE_STOPPED; SetServiceStatus (sh, &ss);}// mainint main (void){ SERVICE_TABLE_ENTRY DispatchTable [] = { { SERVICE_NAME, SvcMain }, { NULL, NULL } }; // This call returns when the service has stopped. // The process should simply terminate when the call returns. StartServiceCtrlDispatcher (DispatchTable); return 0;}