vista和win7在windows服務(wù)中交互桌面權(quán)限問題解決方法:穿透Session 0 隔離
Windows 服務(wù)在后臺執(zhí)行著各種各樣任務(wù),支持著我們?nèi)粘5淖烂娌僮?。有時候可能需要服務(wù)與用戶進(jìn)行信息或界面交互操作,這種方式在XP 時代是沒有問題的,但自從Vista 開始你會發(fā)現(xiàn)這種方式似乎已不起作用。
Session 0 隔離實驗
下面來做一個名叫AlertService 的服務(wù),它的作用就是向用戶發(fā)出一個提示對話框,我們看看這個服務(wù)在Windows 7 中會發(fā)生什么情況。
using System.ServiceProcess;
using System.Windows.Forms;
namespace AlertService
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
MessageBox.Show("A message from AlertService.");
}
protected override void OnStop()
{
}
}
}
程序編譯后通過Installutil 將其加載到系統(tǒng)服務(wù)中:
在服務(wù)屬性中勾選“Allow service to interact with desktop” ,這樣可以使AlertService 與桌面用戶進(jìn)行交互。
在服務(wù)管理器中將AlertService 服務(wù)“啟動”,這時任務(wù)欄中會閃動一個圖標(biāo):
點擊該圖標(biāo)會顯示下面窗口,提示有個程序(AlertService)正在試圖顯示信息,是否需要瀏覽該信息:
嘗試點擊“View the message”,便會顯示下圖界面(其實這個界面我已經(jīng)不能從當(dāng)前桌面操作截圖了,是通過Virtual PC 截屏的,其原因請繼續(xù)閱讀)。注意觀察可以發(fā)現(xiàn)下圖的桌面背景已經(jīng)不是Windows 7 默認(rèn)的桌面背景了,說明AlertService 與桌面系統(tǒng)的Session 并不相同,這就是Session 0 隔離作用的結(jié)果。
Session 0 隔離原理
在Windows XP、Windows Server 2003 或早期Windows 系統(tǒng)時代,當(dāng)?shù)谝粋€用戶登錄系統(tǒng)后服務(wù)和應(yīng)用程序是在同一個Session 中運行的。這就是Session 0 如下圖所示:
但是這種運行方式提高了系統(tǒng)安全風(fēng)險,因為服務(wù)是通過提升了用戶權(quán)限運行的,而應(yīng)用程序往往是那些不具備管理員身份的普通用戶運行的,其中的危險顯而易見。
從Vista 開始Session 0 中只包含系統(tǒng)服務(wù),其他應(yīng)用程序則通過分離的Session 運行,將服務(wù)與應(yīng)用程序隔離提高系統(tǒng)的安全性。如下圖所示:
這樣使得Session 0 與其他Session 之間無法進(jìn)行交互,不能通過服務(wù)向桌面用戶彈出信息窗口、UI 窗口等信息。這也就是為什么剛才我說那個圖已經(jīng)不能通過當(dāng)前桌面進(jìn)行截圖了。
Session 檢查
在實際開發(fā)過程中,可以通過Process Explorer 檢查服務(wù)或程序處于哪個Session,會不會遇到Session 0 隔離問題。我們在Services 中找到之前加載的AlertService 服務(wù),右鍵屬性查看其Session 狀態(tài)。
可看到AlertService 處于Session 0 中:
再來看看Outlook 應(yīng)用程序:
很明顯在Windows 7 中服務(wù)和應(yīng)用程序是處于不同的Session,它們之間加隔了一個保護(hù)墻,在下篇文章中將介紹如何穿過這堵保護(hù)墻使服務(wù)與桌面用戶進(jìn)行交互操作。
如果在開發(fā)過程中確實需要服務(wù)與桌面用戶進(jìn)行交互,可以通過遠(yuǎn)程桌面服務(wù)的API 繞過Session 0 的隔離完成交互操作。
對于簡單的交互,服務(wù)可以通過WTSSendMessage 函數(shù),在用戶Session 上顯示消息窗口。對于一些復(fù)雜的UI 交互,必須調(diào)用CreateProcessAsUser或其他方法(WCF、.NET遠(yuǎn)程處理等)進(jìn)行跨Session 通信,在桌面用戶上創(chuàng)建一個應(yīng)用程序界面。
WTSSendMessage 函數(shù)
如果服務(wù)只是簡單的向桌面用戶Session 發(fā)送消息窗口,則可以使用WTSSendMessage 函數(shù)實現(xiàn)。首先,在上一篇下載的代碼中加入一個Interop.cs 類,并在類中加入如下代碼:
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 函數(shù)中調(diào)用了WTSSendMessage 來發(fā)送信息窗口,這樣我們就可以在Service 的OnStart 函數(shù)中使用,打開Service1.cs 加入下面代碼:
protected override void OnStart(string[] args)
{
Interop.ShowMessageBox("This a message from AlertService.",
"AlertService Message");
}
編譯程序后在服務(wù)管理器中重新啟動AlertService 服務(wù),從下圖中可以看到消息窗口是在當(dāng)前用戶桌面顯示的,而不是Session 0 中。
CreateProcessAsUser 函數(shù)
如果想通過服務(wù)向桌面用戶Session 創(chuàng)建一個復(fù)雜UI 程序界面,則需要使用CreateProcessAsUser 函數(shù)為用戶創(chuàng)建一個新進(jìn)程用來運行相應(yīng)的程序。打開Interop 類繼續(xù)添加下面代碼:
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 函數(shù)中同時也涉及到DuplicateTokenEx、WTSQueryUserToken、CreateEnvironmentBlock 函數(shù)的使用,有興趣的朋友可通過MSDN 進(jìn)行學(xué)習(xí)。完成CreateProcess 函數(shù)創(chuàng)建后,就可以真正的通過它來調(diào)用應(yīng)用程序了,回到Service1.cs 修改一下OnStart 我們來打開一個CMD 窗口。如下代碼:
protected override void OnStart(string[] args)
{
Interop.CreateProcess("cmd.exe",@"C:\Windows\System32\");
}
重新編譯程序,啟動AlertService 服務(wù)便可看到下圖界面。至此,我們已經(jīng)可以通過一些簡單的方法對Session 0 隔離問題進(jìn)行解決。大家也可以通過WCF 等技術(shù)完成一些更復(fù)雜的跨Session 通信方式,實現(xiàn)在Windows 7 及Vista 系統(tǒng)中服務(wù)與桌面用戶的交互操作。
上一篇:C#通過html調(diào)用WinForm的方法
欄 目:C#教程
本文標(biāo)題:vista和win7在windows服務(wù)中交互桌面權(quán)限問題解決方法:穿透Session 0 隔離
本文地址:http://www.jygsgssxh.com/a1/C_jiaocheng/6557.html
您可能感興趣的文章
- 01-10Extjs4如何處理后臺json數(shù)據(jù)中日期和時間
- 01-10C#實現(xiàn)將程序鎖定到Win7任務(wù)欄的方法
- 01-10C#及WPF獲取本機(jī)所有字體和顏色的方法
- 01-10同步調(diào)用和異步調(diào)用WebService
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量二
- 01-10C#編程自學(xué)之?dāng)?shù)據(jù)類型和變量三
- 01-10C#編程自學(xué)之運算符和表達(dá)式
- 01-10C#裝箱和拆箱原理詳解
- 01-10C#編程自學(xué)之類和對象
- 01-10C#編程和Visual Studio使用技巧(下)


閱讀排行
本欄相關(guān)
- 01-10C#通過反射獲取當(dāng)前工程中所有窗體并
- 01-10關(guān)于ASP網(wǎng)頁無法打開的解決方案
- 01-10WinForm限制窗體不能移到屏幕外的方法
- 01-10WinForm繪制圓角的方法
- 01-10C#實現(xiàn)txt定位指定行完整實例
- 01-10WinForm實現(xiàn)仿視頻播放器左下角滾動新
- 01-10C#停止線程的方法
- 01-10C#實現(xiàn)清空回收站的方法
- 01-10C#通過重寫Panel改變邊框顏色與寬度的
- 01-10C#實現(xiàn)讀取注冊表監(jiān)控當(dāng)前操作系統(tǒng)已
隨機(jī)閱讀
- 01-10C#中split用法實例總結(jié)
- 04-02jquery與jsp,用jquery
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10delphi制作wav文件的方法
- 08-05織夢dedecms什么時候用欄目交叉功能?


