|
上一篇 我们已经对Session 0 隔离有了进一步认识,如果在开发过程中确实需要服务与桌面用户进行交互,可以通过远程桌面服务的API 绕过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 中。 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 系统中服务与桌面用户的交互操作。 参考资料
代码下载:
作者:李敬然(Gnie)
出处:{GnieTech} (http://www.cnblogs.com/gnielee/)
版权声明:本文的版权归作者与博客园共有。转载时须注明本文的详细链接,否则作者将保留追究其法律责任。
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?用户注册
×
|