How do I pass DEVINTERFACE_AUDIO_CAPTURE into ActivateAudioInterfaceAsync? - c++

I was looking at the docs for the WASAPI ActivateAudioInterfaceAsync() function and it mentions passing DEVINTERFACE_AUDIO_CAPTURE as deviceInterfacePath to activate the interface on the default audio capture device. That seems like a good practice since the MediaDevice::GetDefaultAudioCaptureId(AudioDeviceRole::Default) call I would otherwise make to get the deviceInterfacePath parameter (which is used in the WASAPI sample) is synchronous - even though it may take a few seconds in some cases, blocking the UI thread and potentially getting your app to be killed.
Unfortunately the docs don't show a sample, especially for how to pass the GUID as the LPCWSTR deviceInterfacePath to ActivateAudioInterfaceAsync.
How can I do it?

What I managed to code up:
Includes to add:
#include <initguid.h>
#include <mmdeviceapi.h>
Initialization:
ComPtr<IActivateAudioInterfaceAsyncOperation> asyncOp; /*needed to get ActivateCompleted callback*/
PWSTR audioCaptureGuidString;
StringFromIID(DEVINTERFACE_AUDIO_CAPTURE, &audioCaptureGuidString);
// This call must be made on the main UI thread. Async operation will call back to
// IActivateAudioInterfaceCompletionHandler::ActivateCompleted
HRESULT hr = ActivateAudioInterfaceAsync(
audioCaptureGuidString, /* deviceInterfacePath (default capture device) */
__uuidof(IAudioClient2), /*riid*/
nullptr, /*activationParams*/
this, /*completionHandler*/
&asyncOp /*createAsync*/);
CoTaskMemFree(audioCaptureGuidString);
// Windows holds a reference to the application's IActivateAudioInterfaceCompletionHandler interface
// until the operation is complete and the application releases the IActivateAudioInterfaceAsyncOperation interface

Related

When is calling CoInitialize required for a Windows console application

The code below, derived from https://learn.microsoft.com/en-us/windows/desktop/shell/folder-info#determining-an-objects-parent-folder, works as expected when compiled and run via Visual Studios 2017:
#include "stdafx.h"
#include <shlobj.h>
#include <shlwapi.h>
#include <objbase.h>
#pragma comment(lib, "shlwapi")
int main()
{
IShellFolder *psfParent = NULL;
LPITEMIDLIST pidlSystem = NULL;
LPCITEMIDLIST pidlRelative = NULL;
STRRET strDispName;
TCHAR szDisplayName[MAX_PATH];
HRESULT hr;
hr = SHGetFolderLocation(NULL, CSIDL_SYSTEM, NULL, NULL, &pidlSystem);
hr = SHBindToParent(pidlSystem, IID_IShellFolder, (void **)&psfParent, &pidlRelative);
if (SUCCEEDED(hr))
{
hr = psfParent->GetDisplayNameOf(pidlRelative, SHGDN_NORMAL, &strDispName);
hr = StrRetToBuf(&strDispName, pidlSystem, szDisplayName, sizeof(szDisplayName));
_tprintf(_T("%s\n"), szDisplayName);
}
psfParent->Release();
CoTaskMemFree(pidlSystem);
Sleep(5000);
return 0;
}
If I replace CSIDL_SYSTEM with CSIDL_MYDOCUMENTS, though, the GetDisplayNameOf method call fails with:
onecore\com\combase\objact\objact.cxx(812)\combase.dll!74EA3270: (caller: 74EA201B) ReturnHr(1) tid(d4c) 800401F0 CoInitialize has not been called.
onecoreuap\shell\windows.storage\regfldr.cpp(1260)\windows.storage.dll!76FE4FA3: (caller: 76E9F7EE) ReturnHr(1) tid(d4c) 80040111 ClassFactory cannot supply requested class
Adding CoInitialize(NULL); before the call to SHGetFolderLocation fixes the issue.
Why is calling CoInitialize required in one case but not the other?
Also, it seems like CoInitialize should always be called, but it's interesting that the sample code doesn't call it. I'm curious why this is the case. I couldn't get the sample code compiling as is - <iostream.h> couldn't be found, which is why I replaced the cout printing code with a call to _tprintf... Maybe that's an indication of the problem? Does the C++ runtime call CoInitialize for you, and maybe VS is trying to build a C application for me or something (like how on Linux, compiling with gcc and g++ has different implications).
As a rule, you should initialize COM/OLE before creating shell COM objects that inherit from IUnknown, use drag & drop etc. This also applies to functions that might use COM internally which could in theory be most of the SH* functions in shell32 and shlwapi.
Why did it work with CSIDL_SYSTEM?
The Windows 95 shell could run without loading COM/OLE. To do this it provided its own mini-COM implementation. Shell extensions could mark themselves as not requiring real COM and things implemented inside shell32 would call a special CoCreateInstance that tries to load things directly from shell32. This was to avoid loading ole32.dll because it is a very big file to load on a Intel 386 machine with 4 MiB of RAM (Windows 95 minimum requirements).
The IShellFolder implementation that deals with the filesystem is implemented in shell32 and does not require COM and is therefore able to handle a path like c:\Windows\system32.
CSIDL_MYDOCUMENTS however, is not a normal folder, it is a namespace extension and parts of its implementation is in mydocs.dll. And as you found out, parts of it does require COM.
All of this is of course a implementation detail and you should never assume that any of this is going to work without initializing COM.
SHGetFolderLocation may delegate execution to an extension that requires COM initialization. Although the documentation does not explicitly say so, you can find a remark about that for ShellExecute which is part of the same module (shell32.dll).
Because ShellExecute can delegate execution to Shell extensions (data
sources, context menu handlers, verb implementations) that are
activated using Component Object Model (COM), COM should be
initialized before ShellExecute is called. Some Shell extensions
require the COM single-threaded apartment (STA) type. In that case,
COM should be initialized as shown here:
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
There are certainly instances where ShellExecute does not use one of
these types of Shell extension and those instances would not require
COM to be initialized at all. Nonetheless, it is good practice to
always initalize COM before using this function.
You can use the following helper class to automatically initialize the COM library on the current thread.
class COMRuntime
{
public:
COMRuntime() {
::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
}
~COMRuntime() {
::CoUninitialize();
}
};
Then just declare one instance of that class:
int main()
{
COMRuntime com;
// the rest of your code
}

ACPI _PS0 failing with UNINITIALIZED_ARG

I have a touchscreen controller (which is an I2C slave) that I need to enable via APCI. This should be done by calling the _PS0 ACPI method. I call this method by using AcpiEvaluateObject with no arguments and no return values.
AcpiEvaluateObject(nullptr, (ACPI_STRING)"\\_SB.I2C4._PS0", nullptr, nullptr); // returns AE_OK
AcpiEvaluateObject(nullptr, (ACPI_STRING)"\\_SB.I2C4.TCS2._PS0", nullptr, nullptr); // returns AE_AML_UNINITIALIZED_ARG
When calling this method on the parent object (I2C4), everything goes fine but calling it on the touch screen controller (TCS2), it fails. What also makes me wonder is that it returns AE_AML_UNINITIALIZED_ARG even though it doesn't take any args (according to the DSDT).
Calling the _CRS method on the same object also works without any problems. I also looked into the Linux kernel source how they change ACPI power states and they use the exact same mechanism. It boils down to the use of acpi_evaluate_object in acpi_dev_pm_explicit_set which also seems to work on the touchscreen device.
I'm not using Linux, but Genode and the Acpica library.
what am I missing to successfully enable the touchscreen device via ACPI? Is there something the Linux kernel is initializing implicitly (I couldn't find something like this)?

Why use the same IDXGIFactory for Device and Swap Chain

The reference of the IDXGIFactory interface tells me, that in order to create a swap chain, I might use the same factory that was used to create the Direct3D device:
Because you can create a Direct3D device without creating a swap chain, you might need to retrieve the factory that is used to create the device in order to create a swap chain.
It also gives the following code exmaple:
IDXGIDevice * pDXGIDevice;
hr = g_pd3dDevice->QueryInterface(__uuidof(IDXGIDevice), (void **)&pDXGIDevice);
IDXGIAdapter * pDXGIAdapter;
hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);
IDXGIFactory * pIDXGIFactory;
pDXGIAdapter->GetParent(__uuidof(IDXGIFactory), (void **)&pIDXGIFactory);
The article is very spare and when trying to fully understand it, the following two questions arose, whereas the first question is the main question (concerning the title of this thread):
Why do I have to use the same factory that was used to create the Direct3D device in order to create the swap chain? Does the factory instance maintain an internal state which is important or is it just to avoid to create another instance of the factory which consumes resources?
Also, within the code sample I struggle with the following line of code:
hr = pDXGIDevice->GetParent(__uuidof(IDXGIAdapter), (void **)&pDXGIAdapter);
To me, it is not logical, that the IDXGIAdapter is the the parent of the IDXGIDevice. Otherwise I would expect the IDXGIAdapter to have a method like CreateDevice which would make the adapter the parent of the device. But that is not the case. Why is the adapter the parent of the device?
The root of the problem goes back to DXGI 1.0 (Direct3D 10) and DXGI 1.1 (Direct3D 11). If you tried to use a IDXGIFactory created by both CreateDXGIFactory and CreateDXGIFactory1 in the same process, things would go pear-shaped pretty quick.
The other issue is that there's an implicit factory created when you use the 'default device' with Direct3D 10.x or 11.x device creation. The 'magic code sequence' above lets you get the factory created implicitly.
The second step in the docs should be dxgiDevice->GetAdapter not dxgiDevice->GetParent. I'll file a doc bug on that.
This is even easier to do if using Microsoft::WRL::ComPtr:
// First, retrieve the underlying DXGI Device from the D3D Device
ComPtr<IDXGIDevice1> dxgiDevice;
DX::ThrowIfFailed(m_d3dDevice.As(&dxgiDevice));
// Identify the physical adapter (GPU or card) this device is running on.
ComPtr<IDXGIAdapter> dxgiAdapter;
DX::ThrowIfFailed(dxgiDevice->GetAdapter(dxgiAdapter.GetAddressOf()));
// And obtain the factory object that created it.
ComPtr<IDXGIFactory1> dxgiFactory;
DX::ThrowIfFailed(dxgiAdapter->GetParent(__uuidof(IDXGIFactory1), &dxgiFactory));
Direct3D 12
Note that this whole area has been cleaned up with Direct3D 12, and you can't use QI to get an IDXGIDevice from a ID3D12Device.
References
DirectX Graphics Infrastructure (DXGI): Best Practices
Anatomy of Direct3D 11 Create Device

Task continuation with context "use_current" does not work

I have tried looking for answer to this since three days back. It is either I have done something fundamentally wrong (that there's an obvious mistake) or the thing is too new to have any references, I can't seem to figure why simple cases like this would fail.
The following code uses PPL task library in C++ in a Windows Store application, simulating a file loading operation that takes 2 seconds before breaking out of the loop (of course this is to illustrate the problem with minimal codes, the real loop does other rendering to show progress, too).
The continuation part of the code (i.e. "fileLoaded = true") never gets called if I use "use_current" as the continuation context:
bool fileLoaded = false;
while (!fileLoaded)
{
concurrency::task<void>([this]()
{
// Simulate file load delay
concurrency::wait(2000);
}).then([this, &fileLoaded]()
{
fileLoaded = true; // This never gets executed!
// If the following is changed to "use_default" or
// "use_arbitrary", then this continuation gets called.
}, concurrency::task_continuation_context::use_current());
concurrency::wait(50);
}
The same code works if I use "use_default" or "use_arbitrary", and properly set "fileLoad" to "true". This code can be placed anywhere in a Windows Store C++ app (e.g. Direct2D app), and it would fail (I have placed it in "DirectXPage::DirectXPage" constructor body, which I expect it to be the main UI thread). Did I do something horribly wrong?
Thanks in advance for your help! :)
Since you are calling .then() on the UI thread, use_current() will cause the continuation to be scheduled for execution on the UI thread.
However, that continuation cannot run until the UI thread is free (i.e., when it is not doing any work). But, in your example, the UI thread is never free: the DirectXPage constructor is running on the UI thread. The DirectXPage constructor will not return until the continuation executes and the continuation cannot execute until the DirectXPage constructor returns.
You need to allow the constructor to return so that the UI thread is free to do other work (like execute the continuation).
Also note that if you are using fileLoaded for cross-thread communication, you need to use an std::atomic<bool> or some other proper synchronization object. A simple bool is insufficient for synchronization.

Need help with events in COM in pure C++!

guys! Very important question:
Please, look at my project (300Kb). I can`t use MFC/ATL, pure C++ only.
I have COM library (niapi.dll), but no sources/headers available, dll only.
There is class for connecting to server (NiApi::SrvrSession), class has login event handler (NiApi::SrvrSession::OnLogin).
I used
#import "NiApi.dll"
to generate wrappers/information,
then
ISrvrSessionPtr session(L"NiApi.SrvrSession");
to create object, then trying
session->put_OnLogin();
to assign events, but there is no one put_On or such member.
niapi.tlh have _ISrvrSessionEvents struct inside, but it have no relations with SrvrSession.
I need to use events from NiApi::SrvrSession for handling connection status.
Please help or my boss kill me! (sorry for broken english, I read better than speak;)
COM events are handled via connection points. You need to write your own COM object that implements whichever event interface you are interested in. Then you need to connect it to the COM object that fires the events. First you QI the COM object for its IConnectionPointContainer, then find the corresponding connection point of the GUID of the event interface. The you call its Advise method to connect it to your event sink.
class CSrvrSessionEvents: public _ISrvrSessionEvents
{
public:
HRESULT OnLogin(long LoginResult)
{
// do something
return S_OK;
}
// implement rest of _ISrvrSessionEvents
};
ISrvrSession* pSrvrSession = ...; // get ISrvrSession from somewhere
_ISrvrSessionEvents* pSrvrSessionEvents = new CSrvrSessionEvents();
IConnectionPointContainer* pCPC = NULL;
pSrvrSession->QueryInterface(IID_IConnectionPointContainer, &pCPC);
IConnectionPoint* pCP = NULL;
pCPC->FindConnectionPoint(__uuidof(_ISrvrSessionEvents), &pCP);
DWORD dwCookie = 0;
pCP->Advise(pSrvrSessionEvents, &dwCookie);
pSrvrSession->Connect(); // I assume this fires some events
pCP->Unadvise(dwCookie);
What is really necessary, is to carefully read
codeproject_TEventHandler.
All explained here.
The put_ prefix is the default prefix for the raw interface (customizable via the raw_property_prefixes attribute). Since you are not using the raw interface, use session->OnLogin=... instead.
For event handling see ADO Events Model Example (VC++)