C# - Windows 服务 获取当前登录的用户桌面目录路径

本文关键字:用户 桌面 路径 登录 Windows 服务 获取 | 更新日期: 2023-09-27 18:31:50

在"本地系统"下运行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);
}

有没有办法获取当前记录的窗口用户路径?

谢谢。

C# - Windows 服务 获取当前登录的用户桌面目录路径

Windows 允许零到多个用户登录,即使默认情况下并非总是如此。

您需要调用三个函数:

1) 使用 WTSEnumerateSession 获取(所有)活动会话。这个问题的一个很好的例子。您可以使用"本地主机"作为服务器名称参数。

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 的练习。

在测试解决方案时,请注意 32 位/64 位注册表问题的注意事项。

另外,您应该阅读此问题以获取更多信息。

可以使用服务中的以下代码,通过任何 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;