之前从服务启动用户界面程序,使用的都是CreateProcess,或者.net库里面的Process::Start函数。但是在WIN2008上面碰到了问题,进程能启动,但是界面出不来。
原因是微软搞得UAC技术。
UAC(User Account Control,用户账户控制) 是微软为提高系统安全而在Windows Vista中引入的新技术,通过限制应用软件而改进Windows操作系统的安全性。
具体到我的程序上,就是无法从服务直接创建窗口。
实际上,用户窗口还是被创建出来了。但是由于服务是用system用户运行的,有自己的窗口站,和我们默认使用的winsta0不是一个窗口站,不能直接通讯,交互,所以当然不能在 "winsta0" 中的默认桌面上显示了。
虽然在VISTA里面就有了,但以前在项目中碰不到。因为用户很少用Vista或者Win7,服务端也都是WIN2003。现在WIN2008用的多了,问题就出来了。
在网上搜了半天,找到了解决方案:
服务启动UI程序不能直接CreateProcess(),否则因为service session,程序UI不能看见
拿到当前explorer.exe的token,然后CreateProcessAsUser模拟当前用户启动进程...这样就可以显示界面了
代码如下:
bool GetTokenByName(ref IntPtr hToken, string lpName)
{
IntPtr hProcessSnap = IntPtr.Zero;
bool bRet = false;
hProcessSnap = CreateToolhelp32Snapshot(0x00000002, 0);
if ((int)hProcessSnap <= 0)
{
return false;
}
//--------------------------------------------
//
ProcessEntry32 pe32 = new ProcessEntry32();
pe32.dwSize = (uint)Marshal.SizeOf(pe32);
int bMore = Process32First(hProcessSnap, ref pe32);
while (bMore == 1)
{
if (0 == string.Compare(pe32.szExeFile, lpName, true))
{
int hProcess = OpenProcess(0x0400, false, (int)pe32.th32ProcessID);
System.IntPtr p = new IntPtr(hProcess);
bRet = OpenProcessToken(p, (int)TOKEN_ALL_ACCESS, ref hToken);
CloseHandle(hProcessSnap);
return bRet;
}
bMore = Process32Next(hProcessSnap, ref pe32);
}
CloseHandle(hProcessSnap);
return false;
}
public bool RunProcess(string szExe, string szPath)
{
IntPtr hToken = new IntPtr();
if (!GetTokenByName(ref hToken, "explorer.exe"))
{
return false;
}
STARTUPINFO si = new STARTUPINFO();
si.cb = Marshal.SizeOf(si);
PROCESS_INFORMATION pi = new PROCESS_INFORMATION();
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.Length = Marshal.SizeOf(sa);
bool result = CreateProcessAsUser(
hToken,
szExe,
String.Empty,
ref sa, ref sa,
false, 0, IntPtr.Zero,
szPath, ref si, ref pi);
return result;
}
[DllImport("KERNEL32.DLL ")]
public static extern IntPtr CreateToolhelp32Snapshot(uint flags, uint processid);
[DllImport("KERNEL32.DLL ")]
public static extern int CloseHandle(IntPtr handle);
[DllImport("KERNEL32.DLL ")]
public static extern int Process32First(IntPtr handle, ref ProcessEntry32 pe);
[DllImport("KERNEL32.DLL ")]
public static extern int Process32Next(IntPtr handle, ref ProcessEntry32 pe);
[StructLayout(LayoutKind.Sequential)]
public struct ProcessEntry32
{
public uint dwSize;
public uint cntUsage;
public uint th32ProcessID;
public IntPtr th32DefaultHeapID;
public uint th32ModuleID;
public uint cntThreads;
public uint th32ParentProcessID;
public int pcPriClassBase;
public uint dwFlags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string szExeFile;
};
[DllImport("kernel32.dll")]
public static extern int OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
public static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
private const int TOKEN_ASSIGN_PRIMARY = 0x1;
private const int TOKEN_DUPLICATE = 0x2;
private const int TOKEN_IMPERSONATE = 0x4;
private const int TOKEN_QUERY = 0x8;
private const int TOKEN_QUERY_SOURCE = 0x10;
private const int TOKEN_ADJUST_PRIVILEGES = 0x20;
private const int TOKEN_ADJUST_GROUPS = 0x40;
private const int TOKEN_ADJUST_DEFAULT = 0x80;
private const int TOKEN_ALL_ACCESS = TOKEN_ASSIGN_PRIMARY
+ TOKEN_DUPLICATE + TOKEN_IMPERSONATE + TOKEN_QUERY
+ TOKEN_QUERY_SOURCE + TOKEN_ADJUST_PRIVILEGES
+ TOKEN_ADJUST_GROUPS + TOKEN_ADJUST_DEFAULT;
private const int ANYSIZE_ARRAY = 1;
[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
}
[DllImport("kernel32.dll", SetLastError = true,
CharSet = CharSet.Auto, 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);