9

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
  • 730,956
  • 141
  • 904
  • 1,278
Jonathan Livni
  • 101,334
  • 104
  • 266
  • 359
  • 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

4 Answers4

8

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
  • 530,221
  • 131
  • 937
  • 1,214
  • 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
  • Should add e.g. `i != nullptr` in the for loop condition so that the unlikely case where `GetEnvironmentStrings` returns NULL is also covered. – stijn Oct 18 '22 at 11:48
  • Also: some 'hidden' vars can actually start with '=' so you might want to exclude those instead of adding empty keys. See source of `GetEnvironmentVariables` in https://referencesource.microsoft.com/#mscorlib/system/environment.cs for a rather complete implementation. – stijn Oct 18 '22 at 12:02
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ᵩ
  • 163,533
  • 20
  • 239
  • 308
  • 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
2

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,939
  • 4
  • 44
  • 61
  • "No one should be using non-wide chars in Windows." - not anymore, true. On the other hand when `_UNICODE` macro is defined `TCHAR` expands to `WCHAR` which expands to `wchar_t` if the compiler supports it. – orhtej2 Jun 13 '22 at 22:19
  • @orhtej2: You're right. However, I don't think code should depends on *whether* a macro is defined and *whether* compiler supports `wchar_t`, which can be disabled in an [MSVC compiler settings](https://learn.microsoft.com/en-us/cpp/build/reference/zc-wchar-t-wchar-t-is-native-type) – Jonathan Jun 14 '22 at 07:29
1

This is a variation of the existing answers in this question from Konrad and Jonathan that might help anyone wanting to create a single widechar string for logging to file or (in my case) for remote diagnostics. Feel free to upvote them instead.

LPWCH we = GetEnvironmentStrings();
const wchar_t* w = we;
do {
    eline = w;
    w += eline.length() + 1;
    env += eline;
    env += L"\r\n";
} while ((eline.length() > 0) && (*w != L'\0'));
FreeEnvironmentStrings(we);
Dave S
  • 1,427
  • 1
  • 16
  • 18