Get current user desktop path - when application is run as administrator lazarus / 34-Windows ---------------------------------------------------------------------- LazProgger · 2019-03-17 15:40 I am using the following function to get the path to the desktop of the user running the application: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses windows, shlobj, ActiveX, ComObj; function _GetSpecialFolder(const CSIDL: integer) : string;var    RecPath : PWideChar;begin    result := '';    RecPath := WideStrAlloc(MAX_PATH);    try      FillChar(RecPath^, MAX_PATH, 0);      if SHGetFolderPathW(0, CSIDL, 0, 0, RecPath) = 0 then result := RecPath;    finally      StrDispose(RecPath);    end;end; You can call it like that: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i APathToDesktop := _GetSpecialFolder(CSIDL_DESKTOPDIRECTORY); That's working fine. When the application is run as normal user. However, it's not working when run as administrator. Then the result is the desktop of the administrator, but I need the desktop of the current user running the current windows session. I have found out that you can use the third parameter of SHGetFolderPathW to path a user token id to get the right path. But how to find out that user id? I have tried approaches like: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, TokenResult) But again, TokenResult is always the administrator token (the current thread is run as administrator, that's right). How to get the token of the user that have started Windows and only clicked on "Run as Administrator"? ---------------------------------------------------------------------- ASBzone · 2019-03-18 03:00 Quote from: LazProgger on March 17, 2019, 04:40:57 pmThat's working fine. When the application is run as normal user. However, it's not working when run as administrator. Then the result is the desktop of the administrator, but I need the desktop of the current user running the current windows session. On Windows desktop editions, you could look for the other logged in user, but on server that would not necessarily be correct, as multiple users could be logged on. I'm not even sure what question to ask for this search...  But you can look at what users own the Explorer process.      (A search confirmed that someone else thinks this is a good idea!) https://stackoverflow.com/questions/5218778/how-do-i-get-the- currently-logged-username-from-a-windows-service-in-net ---------------------------------------------------------------------- LazProgger · 2019-03-18 13:10 I have tried to implement the explorer process idea, you have mentioned. In this case I have used the Shell_TrayWnd process which should be run by the user that has started the system: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TForm1.Button8Click(Sender: TObject);var  ATrayHandle: HWND;  ATrayThreadProcessID: DWORD;  ATrayProcessHandle: THandle;  AUserToken: THandle;   function _GetSpecialFolder(const CSIDL: integer; AUser: THANDLE) : string;  var      RecPath : PWideChar;  begin      result := '';      RecPath := WideStrAlloc(MAX_PATH);      try        FillChar(RecPath^, MAX_PATH, 0);        if SHGetFolderPathW(0, CSIDL, AUser, 0, RecPath) = 0 then result := RecPath;      finally        StrDispose(RecPath);      end;  end; begin   AUserToken := 0;   ATrayHandle := FindWindow('Shell_TrayWnd', nil);  if ATrayHandle <> 0 then begin     if GetWindowThreadProcessId(ATrayHandle, @ATrayThreadProcessID) <> 0 then begin        ATrayProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION, False, ATrayThreadProcessID);      if not OpenProcessToken(ATrayProcessHandle, TOKEN_QUERY, AUserToken) then begin          if GetLastError = ERROR_NO_TOKEN then begin             OpenProcessToken(ATrayHandle, TOKEN_QUERY, AUserToken);          end;        end;     end;  end;      ShowMessage(_GetSpecialFolder(CSIDL_DESKTOPDIRECTORY, AUserToken)); end; In my first tests, this code is working for both situations: If the program is run or not run as administrator. However, this solution seems to be not very clean for me. Okay, you might think there is always a Shell_TrayWnd process, but how reliable is this across different system versions and in future? What do you think? And do you have ideas to improve that code? ---------------------------------------------------------------------- ASerge · 2019-03-18 17:16 Quote from: LazProgger on March 18, 2019, 02:10:54 pmAnd do you have ideas to improve that code? Use GetShellWindow. And what about checking the result and closing the handles? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i uses Windows, ShlObj; function GetShellWindow: HWND; stdcall; external user32; procedure TForm1.Button1Click(Sender: TObject);   function GetSpecialFolder(const CSIDL: LongInt; AUser: THandle): string;  var  Buffer: array[0..MAX_PATH] of WideChar;  begin    Result := '';    Buffer[0] := #0; // To skip compiler warning    if SHGetFolderPathW(0, CSIDL, AUser, SHGFP_TYPE_CURRENT, Buffer) = S_OK then      Result := Buffer;  end; var  ShellWindow: HWND;  ShellProcessId: DWORD;  ShellProcessHandle: THandle;  ShellUserToken: THandle;begin  ShellUserToken := 0;  ShellWindow := GetShellWindow;  if ShellWindow <> 0 then  begin    GetWindowThreadProcessId(ShellWindow, @ShellProcessId);    ShellProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION, False, ShellProcessId);    if ShellProcessHandle <> 0 then    begin      if not OpenProcessToken(ShellProcessHandle, TOKEN_QUERY, @ShellUserToken) then        ShellUserToken := 0;      CloseHandle(ShellProcessHandle);    end;  end;  Caption := GetSpecialFolder(CSIDL_DESKTOPDIRECTORY, ShellUserToken);  if ShellUserToken <> 0 then    CloseHandle(ShellUserToken);end; ---------------------------------------------------------------------- LazProgger · 2019-03-18 19:04 Thank you very much for the code improvement. Oh, I did not know that you have to close the handles. Is that always necessary when working with handles? And is there a reason for changing dwflags from 0 to SHGFP_TYPE_CURRENT? I have tested both versions with or without that flag and it is producing the same results. And a last question: When GetShellWindow is defined in user32. Could there be problems when using that code in a 64 bit environment or a 64 bit application? ---------------------------------------------------------------------- ASerge · 2019-03-18 20:13 Quote from: LazProgger on March 18, 2019, 08:04:14 pmOh, I did not know that you have to close the handles. Is that always necessary when working with handles? Yes, to exclude resource leak. QuoteAnd is there a reason for changing dwflags from 0 to SHGFP_TYPE_CURRENT? I have tested both versions with or without that flag and it is producing the same results. It's equal. Just so more clearly expressed, you need the current, may be the redirected location, not the "default". QuoteAnd a last question: When GetShellWindow is defined in user32. Could there be problems when using that code in a 64 bit environment or a 64 bit application? According to the documentation, at least for Windows 2000. I tested this on 64-bit Windows.