在“本地系统”下运行Windows服务应用程序时,我在获取当前Windows登录用户桌面文件夹时遇到问题。 当我尝试使用时:
Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
我得到一个空字符串(我猜是因为我在“本地系统”下运行该服务)。
这是我的OnStart功能:
protected override void OnStart(string[] args)
{
System.Diagnostics.Debugger.Launch();
//Get the current user desktop path;
string path = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
string filter = "*.*";
// create the watcher.
FileSystemWatcher watcher = new FileSystemWatcher(path, filter)
{
EnableRaisingEvents = true,
IncludeSubdirectories = true
};
//listen to the change event;
watcher.Changed += watcher_Changed;
//Thread.Sleep(Timeout.Infinite);
}
有没有办法获取当前记录的Windows用户路径?
感谢。
答案 0 :(得分:3)
即使并非总是默认情况下,Windows允许登录多个用户。
您需要调用三个函数:
1)使用WTSEnumerateSessions获取(所有)活动会话。 在this question中调用的一个很好的例子。您可以使用“localhost”作为servername参数。
2)获取WTSQueryUserToken(每个)会话的令牌 应该是直截了当的,不要忘记内存管理。
3)使用(每个)令牌查询SHGetKnownFolderPath。 (来自pinvoke.net的一些相关剪贴画):
public static readonly Guid Desktop = new Guid( "B4BFCC3A-DB2C-424C-B029-7FE99A87C641" );
public static readonly Guid PublicDesktop = new Guid( "C4AA340D-F20F-4863-AFEF-F87EF2E6BA25" );
IntPtr token = AllWTSQueryUserTokens().First(); // <-- Your implementation
IntPtr pPath;
if ( SHGetKnownFolderPath(PublicDesktop, 0, token, out pPath ) == 0 )
{
string s = System.Runtime.InteropServices.Marshal.PtrToStringUni( pPath );
System.Runtime.InteropServices.Marshal.FreeCoTaskMem( pPath );
// s now contains the path for the all-users "Public Desktop" folder
}
// Release memory (token)!
将这三者结合在一起是一项非常重要的工作,并且需要进行大量的测试和内存管理,并将其作为OP的练习。
在测试解决方案时请注意caveats with 32bit/64bit registry issues。
此外,您应该阅读this问题以获取更多信息。
答案 1 :(得分:0)
您可以使用服务中的以下代码来使用任何CSIDL获取特殊文件夹。对于桌面目录,您应该将CSIDL_LOCAL_APPDATA
替换为CSIDL_DESKTOPDIRECTORY
。
Pinvoke.net以在C#中导入winapi方法。
public static String GetUserPath()
{
var hUserToken = IntPtr.Zero;
IntPtr pidlist = IntPtr.Zero;
StringBuilder sb = new StringBuilder(MAX_PATH);
GetSessionUserToken(ref hUserToken);
SHGetFolderLocation(IntPtr.Zero, CSIDL_LOCAL_APPDATA, hUserToken, 0, out pidlist);
SHGetPathFromIDListW(pidlist, sb);
return sb.ToString();
}
private static bool GetSessionUserToken(ref IntPtr phUserToken)
{
var bResult = false;
var hImpersonationToken = IntPtr.Zero;
var activeSessionId = INVALID_SESSION_ID;
var pSessionInfo = IntPtr.Zero;
var sessionCount = 0;
// Get a handle to the user access token for the current active session.
if (WTSEnumerateSessions(WTS_CURRENT_SERVER_HANDLE, 0, 1, ref pSessionInfo, ref sessionCount) != 0)
{
var arrayElementSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
var current = pSessionInfo;
for (var i = 0; i < sessionCount; i++)
{
var si = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
//current = new IntPtr(current.ToInt64() + arrayElementSize);
current = (IntPtr)((long)current + arrayElementSize); // should be same as above line
if (si.State == WTS_CONNECTSTATE_CLASS.WTSActive)
{
activeSessionId = si.SessionID;
}
}
}
// If enumerating did not work, fall back to the old method
if (activeSessionId == INVALID_SESSION_ID)
{
activeSessionId = WTSGetActiveConsoleSessionId();
}
SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
sa.nLength = Marshal.SizeOf(sa);
if (WTSQueryUserToken(activeSessionId, ref hImpersonationToken) != 0)
{
// Convert the impersonation token to a primary token
bResult = DuplicateTokenEx(
hImpersonationToken,
0,
ref sa,//IntPtr.Zero,
(int)SECURITY_IMPERSONATION_LEVEL.SecurityDelegation,
(int)TOKEN_TYPE.TokenPrimary,
ref phUserToken);
CloseHandle(hImpersonationToken);
}
return bResult;
}
private const int CSIDL_LOCAL_APPDATA = 0x001c;
private const int MAX_PATH = 260;