How to get paths to user desktops from a service? - c++

I need to enumerate paths to desktop folders for all users on a local Windows system from a service application. The catch is that some users may not be logged in at the time. So is there any API, or some other method to do that?
EDIT1: For those who didn't read the first paragraph let me repeat -- I do not have a token to a user account, a user may not be logged in at the time.
EDIT2: I need this to remove a shortcut from user desktops before a feature supported by my application is turned off, or the app is uninstalled.

Here's the best method I was able to come up so far. So I'd appreciate if someone with a non-English installation of Windows could check this for me?
(I'm posting just a pseudo-code for brevity.)
1 - All of the steps below are done from a local service application.
2 - To get paths to all user profiles enumerate subfolders in the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList key and read ProfileImagePath value from them. It will give profile paths for each user account.
3 - To get the folder name for the desktop, read the Desktop value from the HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders key from the service app (yes, I know HKCU sounds strange, but it worked for me.) After that get the path for the %USERPROFILE% environment variable, using environment variable APIs. So you'll get two paths, something like this:
C:\Windows\system32\config\systemprofile\Desktop
and
C:\Windows\system32\config\systemprofile
Then simply isolate the 2nd string from the 1st, using case-insensitive comparison, and you'll get the folder name for the desktop, (i.e. "\Desktop" in this case.)
4 - Add the desktop folder to the path obtained in step 2 to produce the full path to that user's desktop folder.

From userenv.h : GetProfilesDirectory (win2k+, iirc)
msdn.microsoft.com/en-us/library/windows/desktop/bb762278(v=vs.85).aspx
Use this to find the directory containing all the local user account profile directories.
To find the desktop folder:
Shlobj.h : SHGetKnownFolderPath (vista+)
msdn.microsoft.com/en-us/library/windows/desktop/bb762188(v=vs.85).aspx
You'll need FOLDERID_Desktop as the known-folder id. This symbol is declared in Knownfolders.h
msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx
Using these, you might get closer to what you want without directly reading the registry. Of course this belies any elegant way of enumerating the users aside from inferring from the folder list within the
GetProfilesDirectory. Perhaps there is a more api appropriate way to enumerate the local users.
Secondly, and this depends on the purpose of your service, but perhaps by working with the 'allusers' profile, instead of each individually you won't need to know the current list of local users.

Related

Where can I put my SQLite database in my QT application if I can't put it in my resources?

Recently I was trying to put a SQLite database into a QT 5 application I'm writing. I want it to be universally accessible - that is on all systems regardless of where it's installed. I put it as a resource then found out that evidently you can't put databases in resources as the string for the database path passed to setDatabaseName doesn't get translated to the resource system so the database can't be found.
So where can I put it? I don't want to just put it at the root of the drive like C:\repo.db or D:\repo.db as many people hate files cluttering their root directories (like me). I was going to put it just in the source folder and access it as "repo.db" or as I tried "./resources/database/repo.db" but even QFile doesn't see that. Where can I put it and how to access it there? My settings file was going to be in my resources but I wasn't sure if I could update the file then. I need a place that is available from the moment the application is installed on any system including my own so that it can be accessed both while coding it and when it's built.
I'm not asking for opinions - I want a place that is not in the root, somewhere universal like the installation directory (but how do I find that with code?) or a settings directory (but how do I set that somewhere so I can find it later??)
For such purposes Qt provides a list of QStandardPaths functions that return platform specific standard paths, such as a path to desktop, temp directory etc.
For your particular case you might put your database in the directory that corresponds to the QStandardPaths::AppDataLocation key.
You can use QSettings to save path,settings and restore them.
QSettings m_Arhive("Company", "app_name");
//Set DB path
m_Arhive.setValue("DBPath", "c:/somewhere/database");
//Get DB path
m_Arhive.value("DBPath").toString()

Win32: Programmatically Iterate Through All Users' Standard Directories

Hi my question is what is the best way to iterate through every standard directory location for every user on a computer using the win32 api (docs, music, pics etc) (or qt if possible). I have found these guys:
SHGetKnownFolderPath
GetProfilesDirectory
GetUserProfileDirectory. The GetProfilesDirectory and GetUserProfileDirectory functions are obvious in how to use but don't return a path for the individual standard folders which could be a problem if I need to get a directory from a non-stanard location i.e. D:\Documents for a user whose profile directory is C:\Users\Josh. The SHGetKnownFolderPath function can do what I need it looks like however I don't know how to get an access token for a user not logged in. Is there any way to do what I want with the win32 api maybe with SHGetKnownFolderPath and access tokens for inactive users? Perhaps a third party library? I'm also using Qt but don't think that's relevant I couldn't find such libraries for Qt that do what I want...

C:\ file scan with C++, access denied to specific files

Ok so I have written a code with recursive function that scans my (or any other) C:\ disc.
Using chdir() I'm changing the active directory to C:\ and using system("dir >> C:\Test\"filename") to get the files in that directory written in .txt and then reading it to know what I have. After that I'm checking for additional directories with temp.substr("<DIR>") and calling the function again just with longer path for that specific directory.
The code itself works as it should but I've ran to some directories that I can't access such as Administrator or specific folders in Windows. The only way I've found around them is by adding exeptions to the code so that it just avoids those folders. I'd like to know if there is a way to get a list of folders/files that I don't have access to from cmd since it would make my code a lot more precise or a way to access those directories.
While C++ file API does not have any idea about file permissions and such other than you'll fall when you try to read a file you don't have permissions to, C++ do have access to the Windows API. The Windows API are system calls that gives you access to native OS functionalities, such as calls to GetFileSecurity.
Note that using Windows API means your code would need to take extra steps to ensure portability if you want to run it on non Windows platforms.
The process (your program) runs with certain privileges, normally the users one. The access rights in Windows aren't as simple as "Administrator is god". Even if your user account is member of the "Administrators" group, you can not simply access everything. It usually depends if the OWNER of a file system object grants privileges to other groups and users.
A member of the "Administrators" group might be able to gain access by overriding security settings, but I would advise against that.
You might need elevated privileges and even might need to run your application under a SYSTEM account, it very much depends on what you are trying to achieve.
For more details I suggest to read the "Best practices for Security API" here:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms717796(v=vs.85).aspx
and especially the chapter "Running with Special Privileges".

C++ get appdata path of other users

In my program I have been using SHGetFolderPath to get the AppData path. However I need to get the AppData path of the other users on the computer. The only way I can think to do it is to get the path for the current user, and then replace the current user's name with the other users names. I don't know how to get a list of the users. There's probably a much more elegant solution aswell... If you have insight I would greatly appreciate it.
For your situation I would recommend the following:
Continue to store configuration files in AppData, but store it in CSIDL_COMMON_APPDATA (SHGetFolderPath). This AppData is shared with all users. Your setup program (or an administrator user) can set up a folder in this location named after your program that gives "Everyone" full access (this is very easy with Windows Installer). That way, any user can read/write to it. Everything in "Program Files" should never change. It should only contain read-only executables, DLLs, and other such resources. Microsoft has long discouraged writing to this location and many administrators no longer expect to encounter custom user data that requires regular backup & restore in Program Files.
When your software runs, you can check for data in the current user's AppData (i.e. stored by your old version) and merge it with the data in the machine's AppData (described by #1 above). To migrate the data for the user, log in as that user and run your software.
There really isn't a great way that I'm aware of for gathering all that data from other user profiles. Nothing supported by Microsoft, that is (that I'm aware of!).
Regarding storage of data in Program Files: http://msdn.microsoft.com/en-us/library/bb776776(VS.85).aspx "Do not store user data under the Program Files folder." There are many other references that say similar.

How can I get the path of a Windows "special folder" for a specific user?

Inside a service, what is the best way to determine a special folder path (e.g., "My Documents") for a specific user? SHGetFolderPath allows you to pass in a token, so I am assuming there is some way to impersonate the user whose folder you are interested in.
Is there a way to do this based just on a username? If not, what is the minimum amount of information you need for the user account? I would rather not have to require the user's password.
(Here is a related question.)
Please, do not go into the registry to find this information. That location might change in future versions of Windows. Use SHGetFolderPath instead.
http://msdn.microsoft.com/en-us/library/bb762181(VS.85).aspx
Edit: It looks like LogonUser will provide the token for the other user that you need.
You might try calling ImpersonateLoggedOnUser() to modify a user token for another user, and then passing that to SHGetFolderPath(). Based on the doc for ImpersonateLoggedOnUser(), it looks like you can call LogonUser() to get a token for a specific user.
Just from reading around, I'd guess that the user in question must be logged on in some form in order for this to work. I recall one page stating that the user's registry hive must be mounted in order for this to work (which makes some sense I suppose).
I would mount the user's registry hive and look for the path value. Yes, it's a sub-optimal solution, for all the reasons mentioned (poor forwards compatibility, etc.). However, like many other things in Windows, MS didn't provide an API way to do what you want to do, so it's the best option available.
You can get the SID (not GUID) of the user by using LookupAccountName. You can load the user's registry hive using LoadUserProfile, but unfortunately this also requires a user token, which is going to require their password. Fortunately, you can manually load the hive using RegLoadKey into an arbitrary location, read the data, and unload it (I think).
Yes, it's a pain, and yes, it's probably going to break in future versions of Windows. Perhaps by that time MS will have provided an API to do it, back-ported it into older versions of Windows, and distributed it automatically through Windows update... but I wouldn't hold my breath.
P.S. This information intended to augment the information provided in your related question, including disclaimers.
This information is stored in the registry in the key "HKEY_USERS\S-1-5-21-616815238-485949776-2992451252-3228\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders".
The "S-1-5-21-616815238-485949776-2992451252-3218" is the GUID of the user. You need to get this GUID to find the corresponding key and read it.
In this example they use SHGetFolderPath function you mention and there is a list with all special folders which might be helpful.
NOTE: Microsoft discourages to use the registry key, since it is still there just for backward compatibility