Correctly setting up MMDevice in a DirectX project - c++

I am currently trying to piece together a shader-based music visualizer. The plan is to read data from the current MMDevice, which I'm trying to follow the documentation for, but I must be doing something wrong because I had to jump through all sorts of hoops to get even just the MMDeviceEnumerator to compile.
In order for the uuids of MMDeviceEnumerator and IMMDeviceEnumerator to be defined, I had to set #define WINAPI_FAMILY WINAPI_FAMILY_GAMES. This was also required for EDataFlow and ERole enumerations to be defined. My first question is if I've missed some configuration somewhere, or if this is the intended method of enabling these things.
Currently, I have the following code in an AudioStream class:
class AudioStream {
public:
AudioStream() {
//SUCCEEDING(CoInitializeEx(nullptr, COINIT_MULTITHREADED));
SUCCEEDING(CoCreateInstance(
__uuidof(IMMDeviceEnumerator),
NULL,
CLSCTX_ALL,
__uuidof(MMDeviceEnumerator),
(void**)&this->mmDeviceEnumerator));
SUCCEEDING(this->mmDeviceEnumerator->GetDefaultAudioEndpoint(
eRender,
eConsole,
&this->mmDevice));
}
private:
IAudioClient* audioClient = NULL;
IAudioCaptureClient* captureClient = NULL;
IMMDeviceEnumerator* mmDeviceEnumerator = NULL;
IMMDevice* mmDevice = NULL;
};
If you're familiar with what the DirectX 12 project template looks like, this object is being instantiated in the Sample3DSceneRenderer constructor. The main issue I'm having right now is the following two errors which are immediately raised during startup:
onecore\com\combase\dcomrem\resolver.cxx(2299)\combase.dll!75AA0DFF: (caller: 75B1CF2C) ReturnHr(1) tid(42a8) 80040154 Class not registered
onecore\com\combase\dcomrem\resolver.cxx(2507)\combase.dll!75B1CF4D: (caller: 75AA29E4) ReturnHr(2) tid(42a8) 80040154 Class not registered
This causes the entire app to hang, and the project template visualization to never appear (the succeeding macro exits). Does anyone have any idea why this is failing? It must have to be something with the CoCreateInstance call :(

You are writing a Universal Windows Platform (UWP) app because that's what the "built-in" DirectX 12 App project template creates in Visual Studio. UWPs do not have access to all the same APIs and IMMDevice is not part of the UWP API surface area.
The fact that you defined WINAPI_FAMILY_GAMES means you hacked the API Family Partition macros which will define the API in a UWP context, but it doesn't mean that API actually works from the AppContainer process that all UWPs run in.
You really have two options:
(1) If you want to write a UWP, then you will need to enumerate audio devices via the proper Windows Runtime APIs which are in the Windows::Devices::Enumeration namespace.
Assuming you are using C++/CX language extensions (instead of the more modern C++/WinRT projections), then this code works:
auto operation = DeviceInformation::FindAllAsync(DeviceClass::AudioRender);
while (operation->Status == Windows::Foundation::AsyncStatus::Started)
{
Sleep(100);
}
if (operation->Status != Windows::Foundation::AsyncStatus::Completed)
{
throw std::runtime_error("FindAllAsync");
}
DeviceInformationCollection^ devices = operation->GetResults();
for (unsigned i = 0; i < devices->Size; ++i)
{
using Windows::Devices::Enumeration::DeviceInformation;
DeviceInformation^ d = devices->GetAt(i);
// d->Id->Data();
// d->Name->Data();
}
Also, if you want to get access to the audio capture device from a UWP, you must add a capability to your manifest to request it via <DeviceCapability Name="microphone"/>. See Microsoft Docs.
You should take the time to read the Microsoft Docs on UWPs so you have a better idea of what's supported and what's not.
(2) If you want to write a Win32 desktop app, use the directx-vs-templates instead which include DirectX 12 starting templates for Win32 desktop apps (plus alternative DirectX templates for UWP if that's your thing).
Whichever appmodel you use, you may want to take a look at DirectX Tool Kit for Audio.
BTW, WINAPI_FAMILY_GAMES is used by the Microsoft GDK for Xbox which is for writing titles for Xbox One and Xbox Series X|S. It uses Win32 APIs and doesn't use Windows Runtime APIs, so it has the IMMDevice interface in it's API surface. See Microsoft Docs.

Related

Dynamic C++/WinRT dll loading in Win32 apps

For the past couple of days, I've been trying to learn WinRT, working with the manifests, loading the C++ or C# DLLs, etc. The next logical step for me was to try to build a plugin system that loads my WinRT DLLs that can be written in C++ or C# into my Win32 apps. This seems to be a bit more of a challenge because of unclear documentation. What makes this even more unclear to me is the underlying usage of COM and WRL which I don't have experience with. I've prepared a minimal viable example on GitHub for anyone that could help me figure this out.
A bit of elaboration on why C++/WinRT is only being used from step 6. I'm aiming at building a plugin system around dynamically/runtime loaded DLLs. I don't want to have any explicit references to DLLs or WinMD files in my projects. The goal is to basically have a Win32 application that has a statically linked abstraction project that contains the WinRT generated header files and a set of DLLs that also make use of the same abstraction project and are dynamically loaded during the runtime of the Win32 application. I've built these kind of systems before for C++ and C# but I feel lost with the WinRT layers and which methods to call when to utilize the WinRT factories correctly.
Everything seems to be working fine until step 6 when I'm not sure what would come next.
typedef HRESULT(WINAPI *dllAbstractionFactoryFp)(HSTRING activableClassId, IActivationFactory **factory);
...
// #### 0. Prep
HRESULT hRes;
// #### 1. Load DLL into memory
HMODULE hLibrary;
hLibrary = LoadLibrary(L"TicketMachine.dll");
//hLibrary = ::LoadLibraryEx(L"TicketMachine.dll", NULL, NULL);
if (!hLibrary) return EXIT_FAILURE;
// #### 2. Get the pointer to the DllGetActivationFactory of the loaded DLL
dllAbstractionFactoryFp abstractionFactoryFp;
abstractionFactoryFp = (dllAbstractionFactoryFp)GetProcAddress(hLibrary, "DllGetActivationFactory");
if (!abstractionFactoryFp) return EXIT_FAILURE;
// #### 3. Define the class name to activate
HSTRING activableClassName;
WindowsCreateString(L"TicketMachine.Ticket", 20, &activableClassName);
if (!activableClassName) return EXIT_FAILURE;
// #### 4. Set the activation factory
IActivationFactory* activationFactoryP;
hRes = (abstractionFactoryFp)(activableClassName, &activationFactoryP);
if (!activationFactoryP || hRes != S_OK) return EXIT_FAILURE;
// #### 5. Get the runtime class name to check if everything is going fine
HSTRING className;
activationFactoryP->GetRuntimeClassName(&className);
if (!className) return EXIT_FAILURE;
// #### 6. The identification GUID
IID iid = winrt::guid_of<winrt::TicketMachine::ITicketFactory>();
winrt::TicketMachine::ITicketFactory* iTicketFactory;
hRes = activationFactoryP->QueryInterface(iid, (void**) & iTicketFactory); // iTicketFactory stays uninitialized
if (!iTicketFactory || hRes != S_OK) return EXIT_FAILURE;
// ### ?. ...
;
// #### ?. Profit???
;
With an explicitly linked WinMD file I can just call the class constructor or even the ::impl::call_factory method that will instantiatie the object for me. This of course won't work if the DLL is loaded dynamically on runtime.
winrt::TicketMachine::Ticket staTicket;
// or
winrt::Windows::Foundation::IInspectable baseInterface, innerInterface;
winrt::TicketMachine::Ticket ticket = winrt::impl::call_factory<winrt::TicketMachine::Ticket, winrt::TicketMachine::ITicketFactory>([&](winrt::TicketMachine::ITicketFactory const& f) { return f.CreateInstance(baseInterface, innerInterface); });
This is the GitHub repository where I've prepared this code and which should compile and run out of the box, https://github.com/zborowa/winrt-dynamic-dll-loading
FYI, I've already seen the following questions and also found the following resources:
Loading WinRT component without referencing the DLL/assembly
Consuming a DLL(Universal Apps) from a WinRT Component
Creating WinRT component with static methods in C++ / WRL
https://devblogs.microsoft.com/oldnewthing/20201118-00/?p=104459
https://gist.github.com/clarkezone/43e984fb9bdcd2cfcd9a4f41c208a02f
These Aren't the COM Objects
You're Looking For
And many more...

C++ mapi outlook call to ResolveMessageClass fails randomly?

I am working on my outlook plugin using C++ MAPI.
On one particular button click I want to open New Mail window for which I am following below approach. But to my surprise, IMAPIFormMgr::ResolveMessageClass() function fails. That too randomly. At times it works fine and at other times it fails. Couldn't figure out what could be the cause for the same.
Any pointer, idea?
My code flow is something like below
Note: It's not full code, just little bit of overview.
I have printed all the variables and poiters in my log fine and non of them is NULL or garbage or like.
Still it fails at ResolveMessageClass() function only. That too strangely it fails randomly. many a time it works like champ and suddenly it shows some error in ResolveMessageClass()
Code
CComPtr<IMessage> mapiMessage;
hRes = spOutboxFolder->CreateMessage(&IID_IMessage, 0, &mapiMessage);
CComPtr<IMAPIFormInfo> pFormInfo;
std::wstring szMessageClass(L"IPM.Note");
hRes = pFormMgr->ResolveMessageClass(
wstringTostring(szMessageClass).c_str(), // Message class is ALWAYS ANSI --> Never Unicode
0,
spOutboxFolder.get(),
&pFormInfo);
CComPtr<IPersistMessage> pPersistMessage;
hRes = pFormMgr->CreateForm(NULL, 0L, pFormInfo, IID_IPersistMessage, (LPVOID*)&pPersistMessage);
ULONG_PTR ul = 0;
hRes = spSession->PrepareForm(NULL, mapiMessage, &ul);
hRes = spSession->ShowForm(
NULL, //(ULONG)parent->GetSafeHwnd(),
msgStore,
spInboxFolder.get(),
NULL,
ul,
NULL,
MAPI_POST_MESSAGE,
pPropsMsg[MSG_STATUS].Value.l,
pPropsMsg[MSG_FLAGS].Value.l,
pPropsMsg[MSG_ACCESS].Value.l,
pPropsMsg[MSG_CLASS].Value.lpszA);
Thanks in advance
If your code is in an Outlook addin, why not use OOM to create and display message? In this particular case, using MAPI does not buy you anything.
Use the Outlook object model instead of Extended MAPI. Creating a COM add-in involves two major steps:
Implement the IDTExtensibility2 interface in a class module of a dynamic link library (DLL).
Register the COM add-in.
The key to understanding COM add-in development is the IDTExensibility2 interface. This interface is used by all Office applications to communicate with a COM add-in. This ensures a common initialization mechanism and an ability to pass in the application's object model so that the COM add-in can communicate with the Office application. Listing 23-1 shows the IDTExtensibility2 interface.
public interface IDTExtensibility2
{
void OnAddInsUpdate(ref System.Array custom);
void OnBeginShutdown(ref System.Array custom);
void OnConnection(object Application,
Extensibility.ext_ConnectMode ConnectMode,
object AddInInst, ref System.Array custom);
void OnDisconnection(Extensibility.ext_DisconnectMode RemoveMode,
ref System.Array custom);
void OnStartupComplete(ref System.Array custom);
}
As you can see the OnConnection method gets an instance of the host application instance. So, that is where you could start from developing your add-in.
Read more about the OOM in the Outlook object model overview section of MSDN.

WPD Object Filename Truncated at '.'

In my project, I'm using the Windows Portable Device (WPD) API to enumerate the contents of a mobile device. WPD API Enumeration Guide. I'm able to enumerate over each object and view their properties as shown in the API programming guide. WPD API Properties Guide
However when I try to get an object's name that has a . within the name, the returned value is truncated at that .
HRESULT hr = objectProperties->GetStringValue(WPD_OBJECT_NAME, &strOriginalFileName);
if(FAILED(hr))
return false;
PWSTR wideStr = strOriginalFileName;
char buffer[20];
wcstombs(buffer, wideStr, 20);
qDebug() << buffer;
So for example, an object (folder on the device) with the name of com.example is returned as com. This becomes an obvious issue when I'm trying to locate a specific filepath on the device.
I can't seem to figure out what's wrong. Am I misunderstanding how the filename actually is? Is example another property or something within the com object? I'm very confused.
EDIT:
So I used the WPD API sample software to retrieve all the object properties of the com.example object and you can see that WPD itself cannot get the full folder name.
Thanks for your time!
The WPD Application Programming Reference refers following 3 NAMEs.
WPD_OBJECT_HINT_LOCATION_DISPLAY_NAME: A friendlier name, mostly intended for display
WPD_OBJECT_NAME: The name of the object on device.
WPD_OBJECT_ORIGINAL_FILE_NAME: The original filename of the object on device.
The MS code sample in C++ uses WPD_OBJECT_ORIGINAL_FILE_NAME to get to the actual file name (underneath the object) while transferring files from device to PC.
I modified the MS code sample (to enumerate object properties) and it showed me the actual file name (nothing truncated from the filename com.ef1.first.second)
I used:
Windows Windows 7 Ultimate (without SP1)
Visual Studio 2013
Android 4.4.4 (Moto-E)
Connection type: MTP
Memory type: Internal Memory as well as External (SD Card)
I wouldn't be surprised if it doesn't work on some combination of Windows versions, Windows SDK versions, android versions, Connection types (MTP, PTP, USB Mass Storage).
Here is the part of code that I modified (and that is how it worked).
// Reads properties for the user specified object.
void ReadContentProperties(_In_ IPortableDevice* device)
{
//.... Edited for brevity
tempHr = propertiesToRead->Add(WPD_OBJECT_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
// Here is the added code
tempHr = propertiesToRead->Add(WPD_OBJECT_ORIGINAL_FILE_NAME);
if (FAILED(tempHr))
{
wprintf(L"! Failed to add WPD_OBJECT_ORIGINAL_FILE_NAME to IPortableDeviceKeyCollection, hr= 0x%lx\n", tempHr);
}
//.... Edited for brevity
}

Class not registered (0x80040154) on my own COM component

I am learning basics in COM, so I try to write simple COM component in VS2010 C++ Windows 7.
I created dll for component, registered it using following REG-file:
REGEDIT
HKEY_CLASSES_ROOT\Math.Component.1 = Chapter 6 Math Component
HKEY_CLASSES_ROOT\Math.Component.1\CurVer = Math.Component.1
HKEY_CLASSES_ROOT\Math.Component.1\CLSID = {A888F560-58E4-11d0-A68A-0000837E3100}
HKEY_CLASSES_ROOT\CLSID\{A888F560-58E4-11d0-A68A-0000837E3100} = Chapter 6 Math Component
HKEY_CLASSES_ROOT\CLSID\{A888F560-58E4-11d0-A68A-0000837E3100}\ProgID = Math.Component.1
HKEY_CLASSES_ROOT\CLSID\{A888F560-58E4-11d0-A68A-0000837E3100}\VersionIndependentProgID = Math.Component
HKEY_CLASSES_ROOT\CLSID\{A888F560-58E4-11d0-A68A-0000837E3100}\InprocServer32 = D:\Proga\COM\Debug\server.dll
HKEY_CLASSES_ROOT\CLSID\{A888F560-58E4-11d0-A68A-0000837E3100}\NotInsertable
In dll I exported (stubs for last two)
DllGetClassObject
DllCanUnloadNow
DllRegisterServer
DllUnregisterServer
In my COM client CLSIDFromProgID( szWideProgID, &clsid ); works as expected, returning {A888F560-58E4-11d0-A68A-0000837E3100}.
But when I try to get access to IClassFactory REGDB_E_CLASSNOTREG CoGetClassObject(clsid, CLSCTX_INPROC, NULL, IID_IClassFactory, (void **)&pCF) I get (0x80040154) error.
Both server and client compiled for Win32 target platform (although I tried x64 too). Source code I got from tutorial, so I don't understand what is wrong.
The tutorial you found no doubt is old, written long before 64-bit Windows came around. Registry keys need to be written to HKLM\Software\Wow6432Node\Classes for 32-bit COM servers, to HKLM\Software\Classes for 64-bit COM servers. Your .reg file isn't going to take care of that. You must avoid the HKEY_CLASSES_ROOT alias and replace it with the explicit HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Classes to avoid accidents.
Use the SysInternals' ProcMon utility if you still have problems, you'll see your test program searching for the registry keys and the DLL.

Problems accessing uccapi.dll COM interface C++

I'm working on a project involving the Microsoft Unified Communications Client API; uccapi.dll. I'm also using Codegear C++Builder 2010, not Visual Studio. After registering the dll with regsvr32 and importing it as type library into C++Builder 2010, uccapi_tlb- and uccapi_ocx-files were generated. When having imported these into my new project I'm trying to follow the msdn guideline for creating a Office Communicator Client able of signing into the Office Communication server.
In this regard I have two questions:
What is the correct way of accessing the com-interfaces made available through the ocx?
I've so far found several ways of creating access points, such as.
TCOMIUccPlatform plat;
plat = CoUccPlatform::Create();
and
IUccPlatformPtr im;
im = CreateComObject(CLSID_UccPlatform);
and
IUccPlatform* pIUccPlatform;
hr = CoCreateInstance(CLSID_UccPlatform,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IUccPlatform),
(void**)&pIUccPlatform);
and
IUccPlatformPtr pIPlat;
pIPlat.CreateInstance(__uuidof(IUccPlatform));
The three first seem to work well. The latter will give me an Assertion failed: intf!=0 error with 0×40000015 exception. Using any of the three top ones I can access methods and initialize the platform interface.
However when trying any of the same tactics to access any other interface, such as IUccContext, IUccUriManager or IUccUri, all of which have a clsid defined in the _tlb.h file, I either get a "class not registered" error in the first two cases, or a hresult failure in the third case. Which brings me to my next question.
Using ole-viewer all interfaces are registered as they should. Why wouldn't all co-creatable classes in the dll be registered when registering the dll? And what could be the reasons why don't they act similarly?
Edit1 from UCCAPILib_tlb.h:
//
// COCLASS DEFAULT INTERFACE CREATOR
// CoClass : UccPlatform
// Interface: TCOMIUccPlatform
//
typedef TCoClassCreatorT<TCOMIUccPlatform, IUccPlatform, &CLSID_UccPlatform, &IID_IUccPlatform> CoUccPlatform;
//
// COCLASS DEFAULT INTERFACE CREATOR
// CoClass : UccUriManager
// Interface: TCOMIUccUriManager
//
typedef TCoClassCreatorT<TCOMIUccUriManager, IUccUriManager, &CLSID_UccUriManager, &IID_IUccUriManager> CoUccUriManager;
This issue is already being discussed in detail in the Embarcadero forums.