0

I am looking for a way to retrieve the path to the all users start menu directory in C++. I am only able to get the one of the current user (using Qt):

QString startMenuPath = QStandardPaths::standardLocations(QStandardPaths::ApplicationsLocation).at(0);

However, Qt does not allow to retrieve the one for all users. As far as I know there also is no environment variable containing that path, that I could read.

nobody
  • 19,814
  • 17
  • 56
  • 77
bweber
  • 3,772
  • 3
  • 32
  • 57

3 Answers3

6

To get a known folder, use SHGetFolderPath, and pass a KNOWNFOLDERID or CSIDL for the desired folder.

For example, the following code gets the All Users Start Menu and the Programs folder:

// Remember to #include <Shlobj.h>

WCHAR path[MAX_PATH];

HRESULT hr = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, path);
if (SUCCEEDED(hr))
    std::wcout << L"Start Menu\Programs: " << path << std::endl;

hr = SHGetFolderPathW(NULL, CSIDL_COMMON_STARTMENU, NULL, 0, path);
if (SUCCEEDED(hr))
    std::wcout << L"Start Menu: " << path << std::endl;
theB
  • 6,450
  • 1
  • 28
  • 38
  • Thank you, works. However, I had to use `SHGetFolderPathW` instead of `SHGetFolderPath` because otherwise the function expects an argument of type `CHAR` instead of `WCHAR` for the path. – bweber Oct 13 '15 at 06:23
  • @user1488118 If you are using Visual C++, the Character Set in the project property page determines `CHAR` or `WCHAR` and then also the `SHGetFolderPath` is a macro that transforms into `SHGetFolderPathA` or `SHGetFolderPathW`. Other IDEs/build systems should have a similar switch. – MicroVirus Oct 13 '15 at 09:22
  • 1
    @user1488118 - Indeed. You'd need to compile with UNICODE defined. `SHGetFolderPath` is a macro that resolves to the A/W version depending on whether you're using unicode. Since it's not obvious I updated the answer. – theB Oct 13 '15 at 09:33
2

Thanks goes to user theB for the solution. Here my final piece of code for creating a shortcut in the all-users start menu on Windows (uses Qt):

#include <shlobj.h>

bool createStartMenuEntry(QString targetPath) {
    targetPath = QDir::toNativeSeparators(targetPath);

    WCHAR startMenuPath[MAX_PATH];
    HRESULT result = SHGetFolderPathW(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, startMenuPath);

    if (SUCCEEDED(result)) {
        QString linkPath = QDir(QString::fromWCharArray(startMenuPath)).absoluteFilePath("Some Link.lnk");

        CoInitialize(NULL);
        IShellLinkW* shellLink = NULL;
        result = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_ALL, IID_IShellLinkW, (void**)&shellLink);
        if (SUCCEEDED(result)) {
            shellLink->SetPath(reinterpret_cast<LPCWSTR>(targetPath.utf16()));
            shellLink->SetDescription(L"Description");
            shellLink->SetIconLocation(reinterpret_cast<LPCWSTR>(targetPath.utf16()), 0);

            IPersistFile* persistFile;
            result = shellLink->QueryInterface(IID_IPersistFile, (void**)&persistFile);

            if (SUCCEEDED(result)) {
                result = persistFile->Save(reinterpret_cast<LPCOLESTR>(linkPath.utf16()), TRUE);

                persistFile->Release();
            } else {
                return false;
            }
            shellLink->Release();
        } else {
            return false;
        }
    } else {
        return false;
    }
    return true;
}
bweber
  • 3,772
  • 3
  • 32
  • 57
-2

The path to any user's start menu is going to be (On Windows 7)

C:\Users\username\AppData\Roaming\Microsoft\Windows\Start Menu

For the All Users start menu (in Windows 7), it's

C:\ProgramData\Microsoft\Windows\Start Menu

However, only the admin and the user themselves will have unfettered access to each user's folders; everyone else will lack read/write permissions. You can circumvent this by running the program as admin, but you may wish to instead reconsider your program design, as a solution that depends on access to system-managed folders is, by design, going to be unstable.

Xirema
  • 19,889
  • 4
  • 32
  • 68
  • 1
    Using hardcoded paths is not recommended. For instance, on my system the two paths you have specified do not exist and a program using these hardcoded values will fail. These values are also not portable between Windows versions. The correct way is to use the shell32 API's, such as indicated in the other answer. – MicroVirus Oct 12 '15 at 21:35
  • You're right, but it's a moot point. As I already suggested, a program that depends on access to a directory that will be maintained by the Operating System itself (in addition to the program) will be inherently error prone. – Xirema Oct 12 '15 at 21:37
  • It is perfectly normal for an installer to add shortcuts to the All Users Start Menu. Provided you use the documented method to get the path, there is no reason to expect it to be "unstable". – Harry Johnston Oct 12 '15 at 22:34
  • Well, I do know where the start menu folder is located, but I don't want to use a hard coded path, because I cannot be certain that it will work on all Windows versions and all localizations (I think I remember, at least in prior versions, in the German localisation of Windows the start menu was called 'Startmenü'). Permissions are not a problem since my application acts as an installer which runs with admin privileges anyways since I'm also writing to HKLM in the registry and to the Program Files folder. – bweber Oct 13 '15 at 06:29