编程配置环境变量

  • 什么是环境变量
  • API函数
  • 打开/关闭注册表句柄
  • 获取子键/键值信息
  • 获取一个键值项的数据
  • 创建/打开/删除子键
  • 创建/设置/删除键值项
  • 示例代码


接手了个小任务,需要我在代码里配置环境变量,我查找了好多资料,然后融合了一下网上各位大神的写法,自己弄了个代码。思路就是操作注册表,既可以在注册表里修改已经有的环境变量,也可以自己新建环境变量名,然后把路径添加到自己的环境变量名内。

什么是环境变量

百度百科:环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。 环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。例如Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。

说白了环境变量就是在操作系统中一个具有特定名字的对象,它包含了一个或多个应用程序所将使用到的信息。当要求系统运行一个程序而没有告诉它程序所在的完整路径时,系统除了在当前目录下面寻找此程序外,还应到path中指定的路径去找。用户通过设置环境变量,来更好的运行进程。正确使用“环境变量”可以提高工作效率或实现某些特殊功能

举个例子:运用快捷键Win+R运行,在对话框中输入calc并回车,可以发现我们打开了计算器。我们打开我的电脑,在“C:\Windows\System32”目录下可以看到一个“calc.exe”的文件,我们打开的就是这个。但是我们再输入一个QQ并回车,并不能打开QQ,我们必须输入cmd,然后cd进入安装qq的目录才能打开。这就是环境变量的作用,我们的“C:\Windows\System32”位于系统的Path里,我们可以很方便的打开其中的程序而不用进入其对应的目录,系统从path里寻找。

在cmd中输入SET不加任何参数即可查找本机中的所有环境变量。

  • 用户变量:对于Windows来说一台机器上可能有多个用户,而用户变量就是你设置一些你需要的环境变量,但是不会影响其他用户。
  • 系统变量:整个Windows系统的环境变量
  • Path:在cmd中直接运行某个程序时,输入该程序的名字,系统会在Path路径中寻找匹配项。

任何用户都可以添加、修改或删除用户的环境变量,但是只有管理员才能添加、修改或删除系统环境变量。

API函数

在注册表中,所有的数据都是通过一种树状结构以键和子键的方式组织起来的,十分类似于目录结构。

注册表修改netbios name 的长度限制 注册表修改配置信息_环境变量

  • 根键,主要有五个,分别是
  • HKEY_CLASSES_ROOT:该键根据操作系统中所安装的应用程序的扩展名来指定文件类型。
  • HKEY_CURRENT_USER:该键包含本地工作站中存放的当前登录的用户信息。用户登录Windows的时候,其信息从HKEY_USERS中相应的项复制到这里。
  • HKEY_LOCAL_MACHINE:存放本地计算机的硬件信息
  • HKEY_USERS:保存存放在本地计算机口令列表中的用户标识和密码列表。
  • HKEY_CURRENT_CONFIG:存放当前用户桌面配置的数据、最后使用的文档列表和其他有关的当前用户的Windows版本的安装信息。
  • 子键:可以有多个子键和键值项,理解为磁盘分区下的多个目录就行。
  • 键值项:理解为文件目录下的文件就可以。由三部分组成:名称、数据类型、数据。数据类型有八种:
  • REG_BINARY 二进制数据
  • REG_DWORD 32位双字节数据
  • REG_SZ 以0结尾的字符串
  • REG_DWORD_BIG_ENDIAN 高位排在低位的双字
  • REG_EXPAND_SZ 扩展字符串,可以加入变量如&PATH%
  • REG_LINK UNICODE符号链接
  • REG_RESOURCE_LIST 设备驱动程序资源列表
  • REG_MULTI_SZ 多字符串

打开/关闭注册表句柄

我们想要操作注册表之前应该先打开指定的键,然后通过键的句柄进行操作:

LSTATUS RegOpenKeyExA(
  HKEY   hKey,    //根键句柄
  LPCSTR lpSubKey, //子键句柄
  DWORD  ulOptions, //NULL
  REGSAM samDesired, //权限设置
  PHKEY  phkResult //返回打开的句柄
);

函数执行成功后返回ERROR_SUCCESS

打开权限如下,可以直接设置为KEY_ALL_ACCESS:

  • KEY_ALL_ACCESS:所有权限打开
  • KEY_CREATE_LINK:保留供系统使用
  • KEY_CREATE_SUB_KEY:创建子项必须
  • KEY_ENUMERATE_SUB_KEYS:枚举子项必须
  • KEY_EXECUTE:等效与KEY_READ
  • KEY_NOTIFY:要求为注册表项或注册表项的子项请求更改通知
  • KEY_QUERY_VALUE:查询注册表项的值所必需
  • KEY_READ:合并STANDARD_RIGHTS_READ,KEY_QUERY_VALUE,KEY_ENUMERATE_SUB_KEYS和KEY_NOTIFY值
  • KEY_SET_VALUE:创建,删除或设置注册表值所必需
  • KEY_WOW64_32KEY:表示64位Windows上的应用程序应在32位注册表视图上运行。必须使用OR运算符将此标志与该表中查询或访问注册表值的其他标志组合
  • KEY_WOW64_64KEY:表示64位Windows上的应用程序应在64位注册表视图上运行。必须使用OR运算符将此标志与该表中查询或访问注册表值的其他标志组合。
  • KEY_WRITE:合并STANDARD_RIGHTS_WRITE,KEY_SET_VALUE和KEY_CREATE_SUB_KEY访问权限

必须注意,当我们打开句柄操作完成后,必须要关闭句柄

RegCloseKey(HKEY   hKey);

获取子键/键值信息

LSTATUS RegQueryInfoKeyW(
  HKEY      hKey,  //要获信息的句柄
  LPWSTR    lpClass, //接收创建键时的class字符串
  LPDWORD   lpcchClass, //lpClass长度
  LPDWORD   lpReserved, //NULL
  LPDWORD   lpcSubKeys, //子键数量
  LPDWORD   lpcbMaxSubKeyLen, //子键中最长名称的长度
  LPDWORD   lpcbMaxClassLen, //子键中最长class字符串长度
  LPDWORD   lpcValues,  //键值数量
  LPDWORD   lpcbMaxValueNameLen, //键值中最长名称的长度
  LPDWORD   lpcbMaxValueLen,  //键值项数据最大长度
  LPDWORD   lpcbSecurityDescriptor, //安全描述符长度
  PFILETIME lpftLastWriteTime //最后修改时间
);

虽然函数参数很多,但是实际使用时只需要填写自己需要的就可以,其余赋值NULL。

需要注意这个函数返回的长度都不包括结尾的’\0’字符,所有我们在使用的时候长度应该+1

获取一个键值项的数据

LSTATUS RegQueryValueEx(
  HKEY    hKey,     //打开的句柄
  LPCSTR  lpValueName,  //想要获取值的名字
  LPDWORD lpReserved,  //NULL
  LPDWORD lpType,  //数据类型
  LPBYTE  lpData,   //接收该值的数据的缓冲区指针
  LPDWORD lpcbData  //指向变量的指针,表示缓冲区大小
);

如果lpData参数指定的缓冲区不足以容纳数据,则该函数返回ERROR_MORE_DATA并将所需的缓冲区大小存储在lpcbData指向的变量中。在这种情况下,您必须增加缓冲区大小,然后再次调用 RegQueryValueEx,并将更新后的缓冲区大小传递给lpcbData参数。重复此操作,直到功能成功。

如果lpData为NULL,而lpcbData为非NULL,则该函数返回ERROR_SUCCESS并将数据的大小(以字节为单位)存储在lpcbData指向的变量中。这使应用程序可以确定为值的数据分配缓冲区的最佳方法。

如果lpValueName注册表值不存在,则RegQueryValueEx返回ERROR_FILE_NOT_FOUND。

创建/打开/删除子键

创建和打开子键都可以用RegCreateKeyEx函数:

LSTATUS RegCreateKeyEx(
  HKEY       hKey,  //根键
  LPCSTR    lpSubKey, //子键
  DWORD        Reserved, //NULL
  LPSTR         lpClass, //NULL
  DWORD           dwOptions,//创建子键时的选项
  REGSAM          samDesired,  //权限
  const LPSECURITY_ATTRIBUTES lpSecurityAttributes, //继承性
  PHKEY              phkResult, //返回打开或创建的句柄
  LPDWORD             lpdwDisposition
);

删除子键用下面的代码:

LSTATUS RegDeleteKeyA(
  HKEY   hKey,
  LPCSTR lpSubKey
);

创建/设置/删除键值项

LSTATUS RegQueryValueExW(
  HKEY    hKey,  //键句柄
  LPCWSTR lpValueName, //键值项名称
  LPDWORD lpReserved, //null
  LPDWORD lpType,  //键值项类型
  LPBYTE  lpData, //键值项数据
  LPDWORD lpcbData //键值项长度
);

如果调用时,键值项名称已经存在,则会覆盖原有键值项。如果没有就新建一个。

删除键值项函数:

LSTATUS RegDeleteValueA(
  HKEY   hKey,
  LPCSTR lpValueName
);

示例代码

#include<Windows.h>
#include<shellapi.h>
#include<tchar.h>
#include<string>
#include<iostream>
#include<vector>
#include<fstream>
#include<sstream>
#include<algorithm>
#include<istream>
#pragma comment(lib,"shell32.lib")

using namespace std;


bool AddEnvVariables(const TCHAR* strType, const TCHAR* strPath);   //添加环境变量,传入环境变量名和环境变量路径值
bool IsPathExist(void* PerfData, const TCHAR* myPath);  //查询需要添加的目录是否在环境变量中
bool parseEnv(const wstring& data, const wstring path, std::vector<wstring>& vdata);
bool nyStrstri(const wstring& src, const wstring& sub);
string UTF16toAnsi(const std::wstring& strUnicode);
wstring AnsitoUTF16(const std::string& strAnsi);
wstring string2wstring(string str);

int _tmain()
{
	string Name = "自定义环境变量名";
	string Path = "路径";
	wstring myPath = string2wstring(Path);
	wstring myName = string2wstring(Name);
	int nRet = AddEnvVariables(myName.c_str(),myPath.c_str());

	return 0;
}

/*
系统环境变量的根键HKEY_LOCAL_MACHINE
系统环境变量的子健SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment

用户环境变量的根键HKEY_CURRENT_USER
用户环境变量的子键Environment
*/
bool AddEnvVariables(const TCHAR* strType, const TCHAR* strPath)
{
	if (strPath == NULL)
		return false;

	HKEY hKey;
	const TCHAR* pEnvironmentKey = _T("Environment");

	//打开注册表句柄
	if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_CURRENT_USER, pEnvironmentKey, 0, KEY_WOW64_64KEY | KEY_ALL_ACCESS, &hKey))
	{
		cout << "RegOpenKeyEx Failded" << endl;
	}


	//判断环境变量名是否存在
	DWORD dwBufferSize = 8192;   //初始数据长度
	DWORD dwData;                //返回的数据长度
	DWORD dwRet;                 //判断结果
	void* PerfData = malloc(dwBufferSize);  //数据存放缓冲区
	dwData = dwBufferSize;
	dwRet = RegQueryValueEx(hKey, strType,NULL,NULL, (LPBYTE)PerfData,&dwData);

	while (dwRet == ERROR_MORE_DATA)
	{
		dwBufferSize += 4096;
		PerfData = realloc(PerfData, dwBufferSize);
		dwData = dwBufferSize;
		dwRet = RegQueryValueEx(hKey, strType, NULL, NULL, (LPBYTE)PerfData, &dwData);
	}

			//如果不存在,首先新建环境变量,然后添加环境变量值
	if (dwRet == ERROR_FILE_NOT_FOUND)
	{
		wstring data = strPath;
		data = data +_T( ";");  //添加环境变量
		TCHAR* pathValue;
		TCHAR szbuf[4096] = { 0 };
		_tcscpy_s(szbuf, sizeof(szbuf) / sizeof(szbuf[0]), data.c_str());
		pathValue = szbuf;
		RegSetValueEx(hKey, strType, 0, REG_EXPAND_SZ, (LPBYTE)pathValue, (_tcslen(pathValue) + 1) * sizeof(TCHAR));
	}

			//如果存在,则直接添加环境变量值
	else if (dwRet == ERROR_SUCCESS)
	{
		if (IsPathExist(PerfData, strPath))  //如果环境变量中已经存在路径,则直接退出
		{
			RegCloseKey(hKey);
			free(PerfData);
			return true;
		}
		//如果环境变量中没有路径,则添加路径
		int len = dwData + _tcslen(strPath) + 1;
		TCHAR* temp = new TCHAR[len];    
		memset(temp, 0x00, len);
		_tcscpy_s(temp, len, (TCHAR*)PerfData);

		wstring data;
		vector<wstring> vdata;
		parseEnv(temp, strPath, vdata);
		vector<wstring>::iterator it = vdata.begin();
		for (; it != vdata.end(); ++it)
		{
			data = data + it->c_str() + _T(";");
		}
		data = data + strPath + _T(";");    //添加环境变量

		vdata.clear();

		TCHAR* pathValue;
		TCHAR szbuf[4096] = { 0 };
		_tcscpy_s(szbuf, sizeof(szbuf) / sizeof(szbuf[0]), data.c_str());
		pathValue = szbuf;

		LONG  setResult = RegSetValueEx(hKey,strType,0,REG_EXPAND_SZ,(LPBYTE)pathValue,(_tcslen(pathValue) + 1) * sizeof(TCHAR));
		DWORD dwResult;
		SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, LPARAM(_T("Environment")), SMTO_ABORTIFHUNG, 5000, &dwResult);//广播立即执行 
		delete[] temp;
		temp = NULL;
	}



	//关闭和释放操作
	RegCloseKey(hKey);
	free(PerfData);
	return true;
}

bool IsPathExist(void* PerfData, const TCHAR* myPath)
{
	TCHAR* myoldPath = (TCHAR*)PerfData;
	int i = 0;
	while (myoldPath[i] != _T('\0'))
	{
		i = i + 2;
	}
	TCHAR* path_t = new TCHAR[i / 2 + 2];

	i = 0;
	while (myoldPath[i] != _T('\0'))
	{
		path_t[i / 2] = myoldPath[i];
		i = i + 2;
	}
	path_t[i / 2] = _T('\0');
	path_t[i / 2 + 1] = _T('\0');

	wstring strMyoldPath;
	wstring strMyPath;

	strMyoldPath = path_t;
	strMyPath = myPath;

	if (strMyoldPath[strMyoldPath.length() - 1] != _T(';'))
	{
		strMyoldPath = strMyoldPath + _T(";");
	}
	if (strMyPath[strMyPath.length() - 1] != _T(';'));
	{
		strMyPath = strMyPath + _T(";");
	}

	if (-1 == strMyoldPath.find(strMyPath, 0))
	{
		delete path_t;
		return false;
	}

	free(path_t);
	return true;
}

bool parseEnv(const wstring& data, const wstring path, vector<wstring>& vdata)
{
	stringstream strdata(UTF16toAnsi(data));
	string temp = "";
	vector<string> vtemp;
	while (getline(strdata, temp, ';'))
	{
		vtemp.push_back(temp);
	}
	bool bRet = false;
	string reStr;
	vector<string>::iterator it = vtemp.begin();
	for (; it != vtemp.end();)
	{
		bRet = nyStrstri(AnsitoUTF16(it->c_str()), path);
		if (bRet)
		{
			it = vtemp.erase(it);
			bRet = true;
		}
		else
			++it;
	}
	//删除空字符串
	for (it = vtemp.begin(); it != vtemp.end();)
	{
		if (it->length() == 0)
		{
			it = vtemp.erase(it);
		}
		else
			++it;
	}
	//vdata = vtemp;
	vector<string>::iterator iit = vtemp.begin();
	for (; iit != vtemp.end(); iit++)
	{
		vdata.push_back(AnsitoUTF16(iit->c_str()));
	}
	return bRet;
}
bool nyStrstri(const wstring& src, const wstring& sub)
{
	bool bRet = false;
	if (src.length() == 0 || sub.length() == 0)
	{
		return bRet;
	}
	string strSrc = UTF16toAnsi(src);
	string strSub = UTF16toAnsi(sub);

	transform(strSrc.begin(), strSrc.end(), strSrc.begin(), ::toupper);
	transform(strSub.begin(), strSub.end(), strSub.begin(), ::toupper);

	if (strstr(strSrc.c_str(), strSub.c_str()) != NULL)
	{
		bRet = true;
	}
	return bRet;
}

string UTF16toAnsi(const wstring& strUnicode)
{
	int nAnsiLen = WideCharToMultiByte(CP_ACP, 0, strUnicode.c_str(), -1, NULL, 0, NULL, NULL);
	char* pAnsi = new char[nAnsiLen + 1];
	memset((void*)pAnsi, 0, (nAnsiLen + 1) * sizeof(char));
	::WideCharToMultiByte(CP_ACP, 0, strUnicode.c_str(), -1, pAnsi, nAnsiLen, NULL, NULL);
	std::string strAnsi;
	strAnsi = pAnsi;
	delete[]pAnsi;
	return strAnsi;
}

wstring AnsitoUTF16(const std::string& strAnsi)
{
	//获取转换所需的接收缓冲区大小
	int  nUnicodeLen = ::MultiByteToWideChar(CP_ACP, 0, strAnsi.c_str(), -1, NULL, 0);
	//分配指定大小的内存
	wchar_t* pUnicode = new wchar_t[nUnicodeLen + 1];
	memset((void*)pUnicode, 0, (nUnicodeLen + 1) * sizeof(wchar_t));
	//转换
	::MultiByteToWideChar(CP_ACP, 0, strAnsi.c_str(), -1, (LPWSTR)pUnicode, nUnicodeLen);
	std::wstring  strUnicode;
	strUnicode = (wchar_t*)pUnicode;
	delete[]pUnicode;
	return strUnicode;
}

//将string转换成wstring  
wstring string2wstring(string str)
{
	wstring result;
	//获取缓冲区大小,并申请空间,缓冲区大小按字符计算  
	int len = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), NULL, 0);
	TCHAR* buffer = new TCHAR[len + 1];
	//多字节编码转换成宽字节编码  
	MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.size(), buffer, len);
	buffer[len] = '\0';             //添加字符串结尾  
	//删除缓冲区并返回值  
	result.append(buffer);
	delete[] buffer;
	return result;
}
}