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

Zor*_*war 4 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! */ }
Run Code Online (Sandbox Code Playgroud)

这对我来说并不像使用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
}
Run Code Online (Sandbox Code Playgroud)

因此,我必须在堆上普遍分配或#ifdef在调用函数中执行以释放内存.不是很漂亮

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

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

const DWORD buff_size = GetEnvironmentVariable("HOME",0,0);
TCHAR buff[buff_size];
const DWORD ret = GetEnvironmentVariable("HOME",buff,buff_size);
// ...
Run Code Online (Sandbox Code Playgroud)

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

更新:下面有很多有用的信息.我认为我想要做的最好的选择是使用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
 }
Run Code Online (Sandbox Code Playgroud)

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

谢谢你的帮助.

Bil*_*eal 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);
Run Code Online (Sandbox Code Playgroud)

当然,如果你想ASCII,替换wstringstringGetEnvironmentVariableWGetEnvironmentVariableA.

编辑:你也可以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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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


小智 7

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

CString strComSpec;
if (strComSpec.GetEnvironmentVariable(_T("COMSPEC")))
{
    //Do your stuff here
}
Run Code Online (Sandbox Code Playgroud)