Problems accessing uccapi.dll COM interface C++ - 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.

Related

Correctly setting up MMDevice in a DirectX project

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.

Can't find COM object from C++, although Guid it's registered

First of all happy new year to everyone, hope you're doing well!
I'm working on a C++ project in which I need to call a C# DLL I created following the first answer of this post. Once I have the DLL, I need to call it from Qt, so by using dumpcpp and the .tlb file generated by regasm, I managed to get the .cpp and .h files to use my classes. Just as a reference, the namespace of the classes is Wrapper, and the main class is Device with guid {DD4A4896-C105-4C60-839B-B18C99C8FE15}.
Once I have the generated files to use the DLL, if I try to create a Wrapper:: Device instance on Qt, I get the following error:
QAxBase::setControl: requested control {dd4a4896-c105-4c60-839b-b18c99c8fe15} could not be instantiated
QAxBase::qt_metacall: Object is not initialized, or initialization failed
It doesn't give any more information, so I tried to check if the guid was stored on the system registry (I used the regasm command explained on the previously quoted post, and It said that it was successful, but you never know). Opening Registry editor and searching for the Guid revealed that it's present at: Computer\HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{DD4A4896-C105-4C60-839B-B18C99C8FE15}, which, as far as I know, is the right route for these guids, and it points to the right DLL.
I though It may be due to some kind ActiveQt problem, and as the previously quoted post explained how to use that DLL from VS C++, I decided to give it a try, using this as an another reference. I've finished with this code, which is supposed to create an instance of my Device object
#include <iostream>
#include <atlstr.h>
#import "C:\Users\javie\Documents\Wrapper\Wrapper\bin\x86\Release\netstandard2.0\Wrapper.tlb" named_guids raw_interfaces_only
inline void TESTHR(HRESULT x) { if FAILED(x) _com_issue_error(x); };
int main()
{
try
{
TESTHR(CoInitialize(0));
Wrapper::IDevicePtr devPtr = nullptr;
TESTHR(devPtr.CreateInstance("{DD4A4896-C105-4c60-839B-B18C99C8FE15}"));
}
catch (const _com_error& e)
{
CStringW out;
out.Format(L"Exception occurred. HR = %lx, error = %s", e.Error(), e.ErrorMessage());
MessageBoxW(NULL, out, L"Error", MB_OK);
}
CoUninitialize();// Uninitialize COM
std::cout << "Hello World!\n";
}
However, this doesn't work either, the createInstance method throws an exception of Class not registered and HR=80040154. Again, according to Registry editor, the class is registered, so I don't understand the error. I've also tried with devPtr.CreateInstance("Wrapper.Device"), devPtr.CreateInstance("Wrapper::Device") or `devPtr.CreateInstance("Wrapper::CLSID_Device") as the links I posted suggest, but in those cases I get another exception with HR=800401f3 and message Invalid class string.
It doesn't matter whether VS or Qt Creator are opened as administrator or not, I get the exact same error.
I have run out of ideas, and I really need to be able to use that DLL from Qt using the files generated by dumpcpp.
Does any one know what could be happening? It feels quite strange to me.
If your C++ application is 64-bit, that's the answer right there, because your C# component is 32-bit (or MSIL but registered to the 32-bit hive). In situations like these, a simple test using VBScript is always useful.
Write a simple VB Script (test.vbs)
Dim obj
Set obj = CreateObject("Wrapper.Device") ' or whatever your ProgID is
MsgBox TypeName(obj)
Now, run this macro 2 ways: with 32-bit and 64-bit versions of VBScript:
32-bit > c:\windows\SysWow64\cscript.exe test.vbs
64-bit > c:\windows\system32\cscript.exe test.vbs
This is assuming your C# component is dispatch compatible. If it's not, then it will still give you differing results that you can use to debug.
Assuming automation/IDispatch compatible, one will work and one won't if you have registered your component correctly.
Have you registered correctly? When I use regasm, I always use the the switches /tlb /codebase when registering the C# component for COM.
Ok, in case someone find the same error, I'll explain the solution I found.
The problem was that in my case, the C# class I developed depended on another 32 bits dll which was not registered on my PC. Once I registered the other dll, everything worked fine.
I don't know why VS kept telling me that the class was not registered when my class itselft was registered, it was one of its dependencies that wasn't registered.
Anyway, I discovered this thanks to Joseph's comments, so thanks a lot for your help.

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.

C# COM dll has REGDB_E_CLASSNOTREG error in C++ project

I have a C# dll that I properly have registered for COM Interop, and made COM visible. Using cppbuilder, I imported the type library, which generated the wrapper classes, and I am now attempting to use to create an instance of my C# class. However, I'm getting a REGDB_E_CLASSNOTREG error in my C++ code. I verified the dll is in the registry, and even re-registered it with regasm. No change. What could I be missing?
Here is my C++ code:
_MyClassPtr obj;
HRESULT hr = obj.CreateInstance(__uuidof(MyClass));
//now hr equals REGDB_E_CLASSNOTREG
I've also tried it as such:
IMyClass* obj;
HRESULT hr = CoCreateInstance(__uuidof(MyClass), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMyClass), (void**) &obj);
//same result, hr equals REGDB_E_CLASSNOTREG
I do have one additional dependency in the C# app. I registered it for COM as well with no difference, but did not import it's type library into the C++ project.
UPDATE: based on the comments below, I discovered that CreateInstance is looking up the class guid in the following places in the registry:
HKCU\Software\Classes\Wow6432Node\CLSID\{guid}
HKCR\Wow6432Node\CLSID\{guid}
HKCU\Software\Classes\CLSID\{guid}
HKCR\CLSID\{guid}
But, going through the registry, the only entry under any of the CLSID nodes that is related to my assembly is the guid for the assembly itself, which is, of course, different than the guid for the class, or the interface.
I've manually run regasm under both x86 and x64 mode to try to acheive different results. No differences.
Well, I found out what would work.
IMyClassPtr obj;
HRESULT hr = obj.CreateInstance(CLSID_MyClass);
CLSID_MyCLass was a guid constant in the generated MyClass_TLB.cpp file. Using it instead of __uuidof(...) for the class types enabled everything to start working correctly.

Getting the replica ID of Access database using DAO

I've recently ported an MFC project form VS6 to VS2005. The VS6 project linked ddao35d.lib (DAO 3.5) which is no longer compatible with the 'new' MFC used in VS2005. To get around this I'm now including afxdao.h and changing my database classes from CdbDatabase to CDaoDatabase as recommended by other posts : -
http://www.experts-exchange.com/Programming/Languages/CPP/Q_22465486.html
However, there was a member function in CdbDatabase called GetReplicaID() which is no longer in CDaoDatabase. Does anyone know how to get the replica ID of an Access database using the CDaoDatabase class or otherwise?
Here are the important exerpts from that post: -
"As of Visual C++ .NET, the Visual C++ environment and wizards no longer support DAO (although the DAO classes are included and you can still use them). Microsoft recommends that you use OLE DB Templates or ODBC for new projects. You should only use DAO in maintaining existing applications.
The DAO MFC libraries, including ddao35d.lib, are part of PlatformSDK and are not compatible with the new MFC. You are expected to #include and it will link daouuid.lib."
...
"Adding the and daouuid.lib was the trick. PLUS: changing the declaration of CdbLastOLEError TO CDaoErrorInfo. The CdbLastOLEError is still in , but apparently no longer in the ddao35.lib. Changing to CDaoErrorInfo and linking with the addition of daouuid.lib has corrected the linker error."
I've found the solution.. You can access the DAO directly through the m_pDAODatabase member in the CDaoDatabase class. EG: -
CDaoDatabase dbDatabase;
COleVariant varReplicaID( "", VT_BSTRT );
HRESULT hr;
hr = dbDatabase.m_pDAODatabase->get_ReplicaID(& V_BSTR(&varReplicaID) );
For more details see
http://msdn.microsoft.com/en-us/library/1s0dx3s7.aspx