如何尽可能干净地使用VS C ++ GetEnvironmentVariable?

时间:2010-11-09 03:11:43

标签: c++ visual-c++

(这不是一个问题,就像一个迂腐的练习,所以这里。)

我已经制作了一个很好的小程序,这是我的Linux操作系统的原生程序,但我认为它在我的Windows机器上也很有用。因此,我想访问Windows的环境变量,MSDN引用了这样一个例子:

const DWORD buff_size = 50;
LPTSTR buff = new TCHAR[buff_size];

const DWORD var_size = GetEnvironmentVariable("HOME",buff,buff_size);

if (var_size==0) { /* fine, some failure or no HOME */ }
else if (var_size>buff_size) {

    // OK, so 50 isn't big enough.
    if (buff) delete [] buff;
    buff = new TCHAR[var_size];

    const DWORD new_size = GetEnvironmentVariable("HOME",buff,var_size);

    if (new_size==0 || new_size>var_size) { /* *Sigh* */ }
    else { /* great, we're done */ }
}
else { /* in one go! */ }

对于我来说,使用getenv和检查空指针并不是很好。我也不想动态分配内存,因为我只是想让程序在Windows和我的linux操作系统上运行,这意味着这个MS代码必须与nix代码很好地配合。更具体地说:

template <class T> // let the compiler sort out between char* and TCHAR*
inline bool get_home(T& val) { // return true if OK, false otherwise
#if defined (__linux) || (__unix)
    val = getenv("HOME");
    if (val) return true;
    else return false;
#elif defined (WINDOWS) || defined (_WIN32) || defined (WIN32)
    // something like the MS Code above
#else
    // probably I'll just return false here.
#endif
}

所以,我必须普遍地在堆上进行分配,或者在调用函数中执行#ifdef来释放内存。不是很漂亮。

当然,我本来可以在堆栈中首先分配'buff',但是如果'buff_size'在我的第一次调用时不够大,那么我必须创建一个新的TCHAR[] GetEnvironmentVariable。更好,但如果我是一个学究者并且不想创建多余的数组呢?关于更美观的东西的任何想法?

我不是那么知识渊博,所以有人会吝啬我故意强迫GetEnvironmentVariable失败以获得字符串大小吗?有没有人看到问题:

const DWORD buff_size = GetEnvironmentVariable("HOME",0,0);
TCHAR buff[buff_size];
const DWORD ret = GetEnvironmentVariable("HOME",buff,buff_size);
// ...

还有其他想法或建议吗? (或纠正明显的错误?)

更新: 下面有很多有用的信息。我认为我正在尝试做的最好的选择是使用static char[]之类的:

inline const char* get_home(void) { // inline not required, but what the hell.
#if defined (__linux) || (__unix)
    return getenv("HOME");
#elif defined (WINDOWS) || defined (WIN32) || defined (_WIN32)
    static char buff[MAX_PATH];
    const DWORD ret = GetEnvironmentVariableA("USERPROFILE",buff,MAX_PATH);
    if (ret==0 || ret>MAX_PATH)
        return 0;
    else
        return buff;
 #else
        return 0;
 #endif
 }

也许这不是最优雅的方式,但它可能是同步我想在* nix和Windows之间做的最简单的方法。 (我稍后也会担心Unicode支持。)

感谢帮助人员。

5 个答案:

答案 0 :(得分:9)

DWORD bufferSize = 65535; //Limit according to http://msdn.microsoft.com/en-us/library/ms683188.aspx
std::wstring buff;
buff.resize(bufferSize);
bufferSize = GetEnvironmentVariableW(L"Name", &buff[0], bufferSize);
if (!bufferSize)
    //error
buff.resize(bufferSize);

当然,如果您需要ASCII,请将wstring替换为string,将GetEnvironmentVariableW替换为GetEnvironmentVariableA

编辑:您也可以自己创建getenv。这是因为

  

相同的内存位置可能会在后续的getenv调用中使用,覆盖以前的内容。

const char * WinGetEnv(const char * name)
{
    const DWORD buffSize = 65535;
    static char buffer[buffSize];
    if (GetEnvironmentVariableA(name, buffer, buffSize))
    {
        return buffer;
    }
    else
    {
        return 0;
    }
}

当然,如果你想维持unicode支持,那么使用所有这些的宽字符版本可能是一个好主意。

答案 1 :(得分:4)

VC ++在stdlib.h中实现了getenv,例如,参见here

答案 2 :(得分:1)

这不是原始问题,但可能值得将MFC方式添加到此线程以供参考:

CString strComSpec;
if (strComSpec.GetEnvironmentVariable(_T("COMSPEC")))
{
    //Do your stuff here
}

答案 3 :(得分:0)

不要打扰。 %HOME%是Windows上的路径,所有合理的程序都可以使用。因此,它将适合WCHAR[MAX_PATH]。你不需要处理比它更长的边缘情况 - 如果它更长,大多数文件函数都会拒绝它,所以你也可能提前失败。

但是,假设您可以使用TCHAR[MAX_PATH]char[MAX_PATH]。您无法控制%HOME%的内容;它将包含用户名。如果是“André”(即非ASCII),则必须将%HOME%存储在WCHAR[MAX_PATH]中。

答案 4 :(得分:0)

您在帖子末尾提出的建议是正确的方法 - 调用一次以获取所需的缓冲区大小,然后再次实际获取数据。许多Win32 API以这种方式工作,它起初很混乱但很常见。

你可以做的一件事就是在第一次调用时传入一个最佳猜测缓冲区及其大小,如果失败则只能再次调用。