Windows Application Development Tutorial (1/3)

Overview

Windows has a series of special folders with a particular purpose. One such folder for instance is the Program Files folder where applications are typically installed. The exact path of these special folders can change from one computer to the other so applications need to refer to them using special variables and functions.

This tutorial covers the following special folders.

FolderEnvironment VariableFOLDERIDCSLID
Program FilesProgramFilesFOLDERID_ProgramFilesCSIDL_PROGRAM_FILES
SystemNone, use %windir%\system32FOLDERID_SystemCSIDL_SYSTEM
SystemX86None, use %windir%\SysWOW64FOLDERID_SystemX86CSIDL_SYSTEMX86
WindowswindirFOLDERID_WindowsCSIDL_WINDOWS

When working with a C++ application the recommended function to get the path of a special folder is SHGetKnownFolderPath [1]. This replaces the SHGetFolderPath [2] function and the even older SHGetSpecialFolderPath [3] function. We will show examples later.

There are also several environment variables that contain the exact path of some of the special folders. We will also show how these can be used.

Differences between 32-bit and 64-bit Applications

One of the things that affects the exact path of some of the special folders is whether the OS and the application are the 32-bit or 64-bit versions. We will set up a demonstration environment to show the path of the special folders in each version.

Environment variables

To show how to use the environment variables on a 64-bit version of Windows we need a 32-bit and a 64-bit command prompt. When launching a command prompt from the Start menu, the 64-bit version is launched. The 32-bit version is located in the SysWOW64 [4] folder (usually C:\Windows\SysWOW64). The name of the executable is cmd.exe. We will discuss the role of the SysWOW64 folder later.

If we launch the 64-bit command prompt from the Start menu we get the prompt shown in Figure 1.

64-bit Command Prompt
Figure 1: 64-bit Command Prompt

If we launch cmd.exe found in SysWOW64 we get the prompt shown in Figure 2.

32-bit Command Prompt
Figure 2: 32-bit Command Prompt

They are virtually indistiguishable except for the window title.

C++

We also create a C++ program to show how to use the SHGetSpecialFolderPath function [3] to get the path of the special folders.

We need to compile the source file into a 32-bit program and into a 64-bit program so we can compare their outputs. This tutorial explains how to do this with Visual Studio.

Program Files Folder

The Program Files folder is where applications are stored. In 64-bit versions of Windows applications can be 32-bit or 64-bit. The ProgramFiles environment variable contains the exact path of the Program Files folder. The value of this variable is different in 32-bit and 64-bit command prompts.

ProgramFiles folder in 64-bit command prompt
C:\Users\Xavier Leclercq>echo %ProgramFiles%
C:\Program Files

C:\Users\Xavier Leclercq>
ProgramFiles folder in 32-bit command prompt
C:\Windows\SysWOW64>echo %ProgramFiles%
C:\Program Files (x86)

C:\Windows\SysWOW64>
Snippet of file: main.cpp
void GetProgramFilesFolder()
{
    std::wcout << L"Program Files Folder:" << std::endl;

    PWSTR ppszPath = NULL;
    HRESULT hr = SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, NULL, &ppszPath);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetKnownFolderPath: " << ppszPath << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetKnownFolderPath failed." << std::endl;
    }
    CoTaskMemFree(ppszPath);

    TCHAR programFilesFolder[MAX_PATH];
    hr = SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, SHGFP_TYPE_CURRENT, programFilesFolder);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetFolderPath: " << programFilesFolder << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetFolderPath failed." << std::endl;
    }
    
    BOOL success = SHGetSpecialFolderPath(0, programFilesFolder, CSIDL_PROGRAM_FILES, FALSE);
    if (success)
    {
        std::wcout << L"\tSHGetSpecialFolderPath: " << programFilesFolder << std::endl << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetSpecialFolderPath failed." << std::endl << std::endl;
    }
}

Windows Folder

The Windows folder is where most operating system files can be found. The windir environment variable contains the exact path of this folder. The value is the same for 32-bit and 64-bit programs. The value of the windir environment variable is shown in both cases below.

Windows folder in 64-bit command prompt
C:\Users\Xavier Leclercq>echo %windir%
C:\windows

C:\Users\Xavier Leclercq>
Windows folder in 32-bit command prompt
C:\Windows\SysWOW64>echo %windir%
C:\windows

C:\Windows\SysWOW64>

In addition to the SHGetKnownFolderPath, SHGetFolderPath and SHGetSpecialFolderPath functions there is a legacy function to get the Windows folder: GetWindowsDirectory [5].

The following code snippet shows how to use each function to get the path of the Windows folder.

Snippet of file: main.cpp
void GetWindowsFolder()
{
    std::wcout << L"Windows Folder:" << std::endl;

    PWSTR ppszPath = NULL;
    HRESULT hr = SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &ppszPath);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetKnownFolderPath: " << ppszPath << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetKnownFolderPath failed." << std::endl;
    }
    CoTaskMemFree(ppszPath);

    TCHAR windowsDirectory[MAX_PATH];
    hr = SHGetFolderPath(NULL, CSIDL_WINDOWS, NULL, SHGFP_TYPE_CURRENT, windowsDirectory);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetFolderPath: " << windowsDirectory << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetFolderPath failed." << std::endl;
    }

    BOOL success = SHGetSpecialFolderPath(0, windowsDirectory, CSIDL_WINDOWS, FALSE);
    if (success)
    {
        std::wcout << L"\tSHGetSpecialFolderPath: " << windowsDirectory << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetSpecialFolderPath failed." << std::endl;
    }

    UINT pathSize = GetWindowsDirectory(windowsDirectory, MAX_PATH);
    if (pathSize != 0)
    {
        std::wcout << L"\tGetWindowsDirectory: " << windowsDirectory << std::endl << std::endl;
    }
    else
    {
        std::wcout << L"\tGetWindowsDirectory failed." << std::endl << std::endl;
    }
}

System Folder

The system directory contains system files such as dynamic-link libraries and drivers.

The System folder has a special behaviour to allow both 32-bit and 64-bit programs to run on the 64-bit versions of Windows. If we assume that the path of the System folder returned by the API is C:\windows\system32 this will be the path used by a 64-bit program. And no that's not a typo, the 64-bit programs will indeed use the path with "32" in it. If a 32-bit program tries to use that path it will be redirected to the C:\Windows\SysWOW64 folder. Note that in both cases the API will return C:\windows\system32, the redirection is done at the filesystem level.

More details about this can be found in MSDN: File System Redirector [4].

If you want to get the path to the SysWOW64 directly without relying on the redirection you can use the SystemX86 folder.

There is no specific environment variable that holds the path of the System folder. You can get it by appending "\system32" to the windir environment variable.

In addition to the SHGetKnownFolderPath, SHGetFolderPath and SHGetSpecialFolderPath functions there is a legacy function to get the System folder: GetSystemDirectory [6].

The following code snippet shows how to use each function to get the path of the System folder.

Snippet of file: main.cpp
void GetSystemFolder()
{
    std::wcout << L"System Folder:" << std::endl;

    PWSTR ppszPath = NULL;
    HRESULT hr = SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &ppszPath);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetKnownFolderPath: " << ppszPath << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetKnownFolderPath failed." << std::endl;
    }
    CoTaskMemFree(ppszPath);

    TCHAR systemDirectory[MAX_PATH];
    hr = SHGetFolderPath(NULL, CSIDL_SYSTEM, NULL, SHGFP_TYPE_CURRENT, systemDirectory);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetFolderPath: " << systemDirectory << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetFolderPath failed." << std::endl;
    }

    BOOL success = SHGetSpecialFolderPath(0, systemDirectory, CSIDL_SYSTEM, FALSE);
    if (success)
    {
        std::wcout << L"\tSHGetSpecialFolderPath: " << systemDirectory << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetSpecialFolderPath failed." << std::endl;
    }

    UINT pathSize = GetSystemDirectory(systemDirectory, MAX_PATH);
    if (pathSize != 0)
    {
        std::wcout << L"\tGetSystemDirectory: " << systemDirectory << std::endl;
    }
    else
    {
        std::wcout << L"\tGetSystemDirectory failed." << std::endl;
    }

    std::wcout << L"\tNumber of files in directory: " << GetNumberOfItemsInDirectory(systemDirectory) << std::endl << std::endl;
}

SystemX86 Folder

As we saw in the previous section the System folder is redirected to the SysWOW64 folder for 32-bit applications. It is possible to get the path to this folder directly. This allows a 64-bit application to get the path of this folder for instance.

On 32-bit versions of Windows this folder doesn't exist.

As with the System folder there is no environment variable to get the path of the SysWOW64 folder. You can get it by appending "\SySWOW64" to the windir environment variable.

In addition to the SHGetKnownFolderPath, SHGetFolderPath and SHGetSpecialFolderPath functions there is a legacy function to get the System folder: GetSystemWow64Directory [7]

Snippet of file: main.cpp
void GetSystemFolderX86()
{
    std::wcout << L"SystemX86 Folder:" << std::endl;

    PWSTR ppszPath = NULL;
    HRESULT hr = SHGetKnownFolderPath(FOLDERID_SystemX86, 0, NULL, &ppszPath);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetKnownFolderPath: " << ppszPath << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetKnownFolderPath failed." << std::endl;
    }
    CoTaskMemFree(ppszPath);

    TCHAR systemX86Directory[MAX_PATH];
    hr = SHGetFolderPath(NULL, CSIDL_SYSTEMX86, NULL, SHGFP_TYPE_CURRENT, systemX86Directory);
    if (SUCCEEDED(hr))
    {
        std::wcout << L"\tSHGetFolderPath: " << systemX86Directory << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetFolderPath failed." << std::endl;
    }

    BOOL success = SHGetSpecialFolderPath(0, systemX86Directory, CSIDL_SYSTEMX86, FALSE);
    if (success)
    {
        std::wcout << L"\tSHGetSpecialFolderPath: " << systemX86Directory << std::endl;
    }
    else
    {
        std::wcout << L"\tSHGetSpecialFolderPath failed." << std::endl;
    }

    UINT pathSize = GetSystemWow64Directory(systemX86Directory, MAX_PATH);
    if (pathSize != 0)
    {
        std::wcout << L"\tGetSystemWow64Directory: " << systemX86Directory << std::endl;
    }
    else
    {
        std::wcout << L"\tGetSystemWow64Directory failed." << std::endl;
    }

    std::wcout << L"\tNumber of files in directory: " << GetNumberOfItemsInDirectory(systemX86Directory) << std::endl << std::endl;
}

Disabling the System Folder Redirection

In some rare circumstances a 32-bit application needs to access the native system32 folder and not the SysWOW64 folder.

For C++ programs the Wow64DisableWow64FsRedirection [8] and the Wow64RevertWow64FsRedirection [9] functions can be used to disable and then re-enable the System folder redirection.

The Wow64DisableWow64FsRedirection and Wow64RevertWow64FsRedirection functions only affect the thread they are called from.
Snippet of file: main.cpp
void DisableSystemFolderRedirection()
{
    std::wcout << L"Disable System Folder Redirection:" << std::endl;

    PWSTR ppszPath = NULL;
    HRESULT hr = SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &ppszPath);
    if (SUCCEEDED(hr))
    {
        std::wcout << "\tNumber of files in System folder: " << GetNumberOfItemsInDirectory(ppszPath) << std::endl;

        PVOID OldValue = NULL;
        if (Wow64DisableWow64FsRedirection(&OldValue))
        {
            std::wcout << "\tFolder redirection disabled" << std::endl;
            std::wcout << "\tNumber of files in System folder: " << GetNumberOfItemsInDirectory(ppszPath) << std::endl;

            if (Wow64RevertWow64FsRedirection(OldValue))
            {
                std::wcout << "\tFolder redirection re-enabled" << std::endl;
                std::wcout << "\tNumber of files in System folder: " << GetNumberOfItemsInDirectory(ppszPath) << std::endl;
            }
            else
            {
                std::wcout << L"\tWow64RevertWow64FsRedirection failed." << std::endl;
            }
        }
        else
        {
            std::wcout << L"\tWow64DisableWow64FsRedirection failed." << std::endl;
        }
    }
    else
    {
        std::wcout << L"\tSHGetKnownFolderPath failed." << std::endl;
    }
    CoTaskMemFree(ppszPath);
}

References

  1. MSDN: SHGetKnownFolderPath function
  2. MSDN: SHGetFolderPath function
  3. MSDN: SHGetSpecialFolderPath function
  4. MSDN: File System Redirector
  5. MSDN: GetWindowsDirectory function
  6. MSDN: GetSystemDirectory function
  7. MSDN: GetSystemWow64Directory function
  8. MSDN: Wow64DisableWow64FsRedirection function
  9. MSDN: Wow64RevertWow64FsRedirection function

Further Reading

  1. SamLogic Software: The 'Program Files (x86)' and 'SysWOW64' folders explained

Copyright(c) 2006-2015 Xavier Leclercq | Privacy policy

Home
Contact Us
Search