Window环境变量学习

 

最近开发chrome项目中遇到了问题,chrome中的进程需要一些参数,之前的做法是写死在代码里头的,后来发现每次这些参数都会变化。然后每次一个小改动,构建出来一个包都需要2个多小时,效率非常低。所以想通过一个可配的方式来实现。

我们讨论最终决定使用环境变量来实现。调起chrome进程的业务进程在创建的时候创建环境变量,chrome进程使用这些变量。

讨论的时候还存在一个问题,环境变量对传递大数据可能不太使用,后面我确认了下确实是不能太大(具体后面讲解),不过目前项目遇到的问题是小数据,解决这个问题使用环境变量已经足够了,所以就先定了,先把问题解决了再说,后续再优化。

 

解决这个问题的前提是CreateProcess函数是可以把父进程的环境变量继承给子进程的,参数msdn中的说明:

lpEnvironment

A pointer to the environment block for the new process. If this parameter is NULL, the new process uses the environment of the calling process.

也就是说lpEnvironment参数传入NULL就可以了。

 

然后把msdn中关于GetEnvironmentVariable和SetEnvironmentVariable函数的说明先贴出来吧

BOOL SetEnvironmentVariable( LPCTSTR lpName, LPCTSTR lpValue );

https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setenvironmentvariable

DWORD GetEnvironmentVariable( LPCTSTR lpName, LPTSTR lpBuffer, DWORD nSize );

https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-getenvironmentvariable

 

项目中使用的主要是在业务进程中设置环境变量的值,在chrome进程中读取环境变量的值,同时需要合老版本进行兼容,所以还

需要一个判断环境变量是否存在的功能。

 

设置环境变量:

SetEnvironmentVariable(_T("variableKeyA"), _T("variableValueA"));

这个函数还有一个功能,就是删除环境变量,把第二个参数传入NULL即可。

删除环境变量:SetEnvironmentVariable(_T("variableKeyA"), NULL);

读取环境变量:

需要说明的是我们事前并不知道环境变量字符串需要多大的缓存区,所以一般是先lpBuffer设置为NULL,nSize设置为0

函数会返回需要的缓冲区大小(包括字符串结尾的'\0'),然后我们知道大小了再去申请缓冲区。所以一般会调用两次,第一次得到

缓冲区的大小,第二次才得到真实的内容。(windows api 很多函数都是这样的 -_-)

DWORD value_length = ::GetEnvironmentVariable(variable_name, nullptr, 0);
if (value_length > 0)
{
    wchar_t* buf = new wchar_t[value_length];
    ::GetEnvironmentVariable(variable_name, buf, value_length);
}

然后就是最后一个判断环境变量是否存在了。这个功能可以使用GetEnvironmentVariable函数来实现,主要体现在

该函数的返回值上。

msdn上的说明是这样,如果函数是否会返回0,然后没有环境变量GetLastError会是ERROR_ENVVAR_NOT_FOUND

If the function fails, the return value is zero. If the specified environment variable was not found in the environment block, GetLastError returns ERROR_ENVVAR_NOT_FOUND.

 

我之前想的是函数返回0可能是不知道缓存区有多大,所以会先调用第一次,把lpBuffer设置为NULL,nSize设置为0后调用,得到缓存区大小。也就是说返回0并不能代表环境变量是否存在。所以我之前写的代码是这样子:

bool HasEnvironmentVar(const std::string& env_name)
{
    if (env_name.empty())
        return false;

    ::SetLastError(ERROR_SUCCESS);
    DWORD dwSize = ::GetEnvironmentVariableA(env_name.c_str(), NULL, 0);
    DWORD dwLastError = ::GetLastError();
    if (0 == dwSize && ERROR_ENVVAR_NOT_FOUND == dwLastError)
        return false;

    return true;
}

首先说明这个函数是正确的,没有问题的,按照msdn上的说明来写的。

不过当我看到了chrome中的代码时我发现可以直接通过函数返回值判断就可以了。


我看的是chrome 75内核的代码:

代码路径:base\environment.cc

HasVar用来判断环境变量是否存在:

bool Environment::HasVar(StringPiece variable_name) {
  return GetVar(variable_name, nullptr);
}

GetVar调用了GetVarImpl函数:

bool GetVarImpl(StringPiece variable_name, std::string* result) {
  DWORD value_length =
      ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), nullptr, 0);
  if (value_length == 0)
    return false;
  if (result) {
    std::unique_ptr<wchar_t[]> value(new wchar_t[value_length]);
    ::GetEnvironmentVariable(UTF8ToWide(variable_name).c_str(), value.get(),
                             value_length);
    *result = WideToUTF8(value.get());
  }
  return true;
}

 

也就是说当我们调用GetEnvironmentVariable函数,并且把lpBuffer设置为NULL,nSize设置为0后,如果环境变量

不存在,函数就返回0了。如果存在才是放回真实需要的缓冲区大小。

我本地验证了下果然是这样子。觉得chrome的代码还是又简洁有严谨。

 

 

特别需要注意的是SetEnvironmentVariable这个函数不允许多线程调用。开始我也是不太确定这个问题,我也是看到了chrome代码的注释才确定的。也就是说多线程调用的后果是不知道哪个设置成功了,最后的值是多少,或者说多线程调用后结果不可预期吧。

base\environment.cc 文件中 Environment 类的 SetVar 函数的注释,里头的实现就是调用的SetEnvironmentVariable函数。

 // Returns true on success, otherwise returns false. This method should not be called in a multi-threaded process.
  virtual bool SetVar(StringPiece variable_name, const std::string& new_value) = 0;

 

最后,之前提到的使用环境变量设置大块数据会有问题,我也查了下,确实是有限制。

lpValue

The contents of the environment variable. The maximum size of a user-defined environment variable is 32,767 characters. For more information, see Environment Variables.

Windows Server 2003 and Windows XP:  The total size of the environment block for a process may not exceed 32,767 characters.

If this parameter is NULL, the variable is deleted from the current process's environment.

也就是说单个环境变量的值最大是32767个字符。虽然我本地验证了更多也是没问题的,但是还是按照官方的标准来吧。

 

总结:这篇文章主要是总结了window上环境变量的使用,方便以后遇到类似问题可以快速解决,不用再到处找资料了。

然后这个主题是讲解使用环境变量来解决这个问题的,不过存在大块数据不能使用的问题。这里如果要解决大块数据的

问题可以使用共享内存,可以参考之前写的一篇讲解共享内存的文章:https://blog.csdn.net/zsc_976529378/article/details/52604973

 

然后这个问题从大一点的高度来看就是通过IPC传送的问题。后面再单独写篇文章吧

 

 

附录:参考chrome代码自己实现的一个简易环境变量封装类。
class CEnvironmentVariable
{
public:
    bool GetVar(const std::wstring& variable_name, std::wstring* result)
    {
        DWORD value_length =
            ::GetEnvironmentVariable(variable_name.c_str(), nullptr, 0);
        if (value_length == 0)
            return false;
        if (result) {
            std::unique_ptr<wchar_t[]> value(new wchar_t[value_length]);
            ::GetEnvironmentVariable(variable_name.c_str(), value.get(),
                value_length);
            *result = value.get();
        }
        return true;
    }

    // Syntactic sugar for GetVar(variable_name, nullptr);
    bool HasVar(const std::wstring& variable_name)
    {
        return GetVar(variable_name, nullptr);
    }

    // Returns true on success, otherwise returns false. This method should not
    // be called in a multi-threaded process.
    bool SetVar(const std::wstring& variable_name,
        const std::wstring& new_value)
    {
        return !!SetEnvironmentVariable(variable_name.c_str(), new_value.c_str());
    }

    // Returns true on success, otherwise returns false. 
    // This method should not be called in a multi-threaded process.
    bool UnSetVar(const std::wstring& variable_name)
    {
        return !!SetEnvironmentVariable(variable_name.c_str(), nullptr);
    }
};

 

测试代码:
        CEnvironmentVariable envVar;
        bool b = false;
        b = envVar.HasVar(L"enable");
        b = envVar.SetVar(L"enable", L"yes");
        b = envVar.HasVar(L"enable");
        b = envVar.UnSetVar(L"enable");
        b = envVar.HasVar(L"enable");

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值