SHGetKnownFolderPath equivalent API in Linux
The status of the Linux desktop is a sad mess, especially for this kind of stuff. There are some FreeDesktop standards, but nothing close to Windows Shell APIs for both consistency and commitment to backwards compatibility.
That being said, in my experience the safest thing to do is relying on utilities from XDG, in particular xdg-user-dir
in your case (xdg utils can come in handy in other cases); nowadays they are almost universally installed, and they usually do the "right" thing. Also, many packages rely on them, so it's likely that any DE tests the integration at least on them (IOW: if someone writes configuration that breaks even xdg utils it's likely that they'll fix it; if it's out of spec garbage that happens to work with xdg utils they may not notice).
Please don't try to be too clever with this kind of stuff. Firefox tries to be clever with file associations on Linux and it's 10 years that clicking "open folder" in it opens random applications (for the record, on my box it's extremely fond of opening Gwenview; in the past it liked more FileLight).
How to get known paths for linux
Linux is an operating system kernel. It does not have a concept of user directories.
There are several Linux distributions. The filesystem structure is determined by the distro. Most distros conform to POSIX standard, and follow (to varying degree) the Filesystem Hierarchy Standard by Linux Foundation, which is similar to the directory structures of other UNIX like systems. That said, distributions generally allow the user to use the file system in unconventional configurations. For example, they don't typically force users home directory to be under /home
.
POSIX specifies a few environment variables that are relevant to this context:
HOME
The system shall initialize this variable at the time of login to be a pathname of the user's home directory.
TMPDIR
This variable shall represent a pathname of a directory made available for programs that need a place to create temporary files.
Environment variables can be accessed using std::getenv
in C++.
On desktop systems, the directory structure is also determined to some degree by the desktop environment, of which there are several available. freedesktop.org produces unofficial specifications for interoperability of different desktop environments. On DE's conforming to XDG Base Directory Specification should following environment variables be available:
$XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored. If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used.
$XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored. If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used.
$XDG_DATA_DIRS defines the preference-ordered set of base directories to search for data files in addition to the $XDG_DATA_HOME base directory. The directories in $XDG_DATA_DIRS should be seperated with a colon ':'.
If $XDG_DATA_DIRS is either not set or empty, a value equal to /usr/local/share/:/usr/share/ should be used.
freedesktop.org also provides a utility xdg-user-dirs:
xdg-user-dirs is a tool to help manage "well known" user directories like the desktop folder and the music folder. It also handles localization (i.e. translation) of the filenames.
$(XDG_CONFIG_HOME)/user-dirs.dirs specifies the current set of directories for the user. This file is in a shell format, so its easy to access from a shell script. This file can also be modified by users (manually or via applications) to change the directories used.
So, in case of FOLDERID_RoamingAppData
, you should probably use one of $XDG_x
depending on the use case, falling back to the appropriate default relative to $HOME
as specified.
Error 0x80070005 from SHGetKnownFolderPath after starting process though service
your error that you not pass lpEnvironment
parameter to CreateProcess
call.
lpEnvironment [in, optional]
A pointer to the environment block for the new process. If this
parameter is NULL, the new process uses the environment of the
calling process.
as result child process uses the environment of the calling process. what effect this give to FOLDERID_RoamingAppData
? during resolve FOLDERID_RoamingAppData
- we usually got %USERPROFILE%\AppData\Roaming
- and final result will be depend from value of %USERPROFILE%
. in your case - it will be inherited from service (LocalSystem
) and will be look like C:\WINDOWS\system32\config\systemprofile
- so final path must look like C:\WINDOWS\system32\config\systemprofile\AppData\Roaming
. before return this path to caller - system check are path exist - call GetFileAttributes
. and here it fail (due security permission) - last error - 5 - ERROR_ACCESS_DENIED
. and api final return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)
which is 80070005
.
conclusion - in call CreateProcessAsUser
use pointer to environment block returned by CreateEnvironmentBlock
. and free this block later by DestroyEnvironmentBlock
. also not forget CREATE_UNICODE_ENVIRONMENT
flag in call CreateProcessAsUser
also your error can easy be reproduced in local (not elevated) process by next code:
SetEnvironmentVariableW(L"USERPROFILE", L"C:\\WINDOWS\\system32\\config\\systemprofile");
PWSTR appdate;
HRESULT result;
if ((result = SHGetKnownFolderPath(
FOLDERID_RoamingAppData,
NULL,
NULL,
&appdate)) == S_OK)
{
CoTaskMemFree(appdate);
}
if we will be set invalid path in USERPROFILE
- we got 80070003
error - HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)
How to open a folder in %appdata% with C++?
For maximum compatibility with all versions of Windows, you can use the SHGetFolderPath
function.
It requires that you specify the CSIDL value for the folder whose path you want to retrieve. For the application data folder, that would be CSIDL_APPDATA
.
On Windows Vista and later, you should use the SHGetKnownFolderPath
function instead, which requires that you specify the folder's KNOWNFOLDERID
value. Again, for the application data folder, the appropriate value is FOLDERID_RoamingAppData
.
To use either of these functions from your C++ application, you'll need to include shlobj.h
.
What is the filepath difference between window and linux in python3?
It's not different in python 3 in linux
it's different in linux
. Generally speaking *nix
file paths use /
as a directory separator, where as windows uses \
(for what ever reason).
In python 3 you can use the pathlib.Path
to abstract your code from the OS. So you can do something like
open(Path(f"~/{username}.txt"), "w")
The tilde ~
refers to a user's home directory. Python will figure out which file system the code is running on and do the right thing to map directory separators. You could also do
open(Path(f"/users/{username}.txt"), "w")
to address a specific user directory, the /
refers to the root of the file system and should work on Linux and Windows (although I haven't tested that).
https://docs.python.org/3/library/pathlib.html?highlight=pathlib%20path#module-pathlib
Is there a localization-safe way to get the SYSTEM account's AppData directory?
You need a process running under the SYSTEM
account in order to query the SYSTEM
profile.
Create a separate service that runs as SYSTEM
. When needed, have it call SHGetFolderPath(CSIDL_LOCAL_APPDATA)
or SHGetKnownFolderPath(FOLDERID_LocalAppData)
, specifying NULL
for the user token so that it queries the calling user's (ie SYSTEM
) profile (or, just use GetTempPath()
, which will also work in this context).
Then, the service can pass that path back to your main app through any session-agnostic IPC mechanism that you want (pipes, sockets, etc).
On a side note:
you do not need to type-cast
temp_path
when assigning it toinput_parameter
.temp_path
will implicitly decay into awchar_t*
pointer, which can be passed as-is to theconst wchar_t*
pointer thatstd::wstring::operator=()
takes as input.rather than using
input_parameter.append(L"\\TEMP")
manually (which you would not need at all if you useGetTempPath()
), you should consider usingPathCchCombineEx()
(or related) orSHGetFolderPathAndSubDir()
instead. Let the OS handle the appending for you.you are calling
CloseHandle()
with anHWND
as input, which is wrong since it expects aHANDLE
instead (but this is a no-op in your case since theHWND
is NULL, so just remove theCloseHandle()
altogether). Clearly you are not compiling your project with STRICT Type Checking enabled, otherwise the compiler would have prevented anHWND
from being passed to aHANDLE
parameter.
AppData / Similar for all OS : C++?
If you happen to write a Qt application, there is the QSettings Class
. The documentation says the following about this class:
The QSettings class provides persistent platform-independent application settings.
Users normally expect an application to remember its settings (window sizes and positions, options, etc.) across sessions. This information is often stored in the system registry on Windows, and in XML preferences files on Mac OS X. On Unix systems, in the absence of a standard, many applications (including the KDE applications) use INI text files.
This delivers IMHO the best "out-of-the-box" experience. And it's really platform independent.
An alternative would be boost::program_options
or boost::property_tree
. But the aim of these libraries is the data handling, not so much the storage. This means you would still need to detect the platform, and store the data in the correct location.
Related Topics
How to Measure CPU Time of a Specific Set of Threads
C++ Delete Vector, Objects, Free Memory
Windows 7 Timing Functions - How to Use Getsystemtimeadjustment Correctly
C++ Program Converts Fahrenheit to Celsius
Force Gcc to Notify About Undefined References in Shared Libraries
Is the Behaviour of I = I++ Really Undefined
Why in the Code "456"+1, Output Is "56"
Passing a Std::Array of Unknown Size to a Function
Stl::Multimap - How to Get Groups of Data
How to Avoid Multiple Definition Linking Error
Find Out If String Ends with Another String in C++
Is Std::Cout Guaranteed to Be Initialized
When Do We Have to Use Copy Constructors
Convert Float to String with Precision & Number of Decimal Digits Specified