Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get all env variables in C\C++ on Windows

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.

like image 633
Jonathan Livni Avatar asked Mar 02 '12 14:03

Jonathan Livni


4 Answers

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.
like image 62
Konrad Rudolph Avatar answered Oct 16 '22 23:10

Konrad Rudolph


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);

}
like image 22
Robᵩ Avatar answered Oct 16 '22 23:10

Robᵩ


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;
}
like image 37
Jonathan Avatar answered Oct 17 '22 00:10

Jonathan


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);
like image 1
Dave S Avatar answered Oct 16 '22 23:10

Dave S