从windows服务启动一个带UI程序的界面,这个需求在xp中是很随意的,从Vista开始似乎没有那么随意了,因为Vista中加入了Session的概念,那么什么是Session,我想这篇文章介绍的应该比我权威的多。Session隔离介绍
明白了Session的概念后,我将通过Win32 API来实现从windows服务启动一个带UI的界面(从Session 0中启动Session *的程序)
,这个实现过程是我从C++代码翻译过来的。
实现的思路
涉及的Win32 API
WTSGetActiveConsoleSessionId
获取活动的Session IDWTSQueryUserToken
根据Session ID获取用户TokenCreateProcessAsUser
使用用户Token来启动UI程序实现代码
public class ProcessAsUser
{
public struct SECURITY_ATTRIBUTES
{
public uint nLength;
public uint lpSecurityDescriptor;
public bool bInheritHandle;
}
public struct STARTUPINFO
{
public uint cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public uint dwX;
public uint dwY;
public uint dwXSize;
public uint dwYSize;
public uint dwXCountChars;
public uint dwYCountChars;
public uint dwFillAttribute;
public uint dwFlags;
public ushort wShowWindow;
public ushort cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public uint dwProcessId;
public uint dwThreadId;
}
[DllImport("kernel32.dll")]
static extern uint WTSGetActiveConsoleSessionId();
[DllImport("Wtsapi32.dll")]
private static extern bool WTSQueryUserToken(uint SessionId, out uint hToken);
[DllImport("Kernel32.dll")]
private static extern uint GetLastError();
[DllImport("kernel32.dll")]
private static extern bool CloseHandle(IntPtr hSnapshot);
[DllImport("advapi32.dll")]
public extern static bool CreateProcessAsUser(IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SECURITY_ATTRIBUTES lpProcessAttributes,
ref SECURITY_ATTRIBUTES lpThreadAttributes,
bool bInheritHandle,
uint dwCreationFlags,
uint lpEnvironment,
string lpCurrentDirectory,
ref STARTUPINFO lpStartupInfo,
out PROCESS_INFORMATION lpProcessInformation);
public static bool StartUIProcessFromService(string exePath)
{
//获取Session ID
var sId=WTSGetActiveConsoleSessionId();
if (sId == 0)
{
return false;
}
uint hToken;
var isOk=WTSQueryUserToken(sId, out hToken);
if (!isOk || hToken == 0)
{
return false;
}
var lpProcessAttr = new SECURITY_ATTRIBUTES();
lpProcessAttr.nLength = (uint)Marshal.SizeOf(lpProcessAttr);
var lpThreadAttr = new SECURITY_ATTRIBUTES();
lpThreadAttr.nLength = (uint)Marshal.SizeOf(lpThreadAttr);
var lpStratupInfo = new STARTUPINFO();
lpStratupInfo.cb = (uint)Marshal.SizeOf(lpStratupInfo);
lpStratupInfo.lpDesktop = @"winsta0\default";
PROCESS_INFORMATION lpProcessInfo;
isOk=CreateProcessAsUser((IntPtr)hToken,
exePath,
null,
ref lpProcessAttr,
ref lpThreadAttr,
false,
0,
0,
null,
ref lpStratupInfo,
out lpProcessInfo
);
CloseHandle((IntPtr)hToken);
return isOk;
}
}
之前我们通过WTSGetActiveConsoleSessionId
获取活动Session ID,当有多个用户登录时,Windows提供了WTSEnumerateSessions
方法枚举多个Session ID。
主要涉及API
WTSEnumerateSessions
检索在远程桌面会话主机 (RD 会话主机) 服务器上的会话的列表。WTSFreeMemory
释放由远程桌面服务函数分配的内存。实现代码
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pSessionInfo);
[DllImport("Wtsapi32.dll")]
private extern static bool WTSEnumerateSessions(IntPtr hServer, uint reserved, uint version, out IntPtr ppSessionInfo, out uint pCount);
struct WTS_SESSION_INFO
{
public uint SessionId;
public string pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
private static uint EnumerateActiveSession()
{
uint dwSessionID = 0xFFFFFFFF;
uint dwCount = 0;
IntPtr intPtr = IntPtr.Zero;
try
{
IntPtr hServer = IntPtr.Zero;
if (WTSEnumerateSessions(hServer, 0, 1, out intPtr, out dwCount))
{
var tmp = intPtr;
for (var i = 0; i < dwCount; ++i)
{
var pSessionInfo = (WTS_SESSION_INFO)Marshal.PtrToStructure(tmp, typeof(WTS_SESSION_INFO));
if (WTS_CONNECTSTATE_CLASS.WTSActive == pSessionInfo.State)
{
dwSessionID = pSessionInfo.SessionId;
break;
}
if (WTS_CONNECTSTATE_CLASS.WTSConnected == pSessionInfo.State)
{
dwSessionID = pSessionInfo.SessionId;
}
tmp += Marshal.SizeOf(typeof(WTS_SESSION_INFO));
}
WTSFreeMemory(intPtr);
}
var eCode = GetLastError();
}
catch (Exception ex)
{
var eCode = GetLastError();
}
return dwSessionID;
}