如果要用服务来启动一个带窗口的程序,你会发现只有进程在,但没有窗口出现。
可以写一个Process.run记事体来测试,看有没有记事本窗口。
出现这问题的原因是服务是以system帐号启动的,但当前widnows的启动者是用户身份的administrator或者其它用户自建的管理员帐号, 不在同个会话上当然没有窗体,国外一个大神写了一个类解决了这个问题,获取当前登录的用户sessionId,并用它来启动程序,这样就有窗口了,感谢这位大神,直接上代码:
public class LoaderMan
{
public const int MAXIMUM_ALLOWED = 0x2000000;
// public const IntPtr INVALID_HANDLE_VALUE = 0xffffffff;
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool CloseHandle(IntPtr hSnapshot);
//sessionId是用户的会话ID,默认为当前活动用户;appFullName是程序完整路径,args是启动参数
public static int CreateProcess(int sessionId, string appFullName, string args)
{
if (!System.IO.File.Exists(appFullName))
{
throw new System.IO.FileNotFoundException(appFullName);
}
bool sucess = false;
IntPtr hToken = IntPtr.Zero,
hDupedToken = IntPtr.Zero,
lpEnvironment = IntPtr.Zero;
Process_Information pi = new Process_Information();
SecurityAttributes sa;
try
{
//获取指定会话的用户令牌,须具备System权限
sucess = WTSQueryUserToken(sessionId, out hToken);
if (!sucess)
{
//服务程序中,获取指定会话的桌面进程
var explorer = Process.GetProcesses()
.FirstOrDefault(p => p.SessionId == sessionId && string.Equals(p.ProcessName, "explorer", StringComparison.OrdinalIgnoreCase));
if (explorer == null)
{
return 0;
}
sucess = OpenProcessToken(explorer.Handle, TokenAccessLevels.AllAccess, ref hToken);
if (!sucess)
{
//TraceWin32Error("CreateProcessAs Session" + sessionId.ToString(), "WTSQueryUserToken");
return 0;
}
}
//复制桌面进程的句柄
sa = new SecurityAttributes();
sa.Length = Marshal.SizeOf(sa);
var si = new StartUpInfo();
si.cb = Marshal.SizeOf(si);
sucess =DuplicateTokenEx(
hToken,
MAXIMUM_ALLOWED,
ref sa,
(int)SecurityImpersonationLevel.SecurityIdentification,
(int)TokenType.TokenPrimary,
ref hDupedToken
);
if (!sucess)
{
//TraceWin32Error("CreateProcessAs Session" + sessionId.ToString(), "DuplicateTokenEx");
return 0;
}
//利用复制的句柄在指定的会话中初始化运行环境
sucess =CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);
if (!sucess)
{
//TraceWin32Error("CreateProcessAs Session" + sessionId.ToString(), "CreateEnvironmentBlock");
return 0;
}
//在指定会话中开启进程
if (!string.IsNullOrEmpty(args))
{
args = string.Format("\"{0}\" {1}", appFullName, args);
appFullName = null;
}
sucess =CreateProcessAsUser(
hDupedToken,
appFullName,
args,
ref sa, ref sa,
false, 0, IntPtr.Zero,
null,
ref si,
ref pi
);
if (!sucess)
{
//TraceWin32Error("CreateProcessAs Session" + sessionId.ToString(), "CreateProcessAsUser");
}
return pi.dwProcessID;
}
catch (Exception exp)
{
//TraceWin32Error("CreateProcessAs Session" + sessionId.ToString(), exp.Message);
return 0;
}
finally
{
if (hDupedToken != IntPtr.Zero)CloseHandle(hDupedToken);
if (lpEnvironment != IntPtr.Zero)DestroyEnvironmentBlock(lpEnvironment);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
public static int? GetActiveSessionId()
{
IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero, ppSessionInfo = IntPtr.Zero;
List<string> result = new List<string>();
try
{
int count = 0;
bool success =WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref ppSessionInfo, ref count);
if (success)
{
int dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = ppSessionInfo.ToInt64(); //数组起始地址
for (int i = 0; i < count; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
if (si.State ==WTS_CONNECTSTATE_CLASS.WTSActive)
{
return si.SessionID;
}
current += dataSize;
}
}
else
{
//TraceWin32Error("GetActiveSessionId", "WTSEnumerateSessions");
}
return null;
}
finally
{
if (ppSessionInfo != IntPtr.Zero)WTSFreeMemory(ppSessionInfo);
//CloseHandle(ppSessionInfo);
}
}
#region API声明
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSEnumerateSessions(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] uint Reserved,
[MarshalAs(UnmanagedType.U4)] uint Version,
ref IntPtr ppSessionInfo,
[MarshalAs(UnmanagedType.U4)] ref Int32 pCount);
/// <summary>
/// Terminal Services API Functions,The WTSFreeMemory function frees memory allocated by a Terminal Services function.
/// </summary>
/// <param name="pMemory">[in] Pointer to the memory to free</param>
[DllImport("wtsapi32.dll")]
public static extern void WTSFreeMemory(System.IntPtr pMemory);
[StructLayout(LayoutKind.Sequential)]
public struct WTS_SESSION_INFO
{
public Int32 SessionID;
[MarshalAs(UnmanagedType.LPStr)]
public String pWinStationName;
public WTS_CONNECTSTATE_CLASS State;
}
public enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
[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 SecurityAttributes
{
public Int32 Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
public enum SecurityImpersonationLevel
{
SecurityAnonymous,
SecurityIdentification,
SecurityImpersonation,
SecurityDelegation
}
public enum TokenType
{
TokenPrimary = 1,
TokenImpersonation
}
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool DuplicateTokenEx(
IntPtr hExistingToken,
Int32 dwDesiredAccess,
ref SecurityAttributes lpThreadAttributes,
Int32 ImpersonationLevel,
Int32 dwTokenType,
ref IntPtr phNewToken);
[DllImport("userenv.dll", SetLastError = true)]
public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit);
[DllImport("userenv.dll", SetLastError = true)]
public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment);
[DllImport("advapi32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool CreateProcessAsUser(
IntPtr hToken,
string lpApplicationName,
string lpCommandLine,
ref SecurityAttributes lpProcessAttributes,
ref SecurityAttributes lpThreadAttributes,
bool bInheritHandle,
Int32 dwCreationFlags,
IntPtr lpEnvrionment,
string lpCurrentDirectory,
ref StartUpInfo lpStartupInfo,
ref Process_Information lpProcessInformation);
//以下定义同System.Windows.Forms.SecurityIDType
public enum SID_NAME_USE
{
User = 1,
Group,
Domain,
Alias,
WellKnownGroup,
Account,
Invalid,
Unknown,
Computer,
Label
}
[StructLayout(LayoutKind.Sequential)]
public struct TOKEN_USER
{
public SID_AND_ATTRIBUTES User;
}
[StructLayout(LayoutKind.Sequential)]
public struct SID_AND_ATTRIBUTES
{
public IntPtr Sid;
public uint Attributes;
}
/// <summary>
/// 获取控制台当前活动的会话ID,若没有将返回0xFFFFFFFF(在远程连接至Server2008或Win8时取得的值并不是用户的会话ID)
/// </summary>
/// <returns></returns>
//[DllImport("kernel32.dll", SetLastError = true)]
//public static extern uint WTSGetActiveConsoleSessionId();
/// <summary>
/// 查询指定会话ID的用户令牌,调用方必须具备System权限
/// </summary>
[DllImport("wtsapi32.dll", SetLastError = true)]
public static extern bool WTSQueryUserToken(int sessionId, out IntPtr Token);
[DllImport("advapi32", SetLastError = true)]
public static extern bool OpenProcessToken(IntPtr processHandle, System.Security.Principal.TokenAccessLevels desiredAccess, ref IntPtr htok);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetTokenInformation(IntPtr htok, uint tokenInformationClass, IntPtr hTokInformation, uint tokenInformationLength, out uint returnLength);
#endregion
}
然后调用它:
int? sessionid = Loader2.GetActiveSessionId();
if (isRunAsUser() && sessionid.HasValue)
{
log.AppendLine("关闭以system启动的程序");
var pid = Loader2.CreateProcess(sessionid.Value,
path,args);
if (pid > 0)
{
//以User启动成功
}
}
private bool isRunAsUser()
{
bool isHave = false;
ComputerLoginUserInfo uif = new ComputerLoginUserInfo();
if (uif != null && uif.ComputerLoginUserInfoList != null)
{
var lst = uif.ComputerLoginUserInfoList.Where(p => !string.IsNullOrEmpty(p.UserName)).ToList();
if (lst.Count >= 1)
{
isHave = true;
}
}
return isHave;
}
//---引用
using System.Runtime.InteropServices;
using System.Text;
/// <summary>
/// Windows 任务管理器登录用户信息
/// author:dongee
/// date:2010.10.14
/// </summary>
public class ComputerLoginUserInfo
{
#region 本机连接用户信息API封装
public class TSControl
{
[DllImport("wtsapi32", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool WTSEnumerateSessions(int hServer, int Reserved,
int Version, ref long ppSessionInfo, ref int pCount);
[DllImport("wtsapi32.dll")]
public static extern void WTSFreeMemory(System.IntPtr pMemory);
[DllImport("wtsapi32.dll")]
public static extern bool WTSLogoffSession(int hServer, long SessionId, bool bWait);
[DllImport("Wtsapi32.dll")]
public static extern bool WTSQuerySessionInformation(
System.IntPtr hServer, int sessionId, WTSInfoClass wtsInfoClass,
out StringBuilder ppBuffer, out int pBytesReturned);
public enum WTSInfoClass
{
WTSInitialProgram,
WTSApplicationName,
WTSWorkingDirectory,
WTSOEMId,
WTSSessionId,
WTSUserName,
WTSWinStationName,
WTSDomainName,
WTSConnectState,
WTSClientBuildNumber,
WTSClientName,
WTSClientDirectory,
WTSClientProductId,
WTSClientHardwareId,
WTSClientAddress,
WTSClientDisplay,
WTSClientProtocolType
}
public enum WTS_CONNECTSTATE_CLASS
{
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit,
}
public struct WTS_SESSION_INFO
{
public int SessionID;
[MarshalAs(UnmanagedType.LPTStr)]
public string pWinStationName;
public WTS_CONNECTSTATE_CLASS state;
}
public static WTS_SESSION_INFO[] SessionEnumeration()
{
//Set handle of terminal server as the current terminal server
int hServer = 0;
bool RetVal;
long lpBuffer = 0;
int Count = 0;
long p;
WTS_SESSION_INFO Session_Info = new WTS_SESSION_INFO();
WTS_SESSION_INFO[] arrSessionInfo;
RetVal = WTSEnumerateSessions(hServer, 0, 1, ref lpBuffer, ref Count);
arrSessionInfo = new WTS_SESSION_INFO[0];
if (RetVal)
{
arrSessionInfo = new WTS_SESSION_INFO[Count];
int i;
p = lpBuffer;
for (i = 0; i < Count; i++)
{
arrSessionInfo[i] = (WTS_SESSION_INFO)Marshal.PtrToStructure(new IntPtr(p),
Session_Info.GetType());
p += Marshal.SizeOf(Session_Info.GetType());
}
WTSFreeMemory(new IntPtr(lpBuffer));
}
else
{
//Insert Error Reaction Here
}
return arrSessionInfo;
}
}
#endregion
public System.Collections.Generic.List<ComputerLoginUserInfoModel> ComputerLoginUserInfoList;
public ComputerLoginUserInfo()
{
#region 查询代码
TSControl.WTS_SESSION_INFO[] pSessionInfo = TSControl.SessionEnumeration();
ComputerLoginUserInfoModel cum = null;
ComputerLoginUserInfoList = new System.Collections.Generic.List<ComputerLoginUserInfoModel>();
for (int i = 0; i < pSessionInfo.Length; i++)
{
if ("RDP-Tcp" != pSessionInfo[i].pWinStationName)
{
try
{
int count = 0;
IntPtr buffer = IntPtr.Zero;
StringBuilder userName = new StringBuilder(); // 用户名
StringBuilder clientUser = new StringBuilder(); // 客户端名
StringBuilder stateType = new StringBuilder(); // 会话类型
bool userNameBool = TSControl.WTSQuerySessionInformation(IntPtr.Zero,
pSessionInfo[i].SessionID, TSControl.WTSInfoClass.WTSUserName,
out userName, out count);
bool clientUserBool = TSControl.WTSQuerySessionInformation(IntPtr.Zero,
pSessionInfo[i].SessionID, TSControl.WTSInfoClass.WTSClientName,
out clientUser, out count);
bool stateTypeBool = TSControl.WTSQuerySessionInformation(IntPtr.Zero,
pSessionInfo[i].SessionID, TSControl.WTSInfoClass.WTSWinStationName,
out stateType, out count);
if (userNameBool && clientUserBool && stateTypeBool)
{
cum = new ComputerLoginUserInfoModel();
cum.UserName = userName.ToString();
cum.ClientUserName = clientUser.ToString();
cum.SessionType = stateType.ToString();
}
ComputerLoginUserInfoList.Add(cum);
}
catch (Exception ex) { }
}
}
#endregion
}
}
public class ComputerLoginUserInfoModel
{
#region 用户信息字段
private string userName;
private string clientUserName;
private string sessionType;
/// <summary>
/// 会话类型
/// </summary>
public string SessionType
{
get { return sessionType; }
set { sessionType = value; }
}
/// <summary>
/// 客户端名
/// </summary>
public string ClientUserName
{
get { return clientUserName; }
set { clientUserName = value; }
}
/// <summary>
/// 登录用户名
/// </summary>
public string UserName
{
get { return userName; }
set { userName = value; }
}
#endregion
}