Shgetknownfolderpath Equivalent API in Linux

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 to input_parameter. temp_path will implicitly decay into a wchar_t* pointer, which can be passed as-is to the const wchar_t* pointer that std::wstring::operator=() takes as input.

  • rather than using input_parameter.append(L"\\TEMP") manually (which you would not need at all if you use GetTempPath()), you should consider using PathCchCombineEx() (or related) or SHGetFolderPathAndSubDir() instead. Let the OS handle the appending for you.

  • you are calling CloseHandle() with an HWND as input, which is wrong since it expects a HANDLE instead (but this is a no-op in your case since the HWND is NULL, so just remove the CloseHandle() altogether). Clearly you are not compiling your project with STRICT Type Checking enabled, otherwise the compiler would have prevented an HWND from being passed to a HANDLE 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



Leave a reply



Submit