2016-01-25 102 views
2

我有一個問題,當在「本地系統」下運行Windows服務應用程序時,獲取當前窗口記錄的用戶桌面文件夾。 當我嘗試使用:C# - 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用戶的路徑?

謝謝。

+1

如果沒有用戶登錄或多個用戶該怎麼辦? – stuartd

+0

好吧,我明白你的觀點。所以我必須在這個硬編碼上使用? – Tal

回答

3

即使並非總是默認情況下,Windows允許零登錄到多個用戶。

你需要調用三個功能:與WTSEnumerateSessions

1)獲取(所有)活動會話(S)。 撥打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問題了解更多信息。

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;