Używam CreateProcessAsUser z usługi windows (, prosimy o pozostanie na temat i zakładamy, że mam bardzo dobry powód, aby to zrobić). W przeciwieństwie do tego, o co pytają wszyscy, dostaję okno w mojej aktywnej sesji terminalowej (sesja 1) zamiast tej samej sesji co usługa (sesja 0) - co jest niepożądane.CreateProcessAsUser Tworzenie okna w aktywnej sesji
Przywłaszczyłem Scott Allen's code; i wymyśliło następujące. Godne uwagi zmiany to "powrót do siebie", "CREATE_NO_WINDOW" i wsparcie dla wiersza poleceń.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Security.Principal;
using System.ComponentModel;
using System.IO;
namespace SourceCode.Runtime.ChildProcessService
{
[SuppressUnmanagedCodeSecurity]
class NativeMethods
{
[StructLayout(LayoutKind.Sequential)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public Int32 dwProcessID;
public Int32 dwThreadID;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
public enum SECURITY_IMPERSONATION_LEVEL
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TOKEN_TYPE
{
TokenPrimary = 1,
TokenImpersonation
}
public const int GENERIC_ALL_ACCESS = 0x10000000;
public const int CREATE_NO_WINDOW = 0x08000000;
[
DllImport("kernel32.dll",
EntryPoint = "CloseHandle", SetLastError = true,
CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)
]
public static extern bool CloseHandle(IntPtr handle);
[
DllImport("advapi32.dll",
EntryPoint = "CreateProcessAsUser", SetLastError = true,
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)
]
public static extern bool
CreateProcessAsUser(IntPtr hToken, string lpApplicationName, string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes, ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandle, Int32 dwCreationFlags, IntPtr lpEnvrionment,
string lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
ref PROCESS_INFORMATION lpProcessInformation);
[
DllImport("advapi32.dll",
EntryPoint = "DuplicateTokenEx")
]
public static extern bool
DuplicateTokenEx(IntPtr hExistingToken, Int32 dwDesiredAccess,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
Int32 ImpersonationLevel, Int32 dwTokenType,
ref IntPtr phNewToken);
public static Process CreateProcessAsUser(string filename, string args)
{
var hToken = WindowsIdentity.GetCurrent().Token;
var hDupedToken = IntPtr.Zero;
var pi = new PROCESS_INFORMATION();
var sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
try
{
if (!DuplicateTokenEx(
hToken,
GENERIC_ALL_ACCESS,
ref sa,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,
(int)TOKEN_TYPE.TokenPrimary,
ref hDupedToken
))
throw new Win32Exception(Marshal.GetLastWin32Error());
var si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
si.lpDesktop = "";
var path = Path.GetFullPath(filename);
var dir = Path.GetDirectoryName(path);
// Revert to self to create the entire process; not doing this might
// require that the currently impersonated user has "Replace a process
// level token" rights - we only want our service account to need
// that right.
using (var ctx = WindowsIdentity.Impersonate(IntPtr.Zero))
{
if (!CreateProcessAsUser(
hDupedToken,
path,
string.Format("\"{0}\" {1}", filename.Replace("\"", "\"\""), args),
ref sa, ref sa,
false, 0, IntPtr.Zero,
dir, ref si, ref pi
))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
return Process.GetProcessById(pi.dwProcessID);
}
finally
{
if (pi.hProcess != IntPtr.Zero)
CloseHandle(pi.hProcess);
if (pi.hThread != IntPtr.Zero)
CloseHandle(pi.hThread);
if (hDupedToken != IntPtr.Zero)
CloseHandle(hDupedToken);
}
}
}
}
Teraz zakładamy, że usługa jest uruchomiona w ramach „Domain \ MyService” i jestem zalogowany jako „domena \ Administrator” - a ja uruchamiania aplikacji konsoli jako proces roboczy. Kiedy korzystam z aplikacji klienckiej w celu uzyskania dostępu do usługi (usługa nie jest uruchamiana w trybie konsoli, tj. Jest w sesji 0) i wykonuję metodę, która wywołuje CreateProcessAsUser
, proces roboczy pojawia się na moim pulpicie.
Teraz mogę zrobić to aplikacji systemu Windows bez okna, aby krok po kroku tworzenia okna konsoli; jednak na koniec dnia jest nadal tworzony w sesji 1.
Jakieś pomysły, dlaczego aplikacja konsolowa nie jest tworzona w tej samej sesji co usługa?
Wygląda na to może mieć coś wspólnego z [ta ciemna magia] (http://alex-ionescu.com/?p=60), ale nie mogę dowiedzieć się, w jaki sposób go pominąć. –
Próbowano użyć "Service-0 × 0-3e7 $ \ Default" jako pulpitu - co powoduje awarię aplikacji. –
Jaką wersję systemu Windows? Czy próbowałeś zostawić lpDeskTop na zero? –