Introduction
This is a demonstration of how to wrap and convert the ActivateAudioInterfaceAsync
, IActivateAudioInterfaceAsyncOperation
, IActivateAudioInterfaceCompletionHandler
programming patterns to a task
object and use it.
Background
One should be familiar with C++, WRL, IDL and the MultiMedia (MM) device API.
Using the Code
The code is relatively short but care must be taken to keep a reference on the object and make certain the UI thread is used for the initial call as well as the operation completion call. An event
object is used as a signal mechanism just as is done in the task library.
#ifdef __cplusplus_winrt
namespace Concurrency
#else
namespace Concurrency_winrt
#endif
{
template<typename TCallback>
struct ActivateAudioInvokeHelper :
public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags
<Microsoft::WRL::Delegate>, Microsoft::WRL::FtmBase>
{
explicit ActivateAudioInvokeHelper(TCallback callback) throw() : callback_(callback)
{
}
STDMETHOD(ActivateCompleted)(IActivateAudioInterfaceAsyncOperation *activateOperation)
{
#if _MSC_VER >= 1800
return Microsoft::WRL::DelegateTraits<Microsoft::WRL::DefaultDelegateCheckMode>::CheckReturn(
callback_(activateOperation));
#else
return callback_(activateOperation);
#endif
}
TCallback callback_;
};
template<typename>
Microsoft::WRL::ComPtr<IActivateAudioInterfaceCompletionHandler> ActivateAudioCallback(TCallback callback) throw()
{
return Microsoft::WRL::Make<ActivateAudioInvokeHelper<TCallback>>(callback);
}
__declspec(noinline)
auto create_audio_task(_In_ LPCWSTR deviceInterfacePath, _In_ REFIID riid,
_In_opt_ PROPVARIANT *activationParams, IUnknown** activatedInterface) -> task<void>
{
Microsoft::WRL::ComPtr<IActivateAudioInterfaceAsyncOperation> pOperation;
std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();
Microsoft::WRL::ComPtr<IActivateAudioInterfaceCompletionHandler> pHandler = ActivateAudioCallback(
[activatedInterface, _completed](IActivateAudioInterfaceAsyncOperation *activateOperation) -> HRESULT {
HRESULT innerhr;
HRESULT hr = activateOperation->GetActivateResult(&innerhr, activatedInterface);
if (FAILED(hr)) return hr;
_completed->set();
return innerhr;
});
HRESULT hr = ActivateAudioInterfaceAsync(deviceInterfacePath, riid,
activationParams, pHandler.Get(), pOperation.GetAddressOf());
if (FAILED(hr)) return task<void>();
task<void> _CreatedTask = create_task DEFINE_RET_TYPE(void)(
[_completed]()->DEFINE_RET_FORMAL(void) { _completed->wait(); RET_VAL_BASE });
#if _MSC_VER < 1800
_CreatedTask._SetTaskCreationAddressHint(_ReturnAddress());
#else
task_options _TaskOptions = task_options();
details::_get_internal_task_options(_TaskOptions)._set_creation_callstack(_CAPTURE_CALLSTACK());
#endif
return _CreatedTask;
}
}
</void>
Using the code is as simple as calling create_audio_task
to return a task object based on the arguments to ActivateAudioInterfaceAsync
without the overhead of worrying about the operation parameter which is taken care of completely within the class.
Points of Interest
The programming pattern here is identical for C++/CX which also is deficient in terms of library handling for this interface.
This could be integrated directly into ppltasks.h or using custom native C++ ppltasks_winrt.h which can be found in a separate tip. However, given that this particular interface does not match the AsyncBase
pattern, there is less motivation to think in terms of such a change.
Unlike with normal WRL event handler delegates, in this case FtmBase
must be inherited from to bring in the IAgileObject
interface, otherwise an error E_ILLEGAL_METHOD_CALL
(0x8000000E
) will be returned.
Windows Phone 8.1 does not support more detailed audio interfaces such as IAudioSessionControl
and IAudioMeterInformation
yet it does support the more generic IAudioClient
so when developing universal applications, it is good to take this into account before using interfaces not supported on the phone.
History
- 21st January, 2014: Initial version
- 7th May, 2014: Updated for Visual Studio 2013 and Windows 8.1
- Support confirmed for Visual Studio 2012/2013 and now 2015 as well as Windows 8/8.1/10