[英]How to set “interact with desktop” in windows service installer
我有一個Windows服務,在系統帳戶下運行,並不時執行一些程序( 是的,是的,我知道這是一個不好的做法,但這不是我的決定 )。 我需要設置“與桌面交互”檢查,以便在安裝服務后查看已執行程序的gui。 我嘗試了幾種方法,將下面的代碼放在我的服務安裝程序的AfterInstall或OnCommited事件處理程序中:
ConnectionOptions coOptions = new ConnectionOptions();
coOptions.Impersonation = ImpersonationLevel.Impersonate;
ManagementScope mgmtScope = new System.Management.ManagementScope(@"root\CIMV2", coOptions);
mgmtScope.Connect();
ManagementObject wmiService = new ManagementObject("Win32_Service.Name='" + ServiceMonitorInstaller.ServiceName + "'");
ManagementBaseObject InParam = wmiService.GetMethodParameters("Change");
InParam["DesktopInteract"] = true;
ManagementBaseObject OutParam = wmiService.InvokeMethod("Change", InParam, null);
要么
RegistryKey ckey = Registry.LocalMachine.OpenSubKey(
@"SYSTEM\CurrentControlSet\Services\WindowsService1", true);
if(ckey != null)
{
if(ckey.GetValue("Type") != null)
{
ckey.SetValue("Type", ((int)ckey.GetValue("Type") | 256));
}
}
這兩種方法都“有效”。 他們設置了支票,但是在我啟動服務之后啟動了exe - 並且沒有顯示gui! 所以,如果我停止服務,重新檢查並再次啟動它 - 賓果游戲! 一切都開始並顯示出來。 實現結果的第二種方法是重新啟動 - 之后還會顯示gui。
所以問題是:是否有正確的方法來設置“與桌面交互”檢查,所以它將開始工作而無需重新檢查和重新啟動?
操作系統:Windows XP(尚未試過Vista和7 ......)
private static void SetInterActWithDeskTop()
{
var service = new System.Management.ManagementObject(
String.Format("WIN32_Service.Name='{0}'", "YourServiceName"));
try
{
var paramList = new object[11];
paramList[5] = true;
service.InvokeMethod("Change", paramList);
}
finally
{
service.Dispose();
}
}
與Heisa相同,但與WMI相同。 (代碼是Powershell,但可以輕松移植到C#)
if ($svc = gwmi win32_service|?{$_.name -eq $svcname})
{
try {
$null = $svc.change($svc.displayname,$svc.pathname,16,1,`
"Manual",$false,$svc.startname,$null,$null,$null,$null)
write-host "Change made"
catch { throw "Error: $_" }
} else
{ throw "Service $svcname not installed" }
有關參數描述,請參閱MSDN:Service Change()方法 。
最后在互聯網上搜索了一周之后 - 我找到了一個很棒的工作解決方案: http : //asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html
找到要啟動的桌面。 這可能看起來很滑稽,但並不像看起來那么簡單。 通過終端服務和快速用戶切換,可以有多個交互式用戶同時登錄到計算機。 如果您想要當前位於物理控制台的用戶,那么您很幸運,終端服務API調用WTSGetActiveConsoleSessionId將為您提供所需的會話ID。 如果您的需求更復雜(即您需要與TS服務器上的特定用戶進行交互,或者您需要在非交互式會話中使用窗口站的名稱),則需要使用WTSEnumerateSessions枚舉終端服務器會話並檢查使用WTSGetSessionInformation獲取所需信息的會話。
現在您知道需要與哪個會話進行交互,並且您擁有其ID。 這是整個過程的關鍵,使用WTSQueryUserToken和會話ID,您現在可以檢索登錄到目標會話的用戶的令牌。 這完全緩解了“與桌面交互”設置的安全問題,啟動的進程將不會使用LOCAL SYSTEM憑據運行,而是使用與已登錄到該會話的用戶相同的憑據! 沒有特權提升。
使用CreateProcessAsUser和我們檢索到的令牌,我們可以以正常方式啟動進程,它將使用目標用戶的憑據在目標會話中運行。 有幾點需要注意,lpCurrentDirectory和lpEnvironment都必須指向有效值 - 這些參數的正常默認解析方法不適用於跨會話啟動。 您可以使用CreateEnvironmentBlock為目標用戶創建默認環境塊。
附有工作項目的源代碼。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.