windows声卡统一配置(采样率等参数)

思路

  1. 首先枚举Capture、Render声卡设备;
  2. 在注册表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为一种标识。
  3. 本意想通过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版权协议,转载请附上原文出处链接和本声明。