在另一个会话中枚举用户桌面的Windows

时间:2014-03-06 18:13:50

标签: c# windows winapi

这里有一个简单的问题让我发疯。

我在C#中有一个Windows服务,它可以在XP,Vista和7上运行,并且能够枚举当前用户桌面的窗口(如果有的话)以进行监控。

到目前为止:

我使用EnumDesktopWindows传递IntPtr.Zero作为hdesktop参数,因为我没有用户桌面的句柄,只会枚举特殊桌面中存在的少数几个窗口分配给服务(Session0 \ Winsta0)

我尝试EnumWindows,结果与上面相同!

我尝试使用GetThreadDesktop API获取已知进程的桌面,传递一个explorer.exe的线程的id,但它返回0,所以我无法获取它的桌面或任何其他的。

我尝试使用OpenInputDesktop获取输入桌面,这显然会返回session0中的桌面而非桌面用户。

我该怎么办?!

如果你很好奇,我正在编写一个自助服务终端应用程序,它需要监控所有窗口并防止危险的应用程序管理器,Internet选项,Cmd以及用户不应该打开的任何内容。

欢迎任何建议。 :)

5 个答案:

答案 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 classSO 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上运行的进程访问。