以Windows系统服务得到活动用户的用户名、UserProfile与环境变量

来源:互联网 发布:linux修改ssh端口号 编辑:程序博客网 时间:2024/06/02 12:52

在之前的一篇博文中(http://blog.csdn.net/nirendao/article/details/51194003),介绍了如何使用QT写一个Windows下的Service. 这篇文章试图在此基础上,解决一个问题:如何在这个系统级别的Windows service里面获得活动用户的一些信息,比如用户名,环境变量。本篇文章并未完全解决这些问题,因为很可能是基本没有什么好的方法的。

首先,要明了Windows系统的一些特性,比如Fast User Switching(https://msdn.microsoft.com/en-us/library/windows/desktop/bb776893(v=vs.85).aspx),它指的是在Windows XP之前版本的Windows OS,一个用户要登录,就必须等待其他用户登出(Log off);但是自从Windows XP开始,就可以做到:一个用户在使用的时候,并不需要登出(Log off),而只是切换用户,就可以允许其他用户登录了。这就是Fast User Switching. 另外,也允许真正的多个远程用户登录了(以前只是Windows 2003 Server版才可以). 这里要注意的是:虽然不需要用户登出(Log off),但是能看到桌面的在同一时刻只会有一个用户。比如远程登录桌面,远程者如果看到桌面,需要现有的桌面拥有者允许并退出桌面(不是Log off,只是类似切换用户)。

虽然有Fast User Switching这样的特性,允许多个用户同时登录,但是很可能依然有活动用户的概念(Active User),即看到桌面的那个用户。–这仅仅是笔者的猜测,并未在网上找到相应的文档。那么如何在一个系统级别的进程(如system level service)中得到active user的用户名和环境变量呢?本文分步骤来探讨这个问题。

第一步是如何得到active user的用户名。笔者做了以下的尝试,最终能够实现目标。

尝试-1:
使用QT中的qgetenv()方法获取环境变量%UserName%

qInfo() << "User Name = " << qgetenv("UserName");

可惜这样得到的结果是: hostname$, 如”FINIX-LAPTOP$”. 很明显,这样得到的是和系统相关的信息,而不是特定用户的用户名。

尝试-2:
使用Windows API GetUserName()来得到环境变量%UserName%

wchar_t acUserName[200];DWORD nUserName = sizeof(acUserName);if (GetUserName(acUserName, &nUserName)){    // Translate wchar_t * to QString    qInfo() << "Windows User Name = " << QString::fromWCharArray(acUserName);}

得到的结果是”SYSTEM”. 很显然,仍然认为自己是系统进程,而没有去取活动用户的环境变量%UserName%.

尝试-3:
先去获得活动控制台的session id,然后再去获得相关信息,如用户名。

DWORD sessionId = WTSGetActiveConsoleSessionId();qInfo() << "session id = " << sessionId;wchar_t* ppBuffer[100];DWORD bufferSize;WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, sessionId, WTSUserName, ppBuffer, &bufferSize);

这次尝试成功了。成功地获取到了Active User的用户名。

在第一步成功获得active user的用户名之后,第二步就是:
我们要试图来获得active用户的Home Location,或者也称为%UserProfile%,即一般为C:\Users\

尝试-4:

QStringList homePath = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);qInfo() << homePath.first().split(QDir::separator()).last();

这里得到的结果是: “C:/Windows/system32/config/systemprofile”,这其实是系统级别的%UserProfile%.

尝试-5:

wchar_t envVar[] = L"UserProfile";wchar_t result[100];GetEnvironmentVariable(envVar, result, sizeof(result)/sizeof(wchar_t));qInfo() << "USER PROFILE = " << QString::fromWCharArray(result);

得到的依然是: “C:/Windows/system32/config/systemprofile”.

尝试-6:
同尝试-3. 但是对WTSQuerySessionInformation()使用不同的参数,比如WTSWorkingDirectory或WTSClientDirectory. 然而,得到都是空字符串。

其实我最终想做到的是在此系统级别service中得到某个指定用户或active用户的环境变量,而这里的尝试-5正是调用了Windows获取环境变量的API,可惜获取的仍是系统级别的环境变量,而不是当前活动用户的环境变量。所以这一部分尝试是失败了。

最终的解决方法是这样的:
先用WTSQueryUserToken()获得给定session的登入用户的token,然后将token传给CreateEnvironmentBlock()以获取该用户的环境信息。最后,就可以根据CreateEnvironmentBlock()的输出参数lpEnvironment来获取环境变量了;也可以调用CreateProcessAsUser()来以该用户的身份启动某进程。

最后,我做了一个实验,给这个Windows service添加了一个新功能,就是监控用户的登入、登出、lock、unlock的事件,当有登入或unlock事件发生的时候,在log里打印出当前active user的user name. 实验显示,打印的用户名是正确的。
本文的实验代码如下:
https://github.com/FinixLei/QtProjects/tree/master/TestWindowsService

1 0