ATL COM class registration .rgs file defaults - c++

I'm creating a COM server executable, and have run into a problem with class registration. When I created my class object, the automatically generated .rgs file looked like this:
HKCR
{
NoRemove CLSID
{
ForceRemove {4C6DAD45-64B4-4C55-81C6-4CE125226421} = s 'Test Class'
{
ForceRemove Programmable
LocalServer32 = s '%MODULE%'
{
val ServerExecutable = s '%MODULE_RAW%'
}
TypeLib = s '{EAA173CA-BDBC-463A-8B7A-B010EFA467BC}'
Version = s '1.0'
}
}
}
This created the registry entries correctly for the CLSID. However, when attempting to call CoCreateInstance externally, I was experiencing a hang.
hr = CoCreateInstance( __uuidof(Test), NULL, CLSCTX_ALL, __uuidof(ITest), (void**)&pTest);
After looking at a few other projects for examples, I noticed that they all had registry entries of the type:
HKEY_CLASSES_ROOT\<MODULE>.<CLASS>
HKEY_CLASSES_ROOT\<MODULE>.<CLASS>\CLSID
I investigated the .rgs files for these classes, and noticed they had extra entries not present in my .rgs file. I added them to mine, changing it to:
HKCR
{
TestModule.Test = s 'Test Class'
{
CLSID = s '{4C6DAD45-64B4-4C55-81C6-4CE125226421}'
}
NoRemove CLSID
{
ForceRemove {4C6DAD45-64B4-4C55-81C6-4CE125226421} = s 'Test Class'
{
ForceRemove Programmable
LocalServer32 = s '%MODULE%'
{
val ServerExecutable = s '%MODULE_RAW%'
}
TypeLib = s '{EAA173CA-BDBC-463A-8B7A-B010EFA467BC}'
Version = s '1.0'
}
}
}
And lo and behold, my CoCreateInstance call no longer hung, and I was able to properly retrieve a pointer to an ITest interface.
Now, my question is, with the above particulars in mind, how can I ensure that any future classes I create have this correct .rgs file format? Is there some option I'm missing when creating the class objects? Or will I need to manually add the above for every class I create?
I'm using Visual Studio 2010.

That's the ProgID for the coclass. It is primarily used by scripting languages, the ones that use late binding. CreateObject() is the usual function name. That this has anything to do with a hang is unexplainable, you'd better debug it.
The .rgs entry is otherwise automatically generated by the ATL wizard. The ProgID edit box is the lower right one. It doesn't get filled in automatically like the rest of them, you probably missed it.

Sorry for coming five years later...
I got a similar issue with ATL COM wizard using Visual Studio 2015 pro.
(error 0x80080005 - Server execution failed)
It looks like a bug on the ATL COM wizard (since some VS releases, and still not corrected on the latest VS2015).
I found an answer with a manual correction on this MS page:
https://connect.microsoft.com/VisualStudio/feedback/details/782281/catlservicemodulet-not-registering-components
The above link is no longer available. However, the issue is explained in this blog:
https://blogs.msdn.microsoft.com/jigarme/2008/05/07/cocreateinstance-returns-0x80080005-for-visual-studio-2008-based-atl-service/
Basically, the wizard fails to add the AppID registry entry in the associated rgs file:
NoRemove CLSID
{
ForceRemove {...} = s '...'
{
...
val AppID = s '%APPID%'
}
}
The .rgs files are not completely filled by the wizard.
Hope this helps.

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.

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.

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.

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
}

c++ application cannot find com dll because compiler generating .tlh file with incorrect guids

I have a com dll that gets used in a c++ project. I have not registered the dll but its in the same solution. My application continuously breaks and I found that it breaks here
hr = CoCreateInstance(rclsid, pOuter, dwClsContext, __uuidof(IUnknown), reinterpret_cast<void**>(&pIUnknown));
because I don't have a com class with that guid (rclsid). I did more digging and I found that it gets the guid from a .tlh file in a temp directory but the guid differs from my code.
.tlh file
struct __declspec(uuid("b10a18c8-7109-3f48-94d9-e48b526fc928"))
DocMapEntry;
// [ default ] interface _DocMapEntry
// interface _Object
// interface IDocMapEntry
c# code (com visable dll)
[ComVisible(true)]
[Guid("E98676B9-1965-4248-A9B6-74EC5EE5385A")]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface IDocMapEntry
{
int Offset { get; set; }
int PageOffset { get; set; }
int PageNumber { get; set; }
}
I tried changing the value to the correct value but it continuously gets changed and visual studio asks me to reload.
When I look at the class using com explorer I get a different clsid, and when I try to unregister it, it completes successfully but the item is still there and the file location is mscoree.
my c++ application uses the guid in the .tlh file but doesn't find the file.
public class DocMapEntry : IDocMapEntry
{
...
}
My questions are.
Do I need to register my com dll for this to work?
how can I set the guid for the class?
Why would it create a temp .tlh file with the wrong guid? (optional)
why can't I change this file? (optional)
Thank you.
public interface IDocMapEntry
COM strongly distinguishes between a CLSID and a IID. The IID, the interface identifier, identifies an interface implemented by a COM object. We can see that one, you specified it with the [Guid] attribute. You'd pass it as the 4th argument of CoCreateInstance(), the one where you now pass the IID of IUnknown.
What you need is the CLSID, the class identifier. We can't see it in your snippet, it is the one for whatever class implements your IDocMapEntry. With some odds that it is named DocMapEntry. And additional odds that you didn't give it a [Guid] so the CLR will auto-generate one for you. Note that it will change when you modify the class, one possible reason for CoCreateObject() to fail.
Forgetting to register the .NET assembly with Regasm.exe /codebase is another reason, you stated as much in your question. This is required or COM will not be able to find the DLL and fails the call with error code 0x80040154, "Class not registered". Having it in the same solution is not sufficient, COM search rules are not anything like those used by .NET.
Also note that you are exposing the internals of the class, avoid that by applying the [ClassInterface(ClassInterfaceType.None)] attribute on the class.
No you do not have to register the COM DLL but you do need to register an object responsible for creating the COM objects. Use do this in your application by calling CoRegisterClassObject after the COM subsystem has been initialized (by calling CoInitialize or CoInitializeEx).
IClassFactory *factory = new ObjectFactory();
// Register the factory responsible for creating our COM objects
DWORD classToken;
HRESULT hr = CoRegisterClassObject(
Object_CLSIID,
factory,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
&classToken);
// Now we can create the objects
Object *obj = nullptr;
hr = CoCreateInstance(
Object_CLSIID,
nullptr,
CLSCTX_LOCAL_SERVER,
Object_IID,
reinterpret_cast<void**>(&obj));
Not sure why it's creating a temporary .tlh but I suspect it's something in your build settings - possibly in the build steps.
Is your COM object in a C# dll ? If yes, did you run regasm previously in your system, which could have registered the COM dll.
Guid("E98676B9-1965-4248-A9B6-74EC5EE5385A"): This is your interface GUID, not your component guid.
You should have a GUID declared for COM object in the implementation class, which should be b10a18c8-7109-3f48-94d9-e48b526fc928.
Why would it create a temp .tlh file with the wrong guid?
Are you sure you have not imported the COM dll using #import keyword.