当前位置: 首页 > article >正文

c#,Powershell,mmsys.cpl,使用Win32 API展示音频设备属性对话框

常识(基础)

众所周知,mmsys.cpl使管理音频设备的控制面板小工具,

其能产生一个对话框(属性表)让我们查看和修改各设备的详细属性:

在音量合成器中单击音频输出设备的小图标也能实现这个效果:

分析

查看进程后发现,其原来调用了RunDll32.exe

"C:\Windows\system32\rundll32.exe" shell32.dll,Control_RunDLL mmsys.cpl,,{0.0.0.00000000}.{46f5d09f-309e-4ec5-8919-4a881d3fc9e1},general

那我们是不是可以试着调用一下呢?

本质上,rundll调用了mmsys.cpl中的一个函数,但是,mmsys.cpl中只有CPlApplet一个函数

怎么办?

观察:{0.0.0.00000000}.{46f5d09f-309e-4ec5-8919-4a881d3fc9e1},general

提供数据字符串由两组构成,分别为前面的ID与后面的??!!general组成

这是一个很迷惑人的字符串,实际上,他们应作为一组字符串输入

进阶

那怎样获取ID呢,根据DEEPSEEK帮忙,我编制了一个小C#

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;namespace AudioDeviceEnumerator
{public static class Win32AudioAPI{// 常量定义public const int DEVICE_STATE_ACTIVE = 0x00000001;// 枚举定义public enum EDataFlow{eRender = 0,eCapture = 1,eAll = 2}public enum ERole{eConsole = 0,eMultimedia = 1,eCommunications = 2}// COM 接口定义[ComImport, Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]public interface IMMDeviceEnumerator{int EnumAudioEndpoints(EDataFlow dataFlow, int stateMask, out IMMDeviceCollection devices);int GetDefaultAudioEndpoint(EDataFlow dataFlow, ERole role, out IMMDevice endpoint);int GetDevice(string id, out IMMDevice device);int RegisterEndpointNotificationCallback([MarshalAs(UnmanagedType.Interface)] object handler);int UnregisterEndpointNotificationCallback([MarshalAs(UnmanagedType.Interface)] object handler);}[ComImport, Guid("0BD7A1BE-7A1A-44DB-8397-CC5392387B5E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]public interface IMMDeviceCollection{int GetCount(out int numDevices);int Item(int index, out IMMDevice device);}[ComImport, Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]public interface IMMDevice{int Activate([MarshalAs(UnmanagedType.LPStruct)] Guid iid, int clsCtx, IntPtr activationParams, [MarshalAs(UnmanagedType.IUnknown)] out object interfacePtr);int OpenPropertyStore(int access, out IPropertyStore properties);int GetId([MarshalAs(UnmanagedType.LPWStr)] out string id);int GetState(out int state);}[ComImport, Guid("886D8EEB-8CF2-4446-8D02-CDBA1DBDCF99"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]public interface IPropertyStore{int GetCount(out int count);int GetAt(int index, out PropertyKey key);int GetValue(ref PropertyKey key, out PropVariant value);int SetValue(ref PropertyKey key, ref PropVariant value);int Commit();}[StructLayout(LayoutKind.Sequential)]public struct PropertyKey{public Guid fmtid;public int pid;}[StructLayout(LayoutKind.Sequential)]public struct CNM{public string Name;public string ID;}[StructLayout(LayoutKind.Sequential)]public struct PropVariant{public short vt;public short wReserved1;public short wReserved2;public short wReserved3;public IntPtr pointerValue;public int intValue;}// COM 类标识[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] public class MMDeviceEnumerator { }// 辅助函数:获取设备友好名称public static string GetDeviceFriendlyName(IMMDevice device){IPropertyStore propStore;device.OpenPropertyStore(0, out propStore);PropertyKey key = new PropertyKey{fmtid = new Guid("A45C254E-DF1C-4EFD-8020-67D146A850E0"),pid = 14};PropVariant value;propStore.GetValue(ref key, out value);return Marshal.PtrToStringUni(value.pointerValue);}// 枚举音频设备并返回设备信息public static List<CNM> EnumerateAudioDevices(EDataFlow dataFlow){var enumerator = (IMMDeviceEnumerator)new MMDeviceEnumerator();IMMDeviceCollection devices;enumerator.EnumAudioEndpoints(dataFlow, DEVICE_STATE_ACTIVE, out devices);List<CNM> L = new List<CNM>();int count;devices.GetCount(out count);for (int i = 0; i < count; i++){IMMDevice device;devices.Item(i, out device);string id;device.GetId(out id);string name = GetDeviceFriendlyName(device);CNM c = new CNM();c.Name = name;c.ID = id;L.Add(c);}return L;}}
}

在Powershell中,我们只需要使用ADd-type加载一下,然后调用[AudioDeviceEnumerator.Win32AudioAPI]::EnumerateAudioDevices(
    [AudioDeviceEnumerator.Win32AudioAPI+EDataFlow]::eAll
)即可获取名称和ID

熟悉WIN32编程的同学都知道,CPlApplet函数的标准声明为CPlApplet(IntPtr,int,ver,ver)

而要得到对话框,我们就需要将第二个参数设置为10

        [DllImport("mmsys.cpl", SetLastError = true, CharSet = CharSet.Unicode)]public static extern int CPlApplet(IntPtr hwndCpl,int msg,string lParam1,string lParam2
);

简单一调用

CPlApplet(0,10,null,"{0.0.0.00000000}.{46f5d09f-309e-4ec5-8919-4a881d3fc9e1},general")
成功;

拓展

然而

"{0.0.0.00000000}.{46f5d09f-309e-4ec5-8919-4a881d3fc9e1},general"中,前面ID已经明白,后面的general是什么意思呢?

用IDA逆向一下,得到了以下字符串

playback
recording
sounds
spatial
listento
custom
vendor
sysfx
advanced
levels
tone
hdmi
spdif
general
communications

所以说,这写字符串对应的是选项卡的默认起始位置

结束。(备注:下期更精彩)

http://www.lryc.cn/news/2405029.html

相关文章:

  • STM标准库-TIM旋转编码器
  • 深入解析JVM工作原理:从字节码到机器指令的全过程
  • MCP通信方式之Streamable HTTP
  • 第七十三篇 从电影院售票到停车场计数:生活场景解析Java原子类精髓
  • 【原创】基于视觉模型+FFmpeg+MoviePy实现短视频自动化二次编辑+多赛道
  • C++----剖析list
  • 纳米AI搜索与百度AI搜、豆包的核心差异解析
  • 不到 2 个月,OpenAI 火速用 Rust 重写 AI 编程工具。尤雨溪也觉得 Rust 香!
  • 人工智能:网络安全的“智能守护者”
  • Python60日基础学习打卡Day46
  • 综述论文解读:Editing Large Language Models: Problems, Methods, and Opportunities
  • WEB3全栈开发——面试专业技能点P1Node.js / Web3.js / Ethers.js
  • Vscode下Go语言环境配置
  • Java八股文——MySQL篇
  • Oracle数据库学习笔记 - 创建、备份和恢复
  • Go语言--语法基础5--基本数据类型--输入输出(1)
  • 永磁同步电机无速度算法--自适应龙贝格观测器
  • LangChain工具集成实战:构建智能问答系统完整指南
  • 【razor】x264 在 的intra-refresh和IDR插帧
  • 分库分表的取舍
  • 随机算法一文深度全解
  • 在 Conda 环境下配置 Jupyter Notebook 环境和工作目录
  • MS39531N 是一款正弦驱动的三相无感直流电机驱动器,具有最小振动和高效率的特点
  • web3-基于贝尔曼福特算法(Bellman-Ford )与 SMT 的 Web3 DeFi 套利策略研究
  • 分析 java 的 Map<String,Map<String, List<Map<String,Integer>>>>
  • ChatterBox - 轻巧快速的语音克隆与文本转语音模型,支持情感控制 支持50系显卡 一键整合包下载
  • 前端开发面试题总结-HTML篇
  • 嵌入式学习--江协stm32day4
  • 【Matlab】连接SQL Server 全过程
  • MS8551/MS8552/MS8554 单电源、轨到轨输入输出、高精度运放,可替代AD8551/AD8552/AD8554