239 lines
No EOL
7.7 KiB
C#
239 lines
No EOL
7.7 KiB
C#
using System;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Runtime.ConstrainedExecution;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security;
|
|
using System.Text;
|
|
|
|
using JetBrains.Annotations;
|
|
|
|
using Microsoft.Win32.SafeHandles;
|
|
|
|
namespace AmagnoVirtualPrinter.Utils
|
|
{
|
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
|
[SuppressMessage("ReSharper", "FieldCanBeMadeReadOnly.Global")]
|
|
[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")]
|
|
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
|
|
internal static class Win32Sys
|
|
{
|
|
/// <summary>
|
|
/// Creates process with given command line via Win32 API.
|
|
/// Use this method to start a process as a service.
|
|
/// </summary>
|
|
/// <param name="sessId"></param>
|
|
/// <param name="user"></param>
|
|
/// <param name="commandLine"></param>
|
|
internal static void CreateProcessAsUser(int sessId, string user, string commandLine)
|
|
{
|
|
WTSQueryUserToken((uint) sessId, out var userToken);
|
|
if (userToken.IsInvalid)
|
|
{
|
|
throw new InvalidOperationException($"Could not query user token for session {sessId}! (" + GetLastError() + ")",
|
|
Windows.LastError);
|
|
}
|
|
|
|
CreateProcessAsUser(userToken, user, commandLine);
|
|
}
|
|
|
|
private static void CreateProcessAsUser([NotNull]SafeTokenHandle token, string user, string commandLine)
|
|
{
|
|
var processInformation = new PROCESS_INFORMATION();
|
|
try
|
|
{
|
|
var securityAttributes = new SECURITY_ATTRIBUTES();
|
|
securityAttributes.Length = Marshal.SizeOf(securityAttributes);
|
|
|
|
var startupInfo = new STARTUPINFO();
|
|
startupInfo.cb = Marshal.SizeOf(startupInfo);
|
|
startupInfo.lpDesktop = "winsta0\\default";
|
|
|
|
var info = new ProfileInfo();
|
|
info.dwSize = Marshal.SizeOf(info);
|
|
info.lpUserName = user;
|
|
info.dwFlags = 1;
|
|
|
|
var result = LoadUserProfile(token, ref info);
|
|
if (!result)
|
|
{
|
|
throw Windows.LastError;
|
|
}
|
|
|
|
result = CreateEnvironmentBlock(out var lpEnvironment, token, false);
|
|
if (!result)
|
|
{
|
|
throw Windows.LastError;
|
|
}
|
|
|
|
result = CreateProcessAsUser
|
|
(
|
|
token,
|
|
null,
|
|
commandLine,
|
|
ref securityAttributes,
|
|
ref securityAttributes,
|
|
false,
|
|
0x00000400,
|
|
lpEnvironment,
|
|
null,
|
|
ref startupInfo,
|
|
ref processInformation
|
|
);
|
|
|
|
if (!result)
|
|
{
|
|
throw Windows.LastError;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (processInformation.hProcess != IntPtr.Zero)
|
|
{
|
|
CloseMyHandle(processInformation.hProcess);
|
|
}
|
|
|
|
if (processInformation.hThread != IntPtr.Zero)
|
|
{
|
|
CloseMyHandle(processInformation.hThread);
|
|
}
|
|
|
|
if (!token.IsInvalid)
|
|
{
|
|
token.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
#region Classes
|
|
|
|
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
|
|
{
|
|
private SafeTokenHandle() : base(true)
|
|
{
|
|
}
|
|
|
|
[DllImport("kernel32.dll")]
|
|
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
|
[SuppressUnmanagedCodeSecurity]
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
private static extern bool CloseHandle(IntPtr handle);
|
|
|
|
protected override bool ReleaseHandle()
|
|
{
|
|
return CloseHandle(handle);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"{handle}";
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Structures
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct PROCESS_INFORMATION
|
|
{
|
|
public IntPtr hProcess;
|
|
public IntPtr hThread;
|
|
public int dwProcessID;
|
|
public int dwThreadID;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct STARTUPINFO
|
|
{
|
|
public int cb;
|
|
public string lpReserved;
|
|
public string lpDesktop;
|
|
public string lpTitle;
|
|
public int dwX;
|
|
public int dwY;
|
|
public int dwXSize;
|
|
public int dwXCountChars;
|
|
public int dwYCountChars;
|
|
public int dwFillAttribute;
|
|
public int dwFlags;
|
|
public short wShowWindow;
|
|
public short cbReserved2;
|
|
public IntPtr lpReserved2;
|
|
public IntPtr hStdInput;
|
|
public IntPtr hStdOutput;
|
|
public IntPtr hStdError;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct ProfileInfo
|
|
{
|
|
public int dwSize;
|
|
public int dwFlags;
|
|
public string lpUserName;
|
|
public string lpProfilePath;
|
|
public string lpDefaultPath;
|
|
public string lpServerName;
|
|
public string lpPolicyPath;
|
|
public IntPtr hProfile;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
public struct SECURITY_ATTRIBUTES
|
|
{
|
|
public int Length;
|
|
public IntPtr lpSecurityDescriptor;
|
|
public bool bInheritHandle;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Windows interop
|
|
|
|
[DllImport("userenv.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
private static extern bool LoadUserProfile(SafeTokenHandle hToken, ref ProfileInfo lpProfileInfo);
|
|
|
|
[DllImport("userenv.dll", SetLastError = true)]
|
|
private static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment,
|
|
SafeTokenHandle hToken, bool bInherit);
|
|
|
|
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true,
|
|
CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
|
|
private static extern bool CloseMyHandle(IntPtr handle);
|
|
|
|
[DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true,
|
|
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
|
|
private static extern bool CreateProcessAsUser(SafeTokenHandle hToken,
|
|
string lpApplicationName, string lpCommandLine, ref SECURITY_ATTRIBUTES lpProcessAttributes,
|
|
ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle,
|
|
int dwCreationFlags, IntPtr lpEnvironment, string lpCurrentDirectory,
|
|
ref STARTUPINFO lpStartupInfo, ref PROCESS_INFORMATION lpProcessInformation);
|
|
|
|
[DllImport("wtsapi32.dll", SetLastError = true)]
|
|
private static extern bool WTSQueryUserToken(uint sessionId, out SafeTokenHandle Token);
|
|
|
|
[DllImport("kernel32.dll")]
|
|
public static extern uint GetLastError();
|
|
|
|
[DllImport("kernel32")]
|
|
public static extern int GetPrivateProfileString
|
|
(
|
|
string section,
|
|
string key,
|
|
string def,
|
|
StringBuilder retVal,
|
|
int size,
|
|
string filePath
|
|
);
|
|
|
|
[DllImport("kernel32")]
|
|
public static extern long WritePrivateProfileString
|
|
(
|
|
string section,
|
|
string key,
|
|
string val,
|
|
string filePath
|
|
);
|
|
|
|
#endregion
|
|
}
|
|
} |