asp.net中穿透Session 0 隔离(二)

  对于简单的交互,服务可以通过WTSSendMessage 函数,在用户Session 上显示消息窗口。对于一些复杂的UI 交互,必须调用CreateProcessAsUser 或其他方法(WCF、.NET远程处理等)进行跨Session 通信,在桌面用户上创建一个应用程序界面。

  WTSSendMessage 函数

  如果服务只是简单的向桌面用户Session 发送消息窗口,则可以使用WTSSendMessage 函数实现。首先,在上一篇下载的代码中加入一个Interop.cs 类,并在类中加入如下代码:

  

复制代码 代码如下:

  public static IntPtr WTS_CURRENT_SERVER_HANDLE = IntPtr.Zero;

  public static void ShowMessageBox(string message, string title)

  {

  int resp = 0;

  WTSSendMessage(

  WTS_CURRENT_SERVER_HANDLE,

  WTSGetActiveConsoleSessionId(),

  title, title.Length,

  message, message.Length,

  0, 0, out resp, false);

  }

  [DllImport("kernel32.dll", SetLastError = true)]

  public static extern int WTSGetActiveConsoleSessionId();

  [DllImport("wtsapi32.dll", SetLastError = true)]

  public static extern bool WTSSendMessage(

  IntPtr hServer,

  int SessionId,

  String pTitle,

  int TitleLength,

  String pMessage,

  int MessageLength,

  int Style,

  int Timeout,

  out int pResponse,

  bool bWait);

  在ShowMessageBox 函数中调用了WTSSendMessage 来发送信息窗口,这样我们就可以在Service 的OnStart 函数中使用,打开Service1.cs 加入下面代码:

  

复制代码 代码如下:

  protected override void OnStart(string[] args)

  {

  Interop.ShowMessageBox("This a message from AlertService.", "AlertService Message");

  }

  编译程序后在服务管理器中重新启动AlertService 服务,从下图中可以看到消息窗口是在当前用户桌面显示的,而不是Session 0 中。

  

asp.net中穿透Session 0 隔离(二)

CreateProcessAsUser 函数

  如果想通过服务向桌面用户Session 创建一个复杂UI 程序界面,则需要使用CreateProcessAsUser 函数为用户创建一个新进程用来运行相应的程序。打开Interop 类继续添加下面代码:

  

复制代码 代码如下:

  public static void CreateProcess(string app, string path)

  {

  bool result;

  IntPtr hToken = WindowsIdentity.GetCurrent().Token;

  IntPtr hDupedToken = IntPtr.Zero;

  PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

  SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();

  sa.Length = Marshal.SizeOf(sa);

  STARTUPINFO si = new STARTUPINFO();

  si.cb = Marshal.SizeOf(si);

  int dwSessionID = WTSGetActiveConsoleSessionId();

  result = WTSQueryUserToken(dwSessionID, out hToken);

  if (!result)

  {

  ShowMessageBox("WTSQueryUserToken failed", "AlertService Message");

  }

  result = DuplicateTokenEx(

  hToken,

  GENERIC_ALL_ACCESS,

  ref sa,

  (int)SECURITY_IMPERSONATION_LEVEL.SecurityIdentification,

  (int)TOKEN_TYPE.TokenPrimary,

  ref hDupedToken

  );

  if (!result)

  {

  ShowMessageBox("DuplicateTokenEx failed" ,"AlertService Message");

  }

  IntPtr lpEnvironment = IntPtr.Zero;

  result = CreateEnvironmentBlock(out lpEnvironment, hDupedToken, false);

  if (!result)

  {

  ShowMessageBox("CreateEnvironmentBlock failed", "AlertService Message");

  }

  result = CreateProcessAsUser(

  hDupedToken,

  app,

  String.Empty,

  ref sa, ref sa,

  false, 0, IntPtr.Zero,

  path, ref si, ref pi);

  if (!result)

  {

  int error = Marshal.GetLastWin32Error();

  string message = String.Format("CreateProcessAsUser Error: {0}", error);

  ShowMessageBox(message, "AlertService Message");

  }

  if (pi.hProcess != IntPtr.Zero)

  CloseHandle(pi.hProcess);

  if (pi.hThread != IntPtr.Zero)

  CloseHandle(pi.hThread);

  if (hDupedToken != IntPtr.Zero)

  CloseHandle(hDupedToken);

  }

  [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

  }

  public enum TOKEN_TYPE

  {

  TokenPrimary = 1,

  TokenImpersonation

  }

  public const int GENERIC_ALL_ACCESS = 0x10000000;

  [DllImport("kernel32.dll", SetLastError = true,

  CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

  public static extern bool CloseHandle(IntPtr handle);

  [DllImport("advapi32.dll", SetLastError = true,

  CharSet = CharSet.Ansi, 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);

  [DllImport("advapi32.dll", SetLastError = true)]

  public static extern bool DuplicateTokenEx(

  IntPtr hExistingToken,

  Int32 dwDesiredAccess,

  ref SECURITY_ATTRIBUTES lpThreadAttributes,

  Int32 ImpersonationLevel,

  Int32 dwTokenType,

  ref IntPtr phNewToken);

  [DllImport("wtsapi32.dll", SetLastError=true)]

  public static extern bool WTSQueryUserToken(

  Int32 sessionId,

  out IntPtr Token);

  [DllImport("userenv.dll", SetLastError = true)]

  static extern bool CreateEnvironmentBlock(

  out IntPtr lpEnvironment,

  IntPtr hToken,

  bool bInherit);

  在CreateProcess 函数中同时也涉及到DuplicateTokenEx、WTSQueryUserToken、CreateEnvironmentBlock 函数的使用,有兴趣的朋友可通过MSDN 进行学习。完成CreateProcess 函数创建后,就可以真正的通过它来调用应用程序了,回到Service1.cs 修改一下OnStart 我们来打开一个CMD 窗口。如下代码:

  

复制代码 代码如下:

  protected override void OnStart(string[] args)

  {

  Interop.CreateProcess("cmd.exe",@"C:\Windows\System32\");

  }

  重新编译程序,启动AlertService 服务便可看到下图界面。至此,我们已经可以通过一些简单的方法对Session 0 隔离问题进行解决。大家也可以通过WCF 等技术完成一些更复杂的跨Session 通信方式,实现在Windows 7 及Vista 系统中服务与桌面用户的交互操作。

asp.net中穿透Session 0 隔离(二)

  参考资料

  1. WTSSendMessage Function

  http://msdn.microsoft.com/en-us/library/aa383842(VS.85).aspx

  2. CreateProcessAsUser Function

  http://msdn.microsoft.com/en-us/library/ms682429(v=VS.85).aspx

  3. WTSSendMessage (wtsapi32)

  http://www.pinvoke.net/default.aspx/wtsapi32/WTSSendMessage.html

  4. WTSQueryUserToken Function

  http://msdn.microsoft.com/en-us/library/aa383840(VS.85).aspx

  5. http://www.pinvoke.net/

  代码下载 AlertService2_.rar

  作者:李敬然(Gnie)

  出处:{GnieTech} (http://www.cnblogs.com/gnielee/)