这里有一个简单的问题让我发疯。
我在C#中有一个Windows服务,它可以在XP,Vista和7上运行,并且能够枚举当前用户桌面的窗口(如果有的话)以进行监控。
到目前为止:
我使用EnumDesktopWindows
传递IntPtr.Zero
作为hdesktop
参数,因为我没有用户桌面的句柄,只会枚举特殊桌面中存在的少数几个窗口分配给服务(Session0 \ Winsta0)
我尝试EnumWindows
,结果与上面相同!
我尝试使用GetThreadDesktop
API获取已知进程的桌面,传递一个explorer.exe的线程的id,但它返回0,所以我无法获取它的桌面或任何其他的。
我尝试使用OpenInputDesktop
获取输入桌面,这显然会返回session0中的桌面而非桌面用户。
我该怎么办?!
如果你很好奇,我正在编写一个自助服务终端应用程序,它需要监控所有窗口并防止危险的应用程序管理器,Internet选项,Cmd以及用户不应该打开的任何内容。
欢迎任何建议。 :)
答案 0 :(得分:1)
您无法毫无例外地在另一个会话中枚举窗口。另一方面,如果您具有“作为操作系统的一部分”(SeTcbPrivilege
)权限,则可以在另一个会话中创建一个流程。
请参阅Launching a process in user’s session from a service了解如何做到这一点。
最终可以使用两个进程,一个作为NT服务运行的控制器,以及在用户会话中运行的代理程序。这两个进程可以通过命名管道进行通信,如果用户终止,控制器将重新启动代理。
但是,您还应该使用组策略或其他配置来锁定客户端,以防止其他窗口首先打开。具体来说,Software Restriction Policies将允许您阻止非白名单可执行文件的运行。
如果您使用的是Windows 8.1,还可以使用新推出的Kiosk Mode。
答案 1 :(得分:0)
尝试EnumDesktops功能。然后使用OpenDesktop功能打开桌面 另请参阅:Sample class,SO question (VB.NET)
答案 2 :(得分:0)
不确定这是否有用,但听起来您正试图从在会话0下运行的Windows服务执行此操作。从Windows 7开始,会话0是补丁,以防止服务与桌面交互而无需特殊操作允许。我认为在Windows Server 2008上,根本不允许这样做。
如果您尝试创建服务,请搜索会话0安全问题。这是关于该主题的链接: http://blogs.windows.com/windows/archive/b/developers/archive/2009/10/01/session-0-isolation.aspx
答案 3 :(得分:0)
在Vista及以上版本中,你不能这样做 - 服务在session 0中运行,你无法获得window stations or desktops from other sessions - 特别是:
创建窗口工作站时,它与调用相关联 过程并分配到当前会话。
因此,为了满足您的需求,您需要枚举特定会话的窗口站和桌面。
但是,虽然您可以通过WTSGetActiveConsoleSessionId
获取互动会话ID,但由于EnumWindowStations
没有获取会话ID,而EnumDesktops
需要有效的窗口站处理。
答案 4 :(得分:-1)
您可以通过从服务流程开始通信来实现这一目标。
创建服务的代码必须广播一些系统范围的事件,互斥等。
你可以这样做:
HANDLE hToken = NULL;
if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
printf("OpenProcessToken() error %u\n", GetLastError());
return false;
}
if(!SetPrivilege(hToken, "SeTcbPrivilege", TRUE))
return false;
SECURITY_ATTRIBUTES sa;
char *sdd = "D:"
"(D;OICI;GA;;;BG)" //Deny guests
"(D;OICI;GA;;;AN)" //Deny anonymous
"(A;OICI;GRGWGX;;;AU)" //Allow read, write and execute for Users
"(A;OICI;GA;;;BA)"; //Allow all for Administrators
ConvertStringSecurityDescriptorToSecurityDescriptor(sdd, SDDL_REVISION_1, &sa->lpSecurityDescriptor, NULL);
获得此安全描述符后,在命名事件或互斥锁上使用它。 这样,特定的内核对象就可以被不在会话0上运行的进程访问。