思路
- 首先枚举Capture、Render声卡设备;
- 在注册表SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\下对上述声卡设备进行属性设置,主要有两个属性键{f19f064d-082c-4e27-bc73-6882a1bb8e4c},0和{e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0,MSDN对上述两键值结构的描述为WAVEFORMATEXTENSIBLE。通过观察发现,键值长度为48bytes,而WAVEFORMATEXTENSIBLE结构长度为40bytes,对于键值的前8bytes的具体含义暂时还没有搞清楚,通过观察不同电脑不同声卡的上述两键值,猜测前8bytes为一种标识。
- 本意想通过RegSetValueEx直接对键值进行修改来实现统一配置,后来发现无法获得写的权限(至少我没发现该如何提权获得写入权限)。后发现人为手动编辑注册表是可以修改写入的,通过编写注册表文件并运行也可以修改写入。故采用此法来达到声卡统一配置。
代码
//-----------------------------------------------------------
// This function enumerates all active (plugged in) audio
// rendering endpoint devices. It prints the friendly name
// and endpoint ID string of each endpoint device.
//-----------------------------------------------------------
#include <mmdeviceapi.h>
#include <FunctionDiscoveryKeys_devpkey.h>
#include <iostream>
#include <string>
#include <map>
#include <tchar.h>
#include <queue>
#include <AccCtrl.h>
#include <AclAPI.h>
#include <fstream>
using namespace std;
#define EXIT_ON_ERROR(hres) \
if (FAILED(hres)) { goto Exit; }
#define SAFE_RELEASE(punk) \
if ((punk) != NULL) \
{ (punk)->Release(); (punk) = NULL; }
const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
map<string,string> render,capture,parameters;//render存放录音器的名称和CLSID,capture存放播放器的名称和CLSID,parameters存放相应设备CLSID的配置文件标识(8bytes)
#define MAX_KEY_LENGTH 255
#define MAX_VALUE_NAME 16383
DWORD dwType = REG_BINARY | REG_DWORD | REG_EXPAND_SZ | REG_MULTI_SZ | REG_NONE | REG_SZ;
std::queue<std::wstring> keystack;
//#define COMMAND_OUTPUT
void query(HKEY rootKey, const wchar_t* path , string clsid)
{
#ifdef COMMAND_OUTPUT
_tprintf(TEXT("\nProcess: %s :\n"), path);
#endif
HKEY hKey;
if (RegOpenKeyEx(rootKey, path, 0, KEY_READ, &hKey) != ERROR_SUCCESS)
{
RegCloseKey(hKey);
return;
}
TCHAR achKey[MAX_KEY_LENGTH]; // buffer for subkey name
DWORD cbName; // size of name string
TCHAR achClass[MAX_PATH] = TEXT(""); // buffer for class name
DWORD cchClassName = MAX_PATH; // size of class string
DWORD cSubKeys = 0; // number of subkeys
DWORD cbMaxSubKey; // longest subkey size
DWORD cchMaxClass; // longest class string
DWORD cValues; // number of values for key
DWORD cchMaxValue; // longest value name
DWORD cbMaxValueData; // longest value data
DWORD cbSecurityDescriptor; // size of security descriptor
FILETIME ftLastWriteTime; // last write time
DWORD i, retCode;
TCHAR achValue[MAX_VALUE_NAME];
DWORD cchValue = MAX_VALUE_NAME;
// Get the class name and the value count.
retCode = RegQueryInfoKey(
hKey, // key handle
achClass, // buffer for class name
&cchClassName, // size of class string
NULL, // reserved
&cSubKeys, // number of subkeys
&cbMaxSubKey, // longest subkey size
&cchMaxClass, // longest class string
&cValues, // number of values for this key
&cchMaxValue, // longest value name
&cbMaxValueData, // longest value data
&cbSecurityDescriptor, // security descriptor
&ftLastWriteTime); // last write time
// Enumerate the subkeys, until RegEnumKeyEx fails.
if (cSubKeys)
{
#ifdef COMMAND_OUTPUT
printf("Number of subkeys: %d\n", cSubKeys);
#endif
for (i = 0; i<cSubKeys; i++)
{
cbName = MAX_KEY_LENGTH;
retCode = RegEnumKeyEx(hKey, i,
achKey,
&cbName,
NULL,
NULL,
NULL,
&ftLastWriteTime);
if (retCode == ERROR_SUCCESS)
{
#ifdef COMMAND_OUTPUT
_tprintf(TEXT("(%d) %s\n"), i + 1, achKey);
#endif
//use achKey to build new path and input it into stack.
std::wstring newPath = L"";
newPath.append(path);
newPath.append(L"\\");
newPath.append(achKey);
keystack.push(newPath);
}
}
}
// Enumerate the key values.
if (cValues)
{
#ifdef COMMAND_OUTPUT
printf("Number of values: %d\n", cValues);
#endif
for (i = 0, retCode = ERROR_SUCCESS; i<cValues; i++)
{
cchValue = MAX_VALUE_NAME;
achValue[0] = '\0';
unsigned char vari[70];
retCode = RegEnumValue(hKey, i,
achValue,
&cchValue,
NULL,
NULL,
NULL,
NULL);
if (retCode == ERROR_SUCCESS)
{
BYTE szBuffer[255] = { 0 };
DWORD dwNameLen = 255;
DWORD rQ = RegQueryValueEx(hKey, achValue, 0, &dwType, (LPBYTE)szBuffer, &dwNameLen);
if (rQ == ERROR_SUCCESS)
{
//_tprintf(TEXT("(%d) %s %s\n"), i + 1, achValue, szBuffer);
if (_tcscmp(achValue, _T("{f19f064d-082c-4e27-bc73-6882a1bb8e4c},0")) == 0)
{
string propertiesFlag = "";
char val[3] = {0};
for(int i = 0 ; i < dwNameLen && i < 8 ; ++i)
{
sprintf(val,"%x",szBuffer[i]);
propertiesFlag.append(val);
propertiesFlag += ',';
}
parameters[clsid] = propertiesFlag;
return ;
}
if (_tcscmp(achValue, _T("{e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0")) == 0)
{
string propertiesFlag = "";
char val[3] = {0};
for(int i = 0 ; i < dwNameLen && i < 8 ; ++i)
{
sprintf(val,"%x",szBuffer[i]);
propertiesFlag.append(val);
propertiesFlag += ',';
}
parameters[clsid] = propertiesFlag;
return ;
}
}
}
}
}
//release.
RegCloseKey(hKey);
}
void regQuery(HKEY beginKey, TCHAR* path,string clsid)
{
//Begin to get HKEY of path.
query(beginKey, path,clsid);
while (!keystack.empty())
{
std::wstring newPath = keystack.front();
keystack.pop();
query(beginKey, newPath.c_str(),clsid);
}
//Release.
RegCloseKey(beginKey);
}
//查找id中的最后一个左大括号的索引值
int findLastDKH(char* src)
{
for(int i = strlen(src)-1 ; i >= 0; --i)
if(src[i] == '{')
return i;
return -1;
}
void PrintEndpointNames(int flag)//0 Capture 1 Render
{
HRESULT hr = S_OK;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDeviceCollection *pCollection = NULL;
IMMDevice *pEndpoint = NULL;
IPropertyStore *pProps = NULL;
LPWSTR pwszID = NULL;
hr = CoCreateInstance(
CLSID_MMDeviceEnumerator, NULL,
CLSCTX_ALL, IID_IMMDeviceEnumerator,
(void**)&pEnumerator);
EXIT_ON_ERROR(hr)
if(flag == 0)
hr = pEnumerator->EnumAudioEndpoints(
eCapture, DEVICE_STATE_ACTIVE,
&pCollection);
else
hr = pEnumerator->EnumAudioEndpoints(
eRender, DEVICE_STATE_ACTIVE,
&pCollection);
EXIT_ON_ERROR(hr)
UINT count;
hr = pCollection->GetCount(&count);
EXIT_ON_ERROR(hr)
if (count == 0)
{
printf("No endpoints found.\n");
}
// Each loop prints the name of an endpoint device.
for (ULONG i = 0; i < count; i++)
{
// Get pointer to endpoint number i.
hr = pCollection->Item(i, &pEndpoint);
EXIT_ON_ERROR(hr)
// Get the endpoint ID string.
hr = pEndpoint->GetId(&pwszID);
EXIT_ON_ERROR(hr)
hr = pEndpoint->OpenPropertyStore(
STGM_READ, &pProps);
EXIT_ON_ERROR(hr)
PROPVARIANT varName;
// Initialize container for property value.
PropVariantInit(&varName);
// Get the endpoint's friendly-name property.
hr = pProps->GetValue(
PKEY_Device_FriendlyName, &varName);
EXIT_ON_ERROR(hr)
// Print endpoint friendly name and endpoint ID.
printf("Endpoint %d: \"%S\" (%S)\n",
i, varName.pvarVal, pwszID);
DWORD dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,(LPWSTR)varName.pvarVal,-1,NULL,0,NULL,FALSE);
char *name = new char[dwMinSize];
if(!name)
delete [] name;
WideCharToMultiByte(CP_OEMCP,NULL,(LPWSTR)varName.pvarVal,-1,name,dwMinSize,NULL,FALSE);
printf("%s\n",name);
dwMinSize = WideCharToMultiByte(CP_OEMCP,NULL,(LPWSTR)pwszID,-1,NULL,0,NULL,FALSE);
char *id = new char[dwMinSize];
if(!id)
delete [] id;
WideCharToMultiByte(CP_OEMCP,NULL,(LPWSTR)pwszID,-1,id,dwMinSize,NULL,FALSE);
printf("%s\n",id);
string str_id(id);
str_id = str_id.substr(findLastDKH(id));
if(flag == 0)
capture[name] = str_id;
if(flag == 1)
render[name] = str_id;
delete [] name,id;
CoTaskMemFree(pwszID);
pwszID = NULL;
PropVariantClear(&varName);
SAFE_RELEASE(pProps)
SAFE_RELEASE(pEndpoint)
}
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pCollection)
return;
Exit:
printf("Error!\n");
CoTaskMemFree(pwszID);
SAFE_RELEASE(pEnumerator)
SAFE_RELEASE(pCollection)
SAFE_RELEASE(pEndpoint)
SAFE_RELEASE(pProps)
}
bool isCapture(string clsid)
{
for(map<string,string>::iterator iter = capture.begin(); iter != capture.end(); ++iter)
if(iter->second == clsid)
return true;
return false;
}
int main() {
string front = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\";
string path = "";
string valueF19F064D = "fe,ff,02,00,80,bb,00,00,00,ee,02,00,04,00,10,00,16,00,10,00,03,00,00,00,01,00,00,00,00,00,10,00,80,00,00,aa,00,38,9b,71";
string valueE4870E26 = "fe,ff,02,00,80,bb,00,00,00,dc,05,00,08,00,20,00,16,00,20,00,03,00,00,00,03,00,00,00,00,00,10,00,80,00,00,aa,00,38,9b,71";
string idF19F064D = "{f19f064d-082c-4e27-bc73-6882a1bb8e4c},0";
string idE4870E26 = "{e4870e26-3cc5-4cd2-ba46-ca0a9a70ed04},0";
CoInitialize(NULL);
/提升权限///
//HANDLE hToken;
//TOKEN_PRIVILEGES tkp;
打开当前进程令牌,下面的三个函数是为了提权,让程序有关闭计算机的权限
//if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken) == 0)
//{
// printf("提权失败!\n");
// return 0;
//}
查询SE_DEBUG_NAME权限ID
//if(LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tkp.Privileges[0].Luid) == 0)
//{
// printf("提权失败!\n");
// return 0;
//}
//tkp.PrivilegeCount = 1;
//tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
提升至SE_DEBUG_NAME权限
//if(AdjustTokenPrivileges(hToken, false, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0) == 0)
//{
// printf("提权失败!\n");
// return 0;
//}
/
//获得设备clsid和名称
PrintEndpointNames(0);
PrintEndpointNames(1);
//获得设备属性标识
for(map<string,string>::iterator iter = capture.begin(); iter != capture.end(); ++iter)
{
path = front + "Capture\\" + iter->second + "\\Properties";
int len = MultiByteToWideChar(CP_ACP, 0, path.c_str(), path.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, path.c_str(), path.size(), buffer, len);
buffer[len] = 0;
regQuery(HKEY_LOCAL_MACHINE, buffer,iter->second);
delete [] buffer;
}
for(map<string,string>::iterator iter = render.begin(); iter != render.end(); ++iter)
{
path = front + "Render\\" + iter->second + "\\Properties";
int len = MultiByteToWideChar(CP_ACP, 0, path.c_str(), path.size(), NULL, 0);
TCHAR* buffer = new TCHAR[len + 1];
//多字节编码转换成宽字节编码
MultiByteToWideChar(CP_ACP, 0, path.c_str(), path.size(), buffer, len);
buffer[len] = 0;
regQuery(HKEY_LOCAL_MACHINE, buffer,iter->second);
delete[] buffer;
}
//regQuery(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Capture");
// 生成相应注册表文件
string content = "";
ofstream out;
for(map<string,string>::iterator iter = parameters.begin(); iter!= parameters.end(); ++iter)
{
content = "REGEDIT4\n[HKEY_LOCAL_MACHINE\\" + front + (isCapture(iter->first) ? "Capture" : "Render") + "\\" + iter->first + "\\Properties]\n" + "\"" + idF19F064D + "\"=hex:" + iter->second + valueF19F064D;
out.open(iter->first + "_f19f064d.reg",ios::out | ios::binary);
out << content;
out.close();
content = "REGEDIT4\n[HKEY_LOCAL_MACHINE\\" + front + (isCapture(iter->first) ? "Capture" : "Render") + "\\" + iter->first + "\\Properties]\n" + "\"" + idE4870E26 + "\"=hex:" + iter->second + valueE4870E26;
out.open(iter->first + "_e4870e26.reg",ios::out | ios::binary);
out << content;
out.close();
}
//生成运行文件
content = "@FOR %%A IN (*.REG) DO (REGEDIT /S %%A) \nDEL *.REG";
out.open("run.bat",ios::out | ios::binary);
out << content;
out.close();
system("run.bat");
system("del run.bat");
fflush(stdout); //Needed if you want to redirect output to a file
CoUninitialize();
}版权声明:本文为RyanLunar原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。