#import lead to a HRESULT 0x80040154 "Not registered class" - c++

I'm trying to use a COM DLL from VC++ 2005. I created a TestCOMlib.dll with ATL, created a simple interface ISimple and added one property (type LONG, name Property01) and one method (name Method01).
The DLL seems to be correctly registered in the system (I use OleView to check the entries).
I created a simple MFC dialog app to use the COM dll. I'm using the #import directive to incorporate information from the type library. Visual studio created for me the tlh and tli file.
Then I tried to obtain the ISimple interface but I'm obtaining the error 0x80040154.
The code I'm running inside the test application is the following:
HRESULT hr = S_OK;
hr = CoInitialize(NULL);
ISimplePtr myRef(__uuidof(ISimple));
// Test prop and method
myRef->Property01 = 5;
LONG test = myRef->Property01;
LONG ret = myRef->Method01(_T("Test input"));
ret = myRef->Method01(NULL);
myRef = NULL;
CoUninitialize();
The row returning the error 0x80040154 is ISimplePtr myRef(__uuidof(ISimple)).
OleView correctly display the interface and in the registry the entries seems to be good.
What am I doing wrong? Any idea?
Regards

The underlying class for these smart COM pointers is _com_ptr_t. You are trying to use this constructor:
// Constructs a smart pointer given the CLSID of a coclass. This
// function calls CoCreateInstance, by the member function
// CreateInstance, to create a new COM object and then queries for
// this smart pointer's interface type. If QueryInterface fails with
// an E_NOINTERFACE error, a NULL smart pointer is constructed.
explicit _com_ptr_t(
const CLSID& clsid,
IUnknown* pOuter = NULL,
DWORD dwClsContext = CLSCTX_ALL
);
Key point is that you have to pass the CLSID of the coclass, you are passing the IID of the interface. That's why __uuidof(Simple) works.

Related

Trigger deletion of Outlook email attachment using VSTO AddIn from VC++ app

I am attempting to delete an email in Outlook from a VC++ app. I have an Outlook Addin that is loading in Outlook and registered with the system. The VC++ app to trigger the deletion of the email makes a call to CoCreateInstance() and returns successfully.
HRESULT hr;
IFoo* pISL;
CoInitialize(NULL);
LPOLESTR lpoleStr;
StringFromCLSID(__uuidof(Foo), &lpoleStr);
hr = CoCreateInstance(
__uuidof(Foo), // CLSID of coclass
NULL, // not used - aggregation
CLSCTX_INPROC_SERVER | CLSCTX_INPROC_HANDLER | CLSCTX_LOCAL_SERVER, // type of server
__uuidof(IFoo), // IID of interface
(void**)&pISL); // Pointer to our interface pointer
if (SUCCEEDED(hr))
{
//fire event
BSTR bstr = SysAllocString(params->szObjectPath);
pISL->FooMethod(bstr);
pISL->Release();
SysFreeString(bstr);
}
else
{
// Couldn't create the COM object. hr holds the error code.
}
CoUninitialize();
I am then using the COM object returned to call the methods in the COM interface I requested. These methods fire events that should be listened to by the Outlook Addin.
private void RegisterConnectionPoint()
{
Logger.Log("RegisterConnectionPoint():");
// Call QueryInterface for IConnectionPointContainer
Foo foo = new Foo();
IConnectionPointContainer icpc = (IConnectionPointContainer)foo;
// Find the connection point for the
// _IFoo source interface
Guid IID_IFoo = typeof(_IFoo).GUID;
icpc.FindConnectionPoint(ref IID_IFoo, out icp);
// Pass a pointer of the host to the connection point
icp.Advise(this, out cookie);
}
My problem is the Outlook Addin never receives the events from the COM object. I think it may be due to the Addin not correctly registering with the COM object, or that there are two COM objects one for the VC++ app and one for the Outlook Addin.
My questions are
Is the basic concept of creating a Outlook VSTO Addin that deletes emails upon receiving an event from a COM server whose events are triggered from a separate process feasible?
If the answer is 'yes' to the first question can anyone see what I am doing wrong?
Debugging the VC++ app side I get to this code which it seems is generated ATL code (I haven't written all the code so am guessing sometimes):
int cConnections = m_vec.GetSize();
for (int iConnection = 0; iConnection < cConnections; iConnection++)
{
pThis->Lock();
CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);
pThis->Unlock();
IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);
if (pConnection)
{
CComVariant avarParams[1];
avarParams[0] = attachmentPath;
avarParams[0].vt = VT_BSTR;
CComVariant varResult;
DISPPARAMS params = { avarParams, NULL, 1, 0 };
hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, &varResult, NULL, NULL);
}
}
The problem is m_vec.GetSize() returns 0. So it seems there are no 'connections'. Which would imply I am either creating a different instance of the COM object than the one used by the Outlook Addin or the Addin is not registering correctly.
So the link supplied by #Dmitry was what I needed. By using the Running Object Table (ROT) I was able to get a reference to the Outlook addin loaded by Outlook. So allowing me to call the methods inside it to delete the mail attachment.
Some changes were required including:
Moving registration of the addin to ThisAddin_Startup() as the default OutlookAddin class uses a static constructor so I was not able to reference the Addin inside the constructor.
I also had to move my code to a user session process as this post COMException (0x800401E3): Operation unavailable (Exception from HRESULT: 0x800401E3 (MK_E_UNAVAILABLE)) with scheduled task explains retrieving an COM object from the ROT is not supported for services. This was just a point I thought I should add as it could stump people trying the same thing I was.
Additionally I found the tool ROT viewer found here http://alax.info/blog/1444 useful as you can check if you addin is actually registering to the ROT.
Hope this helps people.

MFC WebBrowser.Navigate error "A null reference pointer was passed to the stub."

In an c++ MFC Application, I have a WebBrowser embedded in the form. It was imbedded in the form via the "Insert ActiveX Control..." dialog and then I created the IDispatch wrapper class and added a variable.
These are the instructions I was following:
http://msdn.microsoft.com/en-us/library/aa752046(v=vs.85).aspx
After a few times of the dialog being created and destroyed, I am receiving a the error "A null reference pointer was passed to the stub." when I call the following:
m_browser->Navigate( _T("about:blank"), NULL, NULL, NULL, NULL );
Unfortunately, I can't recreate this and niether can my QA department (just the clients supposedly Win 7 x64, being called from a win32 application).
Is there something wrong with this approach or am I missing something altogether?
Thanks
Try using BSTR string instead of the literal _T("about:blank") and pass an empty variant instead of NULL:
COleVariant vUrl(_T("about:blank"));
COleVariant vEmpty;
m_browser->Navigate(V_BSTR(&vUrl), &vEmpty, &vEmpty, &vEmpty, &vEmpty);
That matches the signature of IWebBrowser2::Navigate method:
HRESULT Navigate(
BSTR url,
VARIANT *Flags,
VARIANT *TargetFrameName,
VARIANT *PostData,
VARIANT *Headers
);

Visual C++: InvokeHelper() function

I am deciphering a huge project that uses COM, which I am completely new to. It's quite confusing and I can't figure out how everything interacts. All I see is InvokeHelper(...) where I would expect to see a big amount of code. What is InvokeHelper()? What does it do?
Thank you for any help.
Even though it's a late answer, I'd like to post it here as I have spent a couple of days to figure out how it's working. It may be interesting for someone else.
Below is the path how to get to the real code from InvokeHelper() call:
InvokeHelper() should be called for an object of a class, inherited from CWnd with DISPID specified, where DISPID is something like 0x00000261
The class should have inside a call to a method CreateControl() with a GUID of a COM class
The COM class with the GUID should be COM coclass with at least one IDL interface
The IDL interface should implement a method with the attribute [id(DISPID)]. This is the same DISPID as in item 1
Look for implementation of the interface and find the method with this id attribute
VoilĂ !
Sure, if you don't have a source code of the COM class with the CLSID you cannot take a look inside the method, but at least, you can find its name as follows:
DISPID dispidCommand = 0x1; /// This is the dispid, you're looking for
COleDispatchDriver driver;
BOOL bRes = driver.CreateDispatch(GetClsid());
ASSERT(bRes);
HRESULT hr;
CComPtr<ITypeInfo> pti;
hr = driver.m_lpDispatch->GetTypeInfo(0, GetUserDefaultLCID(), &pti);
ASSERT(SUCCEEDED(hr));
UINT nCount = 0;
CComBSTR bstrName; // Name of the method, which is called via DISPID
hr = pti->GetNames(dispidCommand, &bstrName, 1, &nCount);
ASSERT(SUCCEEDED(hr));
I hope it helps someone.
Take care.

How to share COM objects between 2 processes?

I want Application1.exe to instantiate an instance of its Item class.
I want Application2.exe to call GetPrice() on this object.
I have followed steps 1-7 on the following website:
http://www.codeguru.com/Cpp/COM-Tech/activex/tutorials/article.php/c5567/
This is what I have so far.
Application1's main looks like this:
CoInitialize( NULL );
DWORD dwRegister;
ItemFactory *pFactory = new ItemFactory;
CoRegisterClassObject( CLSID_Item, pFactory, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &dwRegister );
_getch();
return 0;
Application2's main looks like this:
CoInitialize( NULL );
CoGetClassObject( CLSID_Item, CLSCTX_LOCAL_SERVER, NULL, IID_IItem, (LPVOID *)&pFactory );
My Issue (hopefully my only issue) is that I have no idea how to associate my Item class (or its interface, IItem) with CLSID_Item; this is just some random GUID I defined in another file. I've tried
CoRegisterPSClsid( IID_IItem, CLSID_Item );
After this line, I tried
Item *pItem;
CoCreateInstance( CLSID_Item, NULL, CLSCTX_LOCAL_SERVER, IID_IItem, (LPVOID *)&pItem );
I get an E_NOINTERFACE error.
Should I be creating a factory with CoCreateInstance? Ugh, so confused...
In order to use COM across process or thread boundraries, you must tell COM about your interfaces so it can marshal your function arguments/return values between processes. The easiest way to do this is to use an interface predefined in the system, such as IDispatch, but if you want to use your own, you must either register a proxy/stub DLL, or a type library. If you don't do this, then calls to QueryInterface for your custom interface across COM domains will fail with E_NOINTERFACE, as you are seeing.

How to prevent crashing if com dll isnt registered

From some old c++ code im trying to use a com dll, it works fine when the dll is registered, but it crahses if the dll isnt registered.
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
IGetTestPtr ptest(__uuidof(tester));
"Use method from the dll"
// Uninitialize COM.
CoUninitialize();
Is it anyway to check if the dll has been registered, before calling IGetTestPtr ptest(__uuidof(tester))?
Or what is the correct way to prevent the crash?
Calling CreateInstance on your object will return an HRESULT that can be tested for success:
IGetTestPtr p = null;
HRESULT hRes = p.CreateInstance( __uuidof(tester) );
bool bSuccess = SUCCEEDED(hRes);
This assumes you've created an interface wrapper around your type library using Visual Studio, where COM Smart Pointers are used in the interface (this gives you the CreateInstance method).
If the DLL is registered, there is a record in HKCR/CLSID/{uuidof(tester)} (curly brackets do matter).
Actually, if it's not, then CoCreateInstance will return an error. Check for this error before using the pointer.
If the COM class is not registered then CoCreateInstance will return REGDB_E_CLASSNOTREG. You should check for general success or failure by using the SUCCEEDED() or FAILED() macros. You also need to create the object properly - see MDSN for an introduction to COM. For example:
HRESULT hr = CoInitialize(NULL);
if (SUCCEEDED(hr))
{
IGetTestPtr ptr = NULL;
hr = CoCreateInstance(CLSID_MyObjectNULL, CLSCTX_INPROC_SERVER, IID_IMyInterface, ptr)
if (SUCCEEDED(hr))
{
Do something with your COM object
...
// Don't forget to release your interface pointer or the object will leak
ptr->Release();
}
hr = CoUninitialize();
}
return hr;
If I recall correctly this "#import" styled COM framework throws exceptions. Add a try-catch block around the code. Google tells me the exceptions are of type _com_error.
Unless you twiddle the params on #import to prevent this, all failing COM calls are going to throw exceptions. You're going to have to turn this off (#import "raw" interfaces I think does it - look at the #import docs) or get real familiar with these exceptions.