接手了个小任务,需要我在代码里配置环境变量,我查找了好多资料,然后融合了一下网上各位大神的写法,自己弄了个代码。思路就是操作注册表,既可以在注册表里修改已经有的环境变量,也可以自己新建环境变量名,然后把路径添加到自己的环境变量名内。
什么是环境变量
百度百科:环境变量(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函数
在注册表中,所有的数据都是通过一种树状结构以键和子键的方式组织起来的,十分类似于目录结构。
- 根键,主要有五个,分别是
- 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;
}
}