How to properly use %USERPROFILE% inside code? - c++

Is my code correct? It seems can compile but does not work properly..
CString testing = _T(" --url=") + cstring + _T(" --out=%USERPROFILE%\\snapshot.png");
I want to point it to user's folder..but still cannot work.

The answer is that you don't use environment variables at all. Rather, you use the shell functions specifically designed to retrieve the path of special folders.
On Windows Vista and later, that function is SHGetKnownFolderPath. It takes KNOWNFOLDERID values to identify the folder whose path you wish to retrieve. In your case, that would be FOLDERID_Profile.
If you need to target earlier versions of Windows (such as XP), you will need to use the SHGetSpecialFolderPath function, instead. It takes a CSIDL value identifying the folder whose path you wish to retrieve. Again, in your case, that would be CSIDL_PROFILE.
Of course, you should never store data directly in the user's profile folder. So hopefully the bit of code that you've shown is for demonstration purposes only. Applications should only create files in the specific locations under the user profile folder, designed for application data storage.
These locations are CSIDL_APPDATA or CSIDL_LOCAL_APPDATA. If you are creating data that the user should be able to modify and should treat as his/her own, then it would be appropriate to store that data in the user's documents folder (CSIDL_MYDOCUMENTS).
More usage information is available in my answer here.
Sample code:
TCHAR szFolderPath[MAX_PATH];
if (!SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_APPDATA, FALSE))
{
// Uh-oh! An error occurred; handle it.
}
Or, using MFC's CString class:
CString buffer;
BOOL bRet = SHGetSpecialFolderPath(NULL, buffer.GetBuffer(MAX_PATH), CSIDL_APPDATA, FALSE);
buffer.ReleaseBuffer();
if (!bRet)
{
// Uh-oh! An error occurred; handle it.
}

As Cody suggested, it's better to use the SHGetSpecialFolderPath function. However, you could use the GetEnvironmentVariable function to get that and other variables set in the system.
TCHAR szBuf[MAX_PATH] = {0};
::GetEnvironmentVariable(_T( "USERPROFILE" ), szBuf, MAX_PATH);

Related

Windows registry key AppliesTo does not work with Users directory

What I am trying to do is to enable a customized context menu item to appear when users right-click folders in a specific directory.
So in HKEY_CLASSES_ROOT\Directory\shell I created my key (say with the name: MyProgram), and I created the subkey command that has the path to my program to run (say, "C:\Users\myuser\myApp\MyProgram.exe").
Until now it is ok and works correctly. But when I add the entry AppliesTo under HKEY_CLASSES_ROOT\Directory\shell\MyProgram and set it to C:\Users it does not work and the context menu item does not appear anymore!
Important Note: My windows language is German and the display name of Users folder in my windows explorer is Benutzer. Whenever I set AppliesTo to C:\Benutzer instead it works correctly, despite the fact that the command works with Users path properly! Alsowhen I echo %USERPROFILE% in the cmd it is printed in English as C:\Users\myuser and not Benutzer.
Is there a way to programmatically get the display path of Users or any folder in the system?
Please note: I cannot simply write Benutzer instead of Users in the path because the path can be dynamic. I am doing it programmatically and want my code to have consistent behavior in different machines with different languages. I am using C++ winreg API to set the registry values (for example: RegOpenKeyEx() and RegSetValueEx()).
Below is an export of the working version of the key:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\shell\MyProgram]
"AppliesTo"="C:\\Benutzer\\myuser"
[HKEY_CLASSES_ROOT\Directory\shell\MyProgram\command]
#="\"C:\\Users\\myuser\\path\\to\\MyProgram.exe\" \"%1\""
I found the solution. The localized name of folders can be obtained using the function SHGetLocalizedName, however it requires some additional work to get the string representation of the localized name. The following code snippet shows an example of how to do it.
PWSTR name = new WCHAR[100];;
PCWSTR folder = TEXT("C:\\Users");
UINT len=100;
int id=0;
HRESULT hr = SHGetLocalizedName(folder, name, len, &id);
if (SUCCEEDED(hr)) {
wprintf(L"%ls\n", name);
ExpandEnvironmentStrings(name, name, len);
HMODULE shell_handle = LoadLibraryEx(name, NULL, DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
if (shell_handle) {
if (LoadString(shell_handle, id, name, len) != 0) {
wprintf(L"%ls\n", name);
}
FreeLibrary(shell_handle);
}
}
The result of the previous code will be Benutzer in case of German locale.
You will need to include the following headers as well
#include <wchar.h>
#include <Shellapi.h>

How to get user home directory on Windows?

I'm developing a cross-platform library that is meant to load configuration files from a user's home directory. The idea is to automatically provide configuration parameters without editing code.
This library can be used in desktop apps or in daemons/services. In (I assume) most Unix environments I can use getpwuid() to get the home directory of the user. In Windows SO told me I could use SHGetKnownFolderPath but its documentation says its for desktop apps only. Is there a way to get this path, on Windows, for the user running a service?
For a console application the simplest method is to either retrieve the USERPROFILE environment variable or concatenate the values of the HOMEDRIVE and HOMEPATH environment variables.
Use the getenv() function in the standard library: https://msdn.microsoft.com/en-us/library/tehxacec.aspx
Example program:
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char** argv) {
printf("USERPROFILE = %s\n", getenv("USERPROFILE"));
printf("HOMEDRIVE = %s\n", getenv("HOMEDRIVE"));
printf("HOMEPATH = %s\n", getenv("HOMEPATH"));
return 0;
}
Output:
USERPROFILE = C:\Users\myuser
HOMEDRIVE = C:
HOMEPATH = \Users\myuser
What about this:
#include <shlobj.h>
WCHAR profilePath[MAX_PATH];
HRESULT result = SHGetFolderPathW(NULL, CSIDL_PROFILE, NULL, 0, profilePath);
if (SUCCEEDED(result)) {
// Do whatever you want with it
// For example:
// QString::fromWCharArray(profilePath)
}
I haven't tested it, though.
Note that what you receive is a wchar array (necessary to handle paths with special characters).
I think it's also possible to query special folders of other users than the current one by using the hToken parameter.
Also refer to the documentation: https://msdn.microsoft.com/en-us/library/windows/desktop/bb762181(v=vs.85).aspx
I used this code in another case where I wanted to obtain the start menu location. See this answer: C++: How do I create a Shortcut in the Start Menu on Windows
You could resolve %HOMEPATH% using ExpandEnvironmentStrings(...)
So do you want to get user home directory in service state?
- If you want that in service state, you have to use GetUserToken() to get user token then duplicate them for CreateprocessAsUser()
- Else I think it's better using SHGetSpecialPath(), SHGetTempPath().

C++ How do we make our application start on computer startup (and of course, after a user signed in)?

How do we make our application start on computer startup (and of course, after the user had signed in)?
And no, I am not making a virus.
Does registry editing sound like a good idea?
My OS is Windows 8.
However, I will try to make my application available for all possible Window OS.
The correct way to do this is simply to add a shortcut to your application's executable to the user's Startup folder. You do not need to (and should not) modify the registry.
Advanced users know how to do this manually already, but it may also be an option you want to provide as part of your installer and/or a configuration dialog in your application.
To do this from C++ code, you will need to do two things:
Retrieve the location of the current user's Startup folder.
This is accomplished by calling the SHGetKnownFolderPath function and specifying the KNOWNFOLDERID of the folder you're interested in. In this case, that would be FOLDERID_Startup.
Sample code:
std::wstring GetStartupFolderPath()
{
PWSTR pszPath;
HRESULT hr = SHGetKnownFolderPath(&FOLDERID_Startup,
0, // no special options required
NULL, // no access token required
&pszPath);
if (SUCCEEDED(hr))
{
// The function succeeded, so copy the returned path to a
// C++ string, free the memory allocated by the function,
// and return the path string.
std::wstring path(pszPath);
CoTaskMemFree(static_cast<LPVOID>(pszPath));
return path;
}
else
{
// The function failed, so handle the error.
// ...
// You might want to throw an exception, or just return an
// empty string here.
throw std::runtime_error("The SHGetKnownFolderPath function failed");
}
}
Note, however, that while SHGetKnownFolderPath is the recommended function to use, it is supported only by Windows Vista and later. If you need to support older versions of the operating system, you'll need to call it dynamically on newer versions where it is available, and otherwise fall back to calling the SHGetFolderPath function. This one takes a different type of identifier, a CSIDL value. The one you want is CSIDL_STARTUP.
Create a shortcut to your application's executable.
This is accomplished using a different set of Shell API functions. I won't bother writing up sample code here because it's all quite well explained on MSDN already: Shell Links
Now you just connect the dots: when you create the shortcut to your application's executable, specify the user's Startup folder as its destination path.
The only other thing to be aware of is that there are actually multiple Startup folders. Each user has one, which is the one we retrieved above using FOLDERID_Startup. About 99% of the time, that's the one you want. Putting a shortcut to your app there will cause it to be launched automatically when that user logs on.
However, there is also a global Startup folder that is shared by all users. This one is identified by FOLDERID_CommonStartup (or CSIDL_COMMON_STARTUP) and requires administrative privileges to add items to. That makes sense, of course, because whatever you put in there is going to launch automatically when any user logs on to the computer. Only administrators can affect global behavior like this. And chances are, your app doesn't need this anyway.
Start menu
Simplest solution is to place .lnk of .bat file into the Start Menu\On startup folder. This is easiest and not too sneaky against the user.
Registry:
Another solution is creating the key in the registry keys:
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Run] //All users
[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\RunOnce] //All users once
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run] //Currend user
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\RunOnce] //Current user once
This is not that transparent - so a bit more agressive against the user.
On windows you can put a shortcut to your application in the Startup folder, or you can implement it as a service.
And that "I am not making a virus" does make you sound guilty... Maybe it is a keylogger? ;)
There are a lot of ways, but they all depend on your OS. For windows take a look at the "Task Schedualer" under "Administrative tools" in the control panel.
Maybe something like this? Note, this code snippet is not written by me.
#include <windows.h>
void Reg() {
::HKEY Handle_Key = 0;
::DWORD Dispoition = 0;
::RegOpenKeyEx( HKEY_LOCAL_MACHINE,
"Software\\Microsoft\\Windows\\CurrentVersion\\Run",
0,
KEY_ALL_ACCESS,
&Handle_Key );
const unsigned char Path[ MAX_PATH ] = "C:\\Windows\\YourProgramName.exe";
::RegSetValueEx( Handle_Key, "My Directory", 0, 1, Path, sizeof( unsigned char ) );
};
What do you guys think?

How does one read a localized name from the registry?

Consider the following registry export:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\WinSock2\Parameters\Protocol_Catalog9\Catalog_Entries\000000000001]
;...
"ProtocolName"="#%SystemRoot%\\System32\\wshtcpip.dll,-60100"
The intention here appears to be for someone to load the DLL in question, and use some form of API retrieve the actual name. But I don't know what that API is :/
I'd like to avoid loading the DLL into my address space (and thus call DLL_PROCESS_ATTACH) if at all possible; can't really trust third party DLLs to be trustworthy.
RegLoadMUIString will do the necessary for you. Note however, that it was introduced in Vista so won't help if you need to support XP.
If you want to avoid code in the DLL running whilst you extract resources, use LoadLibraryEx passing LOAD_LIBRARY_AS_IMAGE_RESOURCE | LOAD_LIBRARY_AS_DATAFILE, or possibly LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE. Once you have done that, you can call LoadString to extract the MUI value.
This is going to help:
HMODULE hModule = LoadLibrary(_T("wshtcpip.dll")); // LoadLibraryEx is even better
TCHAR pszValue[1024] = { 0 };
INT nResult = LoadString(hModule, 60100, pszValue, _countof(pszValue));
LoadString will take care of downloading resource from MUI, if needed. LoadString uses thread locale, which you might want to override prior to the call.
Also: Loading Language Resources on MSDN.

Is there an API to get the original variable values defined in OS

In the Windows System Properties | Environment Variables, there is a variable "AppStatus=status1".
There is an exe named MyApp.exe. In the implementation it changes the variable with the code.
int ret = putenv("AppStatus=status2"); // Change the environment variable.
If use API char * pStatus = getenv("AppStatus");, the returned value is "status2".
What I want to get is the original value defined in the OS ("AppStatus=status1") not in the process block. To implement this I can query the registry key
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path.
But it should be better if there is an API that supports it. Is anybody aware of it?
Just call GetEnvironmentVariable(). It works on the process state as maintained by Windows, not the CRT state modified by putenv().