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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4130180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 14:36:03  来源:igfitidea点击:

How to use VS C++ GetEnvironmentVariable as cleanly as possible?

c++visual-c++

提问by Zorawar

(This is not so much a problem as an exercise in pedantry, so here goes.)

(这与其说是迂腐练习,不如说是一个问题,所以就这样了。)

I've made a nice little program that is native to my linux OS, but I'm thinking it's useful enough to exist on my Windows machine too. Thus, I'd like to access Windows' environment variables, and MSDN cites an example like this:

我已经制作了一个很好的小程序,它是我的 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! */ }

This is not nearly as nice (to me) as using getenv and just checking for a null pointer. I'd also prefer not to dynamically allocate memory since I'm just trying to make the program run on Windows as well as on my linux OS, which means that this MS code has to play nicely with nix code. More specifically:

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

So, I'd have to allocate on the heap universally or do a #ifdefin the calling functions to free the memory. Not very pretty.

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

Of course, I could have just allocated 'buff' on the stack in the first place, but then I'd have to create a new TCHAR[]if 'buff_size' was not large enough on my first call to GetEnvironmentVariable. Better, but what if I was a pedant and didn't want to go around creating superfluous arrays? Any ideas on something more aesthetically pleasing?

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

I'm not that knowledgeable, so would anyone begrudge me deliberately forcing GetEnvironmentVariable to fail in order to get a string size? Does anyone see a problem with:

我不是那么博学,所以有人会嫉妒我故意强迫 GetEnvironmentVariable 失败以获得字符串大小吗?有没有人看到以下问题:

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

Any other ideas or any suggestions? (Or corrections to glaring mistakes?)

任何其他想法或任何建议?(或者更正明显的错误?)

UPDATE: Lots of useful information below. I think the best bet for what I'm trying to do is to use a static char[]like:

更新:下面有很多有用的信息。我认为我想要做的最好的选择是使用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
 }

Perhaps it's not the most elegant way of doing it, but it's probably the easiest way to sync up what I want to do between *nix and Windows. (I'll also worry about Unicode support later.)

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

Thanks for the help guys.

谢谢你们的帮助。

回答by Billy ONeal

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

Of course, if you want ASCII, replace wstringwith stringand GetEnvironmentVariableWwith GetEnvironmentVariableA.

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

EDIT: You could also create getenvyourself. This works because

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

The same memory location may be used in subsequent calls to getenv, overwriting the previous content.

在对 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;
    }
}

Of course, it would probably be a good idea to use the wide character versions of all of this if you want to maintain unicode support.

当然,如果您想保持对 Unicode 的支持,使用所有这些的宽字符版本可能是个好主意。

回答by David Norman

VC++ implements getenv in stdlib.h, see, for example, here.

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

回答by user1228651

This wasn't the original question, but it might worth to add the MFC way to this thread for reference:

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

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

回答by MSalters

Don't bother. %HOME%is a path on Windows, and should be usable by all reasonable programs. Therefore, it will fit in a WCHAR[MAX_PATH]. You don't need to deal with the edge case where it's longer than that - if it's longer, most file functions will reject it anyway so you might as well fail early.

不要打扰。%HOME%是 Windows 上的路径,应该可以被所有合理的程序使用。因此,它将适合WCHAR[MAX_PATH]. 你不需要处理比它更长的边缘情况 - 如果它更长,大多数文件函数无论如何都会拒绝它,所以你最好早点失败。

However, do notassume you can use a TCHAR[MAX_PATH]or a char[MAX_PATH]. You do not have control over the contents of %HOME%; it will contain the users name. If that's "André" (i.e. not ASCII) you must store %HOME%in a WCHAR[MAX_PATH].

不过,千万不能以为你可以使用一个TCHAR[MAX_PATH]或一个char[MAX_PATH]。您无法控制%HOME%; 它将包含用户名。如果那是“André”(即不是 ASCII),则必须存储%HOME%WCHAR[MAX_PATH].

回答by Steve Townsend

The suggestion you made at the end of your post is the right way to do this - call once to get required buffer size and then again to actually get the data. Many of the Win32 APIs work this way, it's confusing at first but common.

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

One thing you could do is to pass in a best-guess buffer and its size on the first call, and only call again if that fails.

您可以做的一件事是在第一次调用时传入最佳猜测缓冲区及其大小,只有在失败时才再次调用。