声卡项目
5 分钟阅读
一、介绍
这本身是一个跨操作系统的项目,但是windows是客户群体最多的一个平台,所以主力是开发win下
二、主要的函数
流程图1 _EnumerateAudioEndpoints
this is the first operation of our processA. and it start with CoCreateInstance() to create object of IMMDeviceEnumeratorB. then if success, use IMMDeviceEnumerator.EnumAudioEndpoint() to enumerate all endpoints(actually include all input & output devices, but API can choose i or o or both devices) in this machine.C. if there exist endpoint, to check the number of endpoints with IMMDeviceCollection.GetCount().D. use for loop to enum every endpoint, retrieve its data with IMMDeviceCollection.Item()E. finally, if retrieving process runs smoothly and there is data who came with Item() operation, push it into endpoint global data pool.
001. 创建com的对象 CoCreateInstance
Refrence
1. CoCreateInstance MS Learning2. COM basic tutorial
Introduce
CoCreateInstance is used for instance of object creating in local machine. CoCreateInstanceEx is used for instance of object creating in REMOTE machine. CoGetClassObject is used for MULTI instances of object creating in local machine. CoCreateInstance encapsulates the CoGetClassObject in the low-level. CoCreateInstance connects the object with the ID.
Requirement
| Type | Name | Usage |
|---|---|---|
| Library | Ole32.lib | |
| Dynamic Link Library | Ole32.dll | |
| Header file | combaseapi.h | |
| …\Windows Kits\10\Include\10.0.22621.0\shared\guiddef.h | REFCLSID & IID definition | |
| …\Windows Kits\10\Include\10.0.22621.0\um\mmdeviceapi.h | ||
| Minimum System Version | Windows 2000 and above |
Parameters
(1/5)[in] rclsid
REFCLSID:REF=ReferenceCLS=ClassID=IdentifierREFCLSID rclsid:是 const CLSID& 的别名,表示对 CLSID 的引用。这样传递参数时,不需要复制整个 CLSID 对象,只需要传递它的引用,从而提高效率。
/*mmdeviceapi.h
* here is the definition of MMDevice Enumerator
* we are using "__uuidof(MMDeviceEnumerator)"
*/
#ifdef __cplusplus
class DECLSPEC_UUID("BCDE0395-E52F-467C-8E3D-C4579291692E")
MMDeviceEnumerator;
#endif
UUID为什么有中间的“-”连字符
UUID(Universally Unique Identifier)或 GUID(Globally Unique Identifier)中使用连字符(`-`)是为了增加可读性和标准化。虽然连字符没有任何技术上的必要性,但它们有助于分隔和识别 UUID 的不同部分。UUID 格式
一个标准的 UUID 格式为 8-4-4-4-12,即总共 32 个十六进制字符分为五个部分,每个部分用连字符分隔。这种格式定义在 RFC 4122 中。
例如:
格式的分隔意义
- 时间戳部分(8个字符):UUID 的前 8 个字符通常表示时间戳的高位部分。
- 时间戳的中间部分(4个字符):接下来的 4 个字符表示时间戳的中间部分。
- 时间戳的低位部分和版本号(4个字符):接下来的 4 个字符表示时间戳的低位部分和版本号。
- 时钟序列(4个字符):接下来的 4 个字符表示时钟序列。
- 节点(12个字符):最后的 12 个字符表示节点信息(通常是基于网络地址)。
这种分隔方式有助于解析和理解 UUID 的不同部分,并确保它们的唯一性。
使用连字符的好处
- 可读性:连字符使得 UUID 更加可读,更容易分辨和检查。
- 标准化:按照 RFC 4122 标准定义的格式,有助于在不同系统和平台之间保持一致性。
- 解析和验证:连字符分隔使得解析和验证 UUID 更加简单和明确。
无连字符表示
在一些情况下,可以去掉连字符并连续地写下所有字符。例如:
这种形式在某些编程语言和库中也是支持的,但为了确保一致性和标准化,通常还是建议使用带有连字符的格式。
总结
- 连字符的作用:主要是增加可读性和标准化。
- UUID 格式:8-4-4-4-12 的格式有助于分隔和识别不同的部分。
- 使用连字符的好处:使得 UUID 更加可读、标准化,便于解析和验证。
尽管技术上可以省略连字符,但使用标准格式带来的可读性和一致性优势使得它成为惯用的做法。
/*
* guiddef.h
*/
#define REFCLSID const IID &
(2/5)[in] pUnkOuter
关于Aggregate,更详尽的解释-FromGpt4o
在 `CoCreateInstance` 函数中,第二个参数 `pUnkOuter` 是一个指向 `IUnknown` 接口的指针,通常用于支持 COM 对象的聚合。聚合是 COM 中的一种技术,允许一个对象(外部对象)包含另一个对象(内部对象),并使内部对象的接口看起来像是外部对象的一部分。参数详细解释
HRESULT CoCreateInstance(
REFCLSID rclsid, // Class identifier (CLSID) of the object to be created
LPUNKNOWN pUnkOuter, // Pointer to controlling IUnknown (for aggregation)
DWORD dwClsContext, // Context in which the code that manages the newly created object will run
REFIID riid, // Interface identifier (IID) of the interface to be used to communicate with the object
LPVOID *ppv // Address of pointer variable that receives the interface pointer requested in riid
);
pUnkOuter 参数
pUnkOuter:指向控制未知接口(IUnknown)的指针。如果不使用聚合,则传递NULL。在聚合的情况下,pUnkOuter应该是外部对象的IUnknown指针。
聚合(Aggregation)
聚合是一种将一个对象嵌入到另一个对象中的机制,使得内部对象的接口对外部对象的客户端透明。聚合在以下情况下有用:
- 对象组合:允许组合对象共享一个公共的
IUnknown接口,从而表现为一个单一对象。 - 对象封装:允许一个对象封装另一个对象,并公开其接口,而不暴露内部实现细节。
聚合的使用
当你使用聚合时,外部对象将包含内部对象,并通过内部对象的接口来实现外部对象的部分功能。在这种情况下,CoCreateInstance 的第二个参数 pUnkOuter 将指向外部对象的 IUnknown 接口,以便内部对象可以调用外部对象的接口。
如果你不使用聚合,则将 pUnkOuter 设置为 NULL,表示不进行聚合。
示例代码
不使用聚合
IMMDeviceEnumerator* deviceEnumerator = NULL;
HRESULT hr = CoCreateInstance(
__uuidof(MMDeviceEnumerator), // CLSID
NULL, // pUnkOuter (no aggregation)
CLSCTX_ALL, // Context
__uuidof(IMMDeviceEnumerator), // IID
(void**)&deviceEnumerator // Interface pointer
);
在这个例子中,pUnkOuter 被设置为 NULL,表示不使用聚合。
使用聚合
假设你有一个类 COuter,它希望包含一个内部对象 CInner:
class COuter : public IUnknown {
public:
// Implement IUnknown methods
HRESULT QueryInterface(REFIID riid, void** ppv) {
if (riid == IID_IUnknown) {
*ppv = static_cast<IUnknown*>(this);
AddRef();
return S_OK;
}
// Handle other interfaces
return E_NOINTERFACE;
}
ULONG AddRef() {
return InterlockedIncrement(&m_refCount);
}
ULONG Release() {
ULONG count = InterlockedDecrement(&m_refCount);
if (count == 0) {
delete this;
}
return count;
}
private:
ULONG m_refCount;
};
// Create an instance of COuter and aggregate CInner
COuter* pOuter = new COuter();
IUnknown* pUnkOuter = static_cast<IUnknown*>(pOuter);
CInner* pInner = NULL;
HRESULT hr = CoCreateInstance(
CLSID_CInner, // CLSID of the inner object
pUnkOuter, // pUnkOuter (aggregation)
CLSCTX_INPROC_SERVER, // Context
IID_IUnknown, // IID of the interface
(void**)&pInner // Interface pointer
);
if (SUCCEEDED(hr)) {
// Use pInner
}
在这个例子中,pUnkOuter 被设置为外部对象的 IUnknown 指针,以便内部对象可以进行聚合。
总结
pUnkOuter是一个指向IUnknown接口的指针,用于支持聚合。- 聚合 允许一个对象包含另一个对象,并使内部对象的接口对外部对象的客户端透明。
- 如果不使用聚合,则将
pUnkOuter设置为NULL。
通过理解 pUnkOuter 参数的用途和聚合的概念,可以更好地理解和使用 COM 编程模型中的聚合特性。
(3/5)[in] dwClsContext
dwClsContext: dw=double world;(microsofts naming style)</br>
 Cls=Class; (Class of COM object)</br>
 Context` means the environment which the object running
#...\Windows Kits\10\Include\10.0.22621.0\um\combaseapi.h
#define CLSCTX_ALL (CLSCTX_INPROC_SERVER| \
CLSCTX_INPROC_HANDLER| \
CLSCTX_LOCAL_SERVER| \
CLSCTX_REMOTE_SERVER)
and these macros are found in
#...\Windows Kits\10\Include\10.0.22621.0\shared\WTypesbase.h
typedef
enum tagCLSCTX
{
CLSCTX_INPROC_SERVER = 0x1,
CLSCTX_INPROC_HANDLER = 0x2,
CLSCTX_LOCAL_SERVER = 0x4,
CLSCTX_INPROC_SERVER16 = 0x8,
CLSCTX_REMOTE_SERVER = 0x10,
CLSCTX_INPROC_HANDLER16 = 0x20,
CLSCTX_RESERVED1 = 0x40,
CLSCTX_RESERVED2 = 0x80,
CLSCTX_RESERVED3 = 0x100,
CLSCTX_RESERVED4 = 0x200,
CLSCTX_NO_CODE_DOWNLOAD = 0x400,
CLSCTX_RESERVED5 = 0x800,
CLSCTX_NO_CUSTOM_MARSHAL = 0x1000,
CLSCTX_ENABLE_CODE_DOWNLOAD = 0x2000,
CLSCTX_NO_FAILURE_LOG = 0x4000,
CLSCTX_DISABLE_AAA = 0x8000,
CLSCTX_ENABLE_AAA = 0x10000,
CLSCTX_FROM_DEFAULT_CONTEXT = 0x20000,
CLSCTX_ACTIVATE_X86_SERVER = 0x40000,
CLSCTX_ACTIVATE_32_BIT_SERVER = CLSCTX_ACTIVATE_X86_SERVER,
CLSCTX_ACTIVATE_64_BIT_SERVER = 0x80000,
CLSCTX_ENABLE_CLOAKING = 0x100000,
CLSCTX_APPCONTAINER = 0x400000,
CLSCTX_ACTIVATE_AAA_AS_IU = 0x800000,
CLSCTX_RESERVED6 = 0x1000000,
CLSCTX_ACTIVATE_ARM32_SERVER = 0x2000000,
CLSCTX_ALLOW_LOWER_TRUST_REGISTRATION = 0x4000000,
CLSCTX_PS_DLL = 0x80000000
} CLSCTX;
(4/5)[in] riid
In REFIID riidREFIID riid:是 const IID& 的别名,表示对 IID 的引用。它的作用与 REFCLSID 类似。
#define REFIID const IID &
(5/5)[out] ppv
ppv: Pointer to Pointer to Void
002. IMMDevice::Activate
HRESULT Activate(
[in] REFIID iid,
[in] DWORD dwClsCtx,
[in] PROPVARIANT *pActivationParams,
[out] void **ppInterface
);
Parameters Description
iid: interface identifier
Exp:
const IID IID_IAudioSessionControl2 = __uuidof(IAudioSessionControl2);
1. IID_IAudioClient
2. IID_IAudioEndpointVolume
3. IID_IAudioMeterInformation
4. IID_IAudioSessionManager
5. IID_IAudioSessionManager2
6. IID_IBaseFilter
7. IID_IDeviceTopology
8. IID_IDirectSound
9. IID_IDirectSound8
10. IID_IDirectSoundCapture
11. IID_IDirectSoundCapture8
12. IID_IMFTrustedOutput
13. IID_ISpatialAudioClient
14. IID_ISpatialAudioMetadataClient
dwClsCtx: double word Class Context
pActivationParams:
ppInterface