AmagnoVirtualPrinter/Common/VirtualPrinter.Utils/Win32Sys.cs
2020-10-19 17:44:50 +02:00

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 VirtualPrinter.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
}
}