7

The signature for the main function in C\C++ can include 3 arguments:

main( int argc, char *argv[ ], char *envp[ ] )

The third is the environment variables.

I'm compiling a library under VS10 and therefor I have no main(). How can I get the environment variables in exactly the same type as is in char *envp[]? I rather not use .NET as to decrease dependencies and perhaps one day be open to portability.

Jonathan Leffler
  • 698,132
  • 130
  • 858
  • 1,229
Jonathan Livni
  • 93,232
  • 99
  • 253
  • 352
  • getenv() and setenv() cannot be used because you want to know the whole list... but if you're using Visual Studio then you develop application for Windows, so what about http://msdn.microsoft.com/en-us/library/ms683187%28v=vs.85%29.aspx ? – Benoit Mar 02 '12 at 14:45
  • Related: http://stackoverflow.com/questions/2692855/extern-c-char-environ-windows-c-cli (possibly even a dupe) – Flexo Mar 02 '12 at 14:46
  • I suggest you do not try to write multi-language source files. – pmg Mar 02 '12 at 14:47
  • maybe this helps: http://stackoverflow.com/questions/631664/accessing-environment-variables-in-c – Stefan Birladeanu Mar 02 '12 at 14:49

3 Answers3

6

GetEnvironmentStrings returns a (read-only!) pointer to the start of the environment block for a process.

The block is a contiguous C-style string that contains null-terminated key=value pairs. The block is ended by an additional null termination.

To make access more convenient, use something like the following function:

typedef std::basic_string<TCHAR> tstring; // Generally convenient
typedef std::map<tstring, tstring> environment_t;

environment_t get_env() {
    environment_t env;

    auto free = [](LPTCH p) { FreeEnvironmentStrings(p); };
    auto env_block = std::unique_ptr<TCHAR, decltype(free)>{
            GetEnvironmentStrings(), free};

    for (LPTCH i = env_block.get(); *i != T('\0'); ++i) {
        tstring key;
        tstring value;

        for (; *i != T('='); ++i)
            key += *i;
        ++i;
        for (; *i != T('\0'); ++i)
            value += *i;

        env[key] = value;
    }

    return env;
}

Of course, a proper implementation would encapsulate this in a class, and probably use std::stringstream rather than manually iterating over the characters, concatenating the strings on char at a time. But I’m lazy.

Usage is like this:

environment_t env = get_env();

// Now you can write env[T("Var1")] to access a variable.
Konrad Rudolph
  • 506,650
  • 124
  • 909
  • 1,183
  • Can I then cast `char*[]` on the return value? Could you add this syntax if possible to your answer? – Jonathan Livni Mar 02 '12 at 14:49
  • @Jonathan No you cannot, `LPTCH` is a typedef for `TCHAR*`. It’s a *single* contiguous string, not an array of strings. Since your question is tagged, the easiest way to work with this mess is to parse it and put it in a vector. Wait … – Konrad Rudolph Mar 02 '12 at 14:53
  • I think that should be singular "GetEnvironmentVariable": http://msdn.microsoft.com/en-us/library/windows/desktop/ms683188%28v=vs.85%29.aspx However this requires knowing the name of the variable in advance. – Agnel Kurian Mar 02 '12 at 15:02
  • 1
    Please note that according to http://msdn.microsoft.com/en-us/library/windows/desktop/ms683187(v=vs.85).aspx one should release the env block via calling to FreeEnvironmentStrings() – Shmil The Cat Jan 16 '14 at 21:42
4

I don't know about windows, but on Linux this variable:

extern char **environ;

is exactly what you are looking for.

#include <stdio.h>
#include <assert.h>

extern char **environ;

int main (int ac, char **av, char **envp) {

  assert(envp == environ);

}
Robᵩ
  • 154,489
  • 17
  • 222
  • 296
  • 3
    `#include ` should declare `environ` these days. There was a time when `environ` was the only variable without a header declaring it, but they finally fixed that. – Jonathan Leffler Mar 02 '12 at 15:11
  • Given the answer to [this question](http://stackoverflow.com/questions/1370399/c-function-arg-char-is-not-the-same-as-char), I guess that `char **environ` and `char *envp[]` are of the same type – Jonathan Livni Mar 02 '12 at 16:45
1

The following is based of @Konrad's excellent answer, with 2 main differences:

  • Using wchar_t rather than TCHAR. No one should be using non-wide chars in Windows.
  • Constructing key and value using std::wstring str(buffer, buflen), as suggested in this answer. I believe performance should be better than concating char-by-char, though I haven't measured it.

code:

typedef std::map<std::wstring, std::wstring> environment_t;
environment_t get_env() {
    environment_t env;

    auto free = [](wchar_t* p) { FreeEnvironmentStrings(p); };
    auto env_block = std::unique_ptr<wchar_t, decltype(free)>{
        GetEnvironmentStringsW(), free};

    for (const wchar_t* name = env_block.get(); *name != L'\0'; )
    {
        const wchar_t* equal = wcschr(name, L'=');
        std::wstring key(name, equal - name);

        const wchar_t* pValue = equal + 1;
        std::wstring value(pValue);

        env[key] = value;

        name = pValue + value.length() + 1;
    }

    return env;
}
Community
  • 1
  • 1
Jonathan
  • 6,328
  • 4
  • 37
  • 55