C# - Windows Service Get the Current logged user Desktop directory path

生来就可爱ヽ(ⅴ<●) 提交于 2021-01-28 00:50:30

问题


I have a problem getting the Current windows logged user desktop folder when running windows service application under "Local System". when I try to use:

Environment.GetFolderPath(Environment.SpecialFolder.Desktop);

i get an empty string (I guess because i'm running the service under "Local System").

this is my OnStart function:

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);
}

Is there a way to get the current logged windows user path?

Thanks.


回答1:


Windows allows, even though not always by default, for zero to multiple users, to be logged on.

You need to call three functions:

1) Get (all) active session(s) with WTSEnumerateSessions. A nice example of calling in this question. You could use "localhost" as servername parameter.

2) Get Token(s) for (each) session with WTSQueryUserToken Should be straight-forward, don't forget memory management though.

3) Query SHGetKnownFolderPath with (each) token. (some relevant cutouts from 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)!

Gluing these three together is quite the little job and a lot of testing and memory management, left as an exercise to OP.

Watch out for caveats with 32bit/64bit registry issues when testing your solution.

Also, you should read this question for more info.




回答2:


You can use the following code from within the service to get special folder using any CSIDL. You should replace CSIDL_LOCAL_APPDATA with CSIDL_DESKTOPDIRECTORY for desktop directory.

Pinvoke.net can be looked up to import winapi methods in C#.

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;


来源:https://stackoverflow.com/questions/34992883/c-sharp-windows-service-get-the-current-logged-user-desktop-directory-path

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!