4

I am working on a C++ Console Application in Visual Studio 2012 on Windows 7 and I want to get the values of some environment variables from within the application.
Here is what I've tried so far -:

int main()
{
    char a[1000];
    int s=GetEnvironmentVariableA("HOME",a,1000);
}  

However, I am getting the value of s to be 0, indicating that variable "HOME" does not exist.
Also, getenv("HOME") returns NULL too.
So, what is the correct procedure of doing this ?

Alex K.
  • 171,639
  • 30
  • 264
  • 288
Anmol Singh Jaggi
  • 8,376
  • 4
  • 36
  • 77
  • 2
    If your expectation is that `%HOME%` is set by the system, it isn't. (peruse the results of `set` in a console) – Alex K. Jun 09 '14 at 16:48
  • 2
    Do note that `HOME` is a typical Linux/Mac environment variable, not Windows. – chris Jun 09 '14 at 16:54

2 Answers2

4

What this program is telling you, most likely, is that your process environment does not contain a variable named HOME. Note that HOME is not a variable that you would expect to be defined, unless you have taken steps to define it. Either by adding it to the system's environment, or by specifying a bespoke environment when creating the process.

The documentation says the following about the return value:

If the function succeeds, the return value is the number of characters stored in the buffer pointed to by lpBuffer, not including the terminating null character.

If lpBuffer is not large enough to hold the data, the return value is the buffer size, in characters, required to hold the string and its terminating null character and the contents of lpBuffer are undefined.

If the function fails, the return value is zero. If the specified environment variable was not found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.

So, if the function returns 0, do as the documentation says. Call GetLastError to find out why the function call failed.

But as I said, with probability very close to 1, the reason will simply be that your process environment has not defined a variable named HOME.

As to how you move forward, most likely you are looking for a location in the user's profile. Exactly how you do this will depend on where in the profile you wish to store/load the file. One of the APIs related to CSIDL or known folder IDs will serve your needs.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • re "Either by adding it to the system's environment", there are already **too many** environment variables intended to represent the user's home directory. Adding yet another one is not a good idea. See e.g. http://xkcd.com/927/ – Cheers and hth. - Alf Jun 09 '14 at 17:44
  • I can't see that this answers the user's question, the sentence with a question mark, "what is the correct procedure of doing this ?". – Cheers and hth. - Alf Jun 09 '14 at 17:55
  • @Cheers Nobody suggested that it was a good idea to add another variable. And as for answering the question, I took the question to be how to read an environment variable named `HOME`. – David Heffernan Jun 09 '14 at 18:02
3

Regarding your question,

So, what is the correct procedure of doing this ?

Windows doesn't have a single HOME standard variable. Instead, in the old days there were HOMEDRIVE and HOMEPATH, and apparently because they didn't know about it, with Windows Explorer in Windows 95, a new variable called USERPROFILE.

[C:\Users\alfps_000]
> set home
HOMEDRIVE=C:
HOMEPATH=\Users\alfps_000

[C:\Users\alfps_000]
> set user
USERDOMAIN=FRIKADELL
USERDOMAIN_ROAMINGPROFILE=FRIKADELL
USERNAME=alfps_000
USERPROFILE=C:\Users\alfps_000

[C:\Users\alfps_000]
> _

The silly triple-oh suffix (as if I were better than double-oh seven) is just what Windows 8.1 saw fit to give me. It's just too much work to cajole Windows into reasonable choices. And so not just with usernames but also with environment variables.

Here's your program rewritten to use the Windows variable that vaguely corresponds to Unix-land HOME, namely USERPROFILE:

#include <iostream>
#include <stdlib.h>     // getenv
using namespace std;

auto main() -> int
{
    cout
        << "User's profile directory: "
        << "[" << getenv( "USERPROFILE" ) << "]"
        << endl;
}

The Windows environment variables are awkward and not guaranteed, but still usable in scripts and very simple programs like the one above. In more serious C++ code you can instead use the SHGetKnownFolderPath API function. With Visual C++ it can look like this:

#undef UNICODE
#define UNICODE
#include <windows.h>
#include <shlobj.h>     // SHGetKnownFolderPath
#include <objbase.h>    // CoTaskMemFree

#include <iostream>     // std::wcout
#include <memory>       // std::unique_ptr
#include <stdexcept>    // std::runtime_error, std::exception
#include <stdlib.h>     // EXIT_FALURE, EXIT_SUCCESS
using namespace std;

void cotaskmem_free( wchar_t* p ) { CoTaskMemFree( p ); }

auto main() -> int
{
    using X = runtime_error;
    using String_deallocation = unique_ptr<wchar_t[], void(*)(wchar_t*)>;

    try
    {
        wchar_t* path;
        HRESULT const hr = SHGetKnownFolderPath(
            FOLDERID_Profile,       // REFKNOWNFOLDERID rfid -> %USERPROFILE%
            0,                      // DWORD dwFlags,
            0,                      // HANDLE hToken,
            &path                   // PWSTR *ppszPath
        );
        if( FAILED( hr ) ) { throw X( "SHGetKnownFolderPath failed" ); }
        String_deallocation const path_cleanup( path, cotaskmem_free );
        wcout << "User profile directory: [" << path << "]" << endl;
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        wcerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

g++ (per version 4.8.2) doesn't yet support API functions from Windows Vista and onward, at least not in general, so if you need to support g++ use some older function.

Note:

It's not unlikely that whatever you intended to access or place in %HOME%, would better be accessed or placed in one of the other special users's directories, also available via SHGetKnownFolderPath.

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • In networked environments, `%HOMEDRIVE%%HOMEPATH%` points to the user's network home directory while `%USERPROFILE%` points to the user's profile (which is always on the local disk). So they aren't really redundant. – Harry Johnston Jun 10 '14 at 02:15
  • @HarryJohnston: Thanks for pointing that out. I found an [SO question about it](http://stackoverflow.com/questions/606483/what-is-the-meaning-of-these-windows-enviroment-variables-homedrive-homepath). – Cheers and hth. - Alf Jun 10 '14 at 02:49