Problem Description
In the following function, we have an IUnkown object which is querying another interface.
hr = pI->QueryInterface(IID_IActiveIMMApp, (void**)&pIApp);
I don't understand how can pI query IActiveIMMMessagePumpOwner. How can we know that there is a handle from the Com Object to IActiveIMMMessagePumpOwner. How can the IID_IActiveIMMApp used in here tell us that yes there is a handle to the IActiveIMMMessagePumpOwner and we would like to get the a pointer to the pointer pointing to the interface?
If CLSID_CActiveIMM is the reason for knowing that the COM objects handles the interface. then why can't add as much interfaces Ids as we want in CoCreateInstance.
Source Code
global_ime_init(ATOM atom, HWND hWnd)
{
IUnknown *pI;
HRESULT hr;
if (pIApp != NULL || pIMsg != NULL)
return;
OleInitialize(NULL);
/*
* Get interface IUnknown
*/
hr = CoCreateInstance(CLSID_CActiveIMM, NULL, CLSCTX_SERVER,
IID_IUnknown, (void**)&pI);
if (FAILED(hr) || !pI)
return;
/*
* Get interface IActiveIMMApp
*/
hr = pI->QueryInterface(IID_IActiveIMMApp, (void**)&pIApp);
if (FAILED(hr))
pIApp = NULL;
/*
* Get interface IActiveIMMMessagePumpOwner
*/
hr = pI->QueryInterface(IID_IActiveIMMMessagePumpOwner, (void**)&pIMsg);
if (FAILED(hr))
pIMsg = NULL;
if (pIApp != NULL)
{
pIApp->Activate(TRUE);
pIApp->FilterClientWindows(&atom, 1);
}
if (pIMsg != NULL)
pIMsg->Start();
pI->Release();
s_hWnd = hWnd;
}
P.S. I started learning the COM Model 1 hour ago, so please bear with me.
When the IUnknown::QueryInterface is executed, you pass the execution to the actual object and so if it recognizes the arguments and it understands the IID_IActiveIMMApp you passed, it supplies you with the pointer you ask for.
This is described in every COM tutorial in the QueryInterface explanation, for example, here:
QueryInterface is the mechanism that allows clients to dynamically discover (at run time) whether or not an interface is supported by a COM component; at the same time, it is the mechanism that a client uses to get an interface pointer from a COM component. When an application wants to use some function of a COM component, it calls that object's QueryInterface, requesting a pointer to the interface that implements the desired function. If the COM component supports that interface, it will return the appropriate interface pointer and a success code. If the COM component doesn't support the requested interface, it will return an error value. The application will then examine the return code; if successful, it will use the interface pointer to access the desired method. If the QueryInterface failed, the application will take some other action, letting the user know that the desired method is not available.
The following example shows a call to QueryInterface on the PhoneBook component. We are asking this component, "Do you support the ILookup interface?" If the call returns successfully, we know that the COM component supports the ILookup interface and we have a pointer to use to call methods contained in the ILookup interface (either LookupByName or LookupByNumber). If not, we know that the PhoneBook COM component does not implement the ILookup interface.
LPLOOKUP *pLookup;
TCHAR szNumber[64];
HRESULT hRes;
// Call QueryInterface on the COM Component PhoneBook, asking for a pointer
// to the Ilookup interface identified by a unique interface ID.
hRes = pPhoneBook->QueryInterface( IID_ILOOKUP, &pLookup);
if( SUCCEEDED( hRes ) )
{
pLookup->LookupByName("Daffy Duck", &szNumber); // Use Ilookup interface pointer.
pLookup->Release(); // Finished using the IPhoneBook interface pointer.
}
else
{
// Failed to acquire Ilookup interface pointer.
}
[...]
A COM component implements IUnknown to control its lifespan and to provide access to the interfaces it supports. A COM component does not provide direct access to its data. GUIDs provide a unique identifier for each class and interface, thereby preventing naming conflicts. And finally, the COM Library is implemented as part of the operating system, and provides the "legwork" associated with finding and launching COM components.
By holding any COM interface you are controlling its life time just by having a pointer, and the COM object you talk to is essentially a "black box". You "see" a "view" of its functionality by having methods of this particular COM interface available for calling. With QueryInterface you request another "view" with a different set of methods associated with the interface identifier. If you are given this new pointer and it's recognized by the COM object you talk to, those new methods are also available for calling.
Related
I need to implement the IServiceProvider interface in an open source MFC application; specifically my TTSApp application.
I am attempting to add support for the IAccessibleApplication interface, which is used by screen readers to obtain information about an application's name and version information.
It appears that Google Chrome implements the IServiceProvider interface via the AXPlatformNodeWin class, which is derived from the CComObjectRootEx class and other classes and interface. The problem is that an MFC application does not use the CComObjectRootEx class; it is used by ATL.
I have found the IServiceProviderImpl Class. Unfortunately, I cannot find any information on how it fits in the context of an application. Which class in my class hierarchy needs to be derived from the IServiceProviderImpl Class; my CWinApp derived class, my CDialogEx derived class, or some other class?
I learned a great deal while on my quest in search for an answer to this question. During the quest I fell down the rabbit hole (Alice's Adventures in Wonderland by Charles Lutwidge Dodgson, a.k.a. Lewis Carroll) only to find Cthulhu (The Call of Cthulhu by H. P. Lovecraft) waiting for me.
My initial research led me to the following macros defined in afxwin.h.
DECLARE_INTERFACE_MAP
BEGIN_INTERFACE_MAP
END_INTERFACE_MAP
BEGIN_INTERFACE_PART
END_INTERFACE_PART
The best documentation I could find for these macros is in the TN038: MFC/OLE IUnknown Implementation technical note. A good sample demonstrating the use of these macros and the implementation of the QueryService function is the TstCon sample.
Of course, this led to another question, what window do I need to do this for? To answer this question I looked at the source code of a certain screen reader to see how it uses the IAccessibleApplication interface.
The following function, though not the actual code used, demonstrates the technique (I cannot share the actual code since the screen reader is not open source).
std::wstring GetApplicationNameUsingTheIAccessibleApplicationInterface(
HWND hwnd, long idObject, long idChild)
{
CComPtr<IAccessible> acc;
CComVariant var;
auto hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &acc, &var);
if (hr != S_OK) return L"";
if (!acc) return L"";
CComQIPtr<IServiceProvider> serviceProvider = acc;
if (!serviceProvider) return L"";
CComQIPtr<IAccessibleApplication> application;
hr = serviceProvider->QueryService(
IID_IAccessible, __uuidof(IAccessibleApplication),
reinterpret_cast<void**>(&application));
if (FAILED(hr)) return L"";
if (!application) return L"";
CComBSTR appName;
hr = application->get_appName(&text);
if (FAILED(hr)) return L"";
return appName.m_str;
}
This function, or something like it, is called from our WinEventProc callback function in response to the EVENT_OBJECT_FOCUS event. This indicates that I need to do this for every window that can gain focus.
Armed with what I thought was the answer to my question, I dove in and implemented the IAccessibleApplication interface and added the necessary code to all of my focus-able windows. Much to my horror, my QueryService function was never called. When I debugged the screen reader to find out why, I discovered that the implicit QueryInterface implied by the following line of code failed.
CComQIPtr<IServiceProvider> serviceProvider = acc;
This led to a long and arduous quest to discover why the call to QueryInterface was failing.
I was working on a personal project at first so I could not call upon resources I have at my employer. Then, entirely by chance, I was assigned a task that required me to provide information on how to implement the IAccessible2 interface in a C++ application to a client who needed the information to help them make their applications more accessible. Hurrah, I was finally able to call upon coworkers for aid!
My coworker guided me down the correct path.
Create a customized version of the IAccessibleProxyImpl class and CAccessibleProxy class using source code obtained from atlacc.h.
Add a COM_INTERFACE_ENTRY for IAccessibleApplication in the COM_MAP (BEGIN_COM_MAP/END_COM_MAP) for my custom IAccessibleProxyImpl class.
Use the BEGIN_SERVICE_MAP, END_SERVICE_MAP, and SERVICE_ENTRY macros to provide an implementation of the IServiceProvider interface.
Provide an override for the CWnd::CreateAccessibleProxy function to cause my windows to use my custom accessible proxy and thus my implementation of the IAccessibleApplication interface.
Now the screen reader uses the application name I provide for the IAccessibleApplication interface for my application.
The application I did this for is open source. It is my TTSApp application. I have also made an example that demonstrates how to use a similar technique to support the IAccessible2 interface available here.
I am sharing this in the hopes that the information will prove to be helpful.
I have a multhithreaded application which works sitting in MTA. It performs calls of outproc object (let's name it OUTPROC_OBJ_A) and everything works fine:
each thread calls CoInitializeEx(NULL, COINIT_MULTITHREADED).
But now, I have yet another couple of com objects sitting in a dll - I need to use it in my application. Lets name one of it INPROC_OBJ_B. Their threading model is apartment threading. Also, it shares interfaces from OUTPROC_OBJ_A (it uses some of data as arguments of methods importing it from .tlb file)
When I do something like this (in one thread):
idl 1:
interface IData : IDispatch
{
}
coclass ConcreteData : IData;
idl 2: (uses idl 1):
interface IOBJ_B : IDispatch
{
void initData([in, out] IData* data);
}
coclass INPROC_OBJ_B : IOBJ_B;
main:
{
CoInitializeEx(NULL, COINIT_MULTITHREADED);
...
{
// 1.) Create instance of INPROC_OBJ_B.
CComPtr<IOBJ_B> pOBJ_B = CreateInstance(INPROC_OBJ_B);
// 2.) Pass to some method of INPROC_OBJ_B some data (For example, pointer to another com object created here.)
CComPtr<IData> pData = CreateInstance(ConcreteData);
pOBJ_B->initData(pData);
// 3.) Here it crashes. Probably on release of pData.
// initData now is empty and does nothing.
}
CoUninitialize();
}
When I do this, it can work fine sometimes. But sometimes, it crashes the entire application with access violation. Sometimes it throws object_is_not_connected_exception.
When I replace COINIT_MULTITHREADED with COINIT_APARTMENTTHREADED in CoInitializeEx, it works fine. But I can't do this in my application - The entire infrastructure is in MTA.
[UPDATE] : more pseudo code in example.
What am I doing wrong?
I have a Visual Studio add-in, which contains a scripting engine implemented in C++.
The add-in can only communicate with Visual Studio using IDispatch interfaces.
I am in the process of upgrading it from VS 2005 to VS 2010.
The add-in makes a series of IDispatch::Invoke() calls equivalent to this Visual Basic:
control = commandBar.Controls.Add(MsoControlType.msoControlButton)
control.Caption = "My button"
control.FaceId = 59
In VS 2005, this used to work. But in VS 2010 it doesn't. GetIDsOfNames() returns DISP_E_UNKNOWNNAME for "FaceId".
Note that "Caption" (which succeeds) is a property of CommandBarControl, and "FaceId" (which fails) is a property of the CommandBarButton subclass. The classname for the button's IDispatch* is CommandBarControl. So I think I need to downcast the CommandBarControl IDispatch* to a CommandBarButton IDispatch* somehow.
In Visual Basic I could write:
button = DirectCast(control, CommandBarButton)
button.FaceId = 59
But I don't know what DirectCast() does internally. If I did I'd probably be close to solving this.
Thanks
Just answering my own question here... bleah.
First I discovered that if I query the IDispatch for ICommandBarButton, then query that for IDispatch, I get a different IDispatch (at a different address) which recognises the ICommandBarButton properties. That left me with how to find the IID of some arbitrary interface name given to me by the script (in this case, the string "CommandBarButton").
But after more experimenting, it looks like I can downcast an IDispatch to the most derived class by simply querying for IUnknown, then querying that for IDispatch again. No other IIDs or trickery needed.
In the situation I described in the question, The second IDispatch* returned is a different address to the first, and is of type _CommandBarButton (note the underscore) where the original was of type ICommandBarControl. _CommandBarButton seems to have all the functionality of CommandBarButton. (See http://technet.microsoft.com/en-us/microsoft.visualstudio.commandbars.commandbarbutton%28v=vs.90%29)
I now include this code in my routine which returns an IDispatch object to the script engine:
/*
* Query for IUnknown, then for IDispatch again. This APPEARS to return
* an IDispatch for the most derived class, thus exposing all methods and
* properties, and in the process returns a different IDispatch to the
* original (multiple dispatch interfaces on the same object).
*
* For example, calling ICommandBarControls.Add(msoControlButton) returns
* an IDispatch for the CommandBarControl base class, not the
* CommandBarButton class. After casting IDispatch to IUnknown and back,
* we get an IDispatch for _CommandBarButton, which appears to be almost,
* but not quite, a CommandBarButton. That is, CommandBarButton inherits
* just about every one of its properties and methods from
* _CommandBarButton -- certainly all the ones we're interested in anyway.
*/
HRESULT hr;
IUnknown *unknown;
hr = dispatch->QueryInterface(IID_IUnknown, (void**)&unknown);
if (hr == S_OK)
{
IDispatch *dispatch2 = NULL;
unknown->QueryInterface(IID_IDispatch, (void**)&dispatch2);
if (hr == S_OK)
{
dispatch->Release();
dispatch = dispatch2;
}
unknown->Release();
}
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.
After much help from all my StackOverFlow brethren, I managed to create a C++ DLL that calls my C# classes via COM and passes data back and forth to an external application. There was much celebration in the kingdom after that code started working.
Now I have a new problem. I'm expanding the DLL so that it can call different classes (all implementing the same interface). I need to decide what class to call depending on a char array passed to the DLL when it is loaded. The methods I call are exactly the same regardless of which class I use. What is the best way to switch between classes when calling the DLL?
// Initialize COM.
HRESULT hr = CoInitialize(NULL);
// I want to do something like this....but how?
if (strcmp(modelType, "Model1") == 0) {
IUnitModelPtr pIUnit(__uuidof(ClassOne));
}
if (strcmp(modelType, "Model2") == 0) {
IUnitModelPtr pIUnit(__uuidof(ClassTwo));
}
//call method 1
//call method 2
CoUninitialize();
//exit
This is probably a fairly simple question, but I really do not know any C++. Just getting COM going was a major challenge for me.
edit: Im sure there are some super elegant ways to achieve this (reflection?) but please limit your suggestions to stuff that can be implemented easily....efficiency is not important here and maintainability is not really an issue.
Do smth like this:
GUID classId = GUID_NULL;
if( strcmp( modelType, "Model1" ) == 0 ) {
classId = __uuidof( class1 );
} else if( strcmp( modelType, "Model2" ) == 0 ) {
classId = __uuidof( class2 );
} else if(... etc, continue for all possible model types
}
IUnitModelPtr unit;
unit.CreateInstance( classId );
// interface methods can be called here
Two options.
As each different interface implementation is a coclass, and your C++ code uses the right prog-id/classid to perform the create.
Alternately the exposed coclass is a factory with a method to return the actual implementation class.
Both need logic to map your modelType to implementation class. You can do this in the C++ code or in the .NET code. Without more information about the overall context (is the string --> coclass mapping logically part of the COM component or of the caller).
(In a pure C++ COM implementation you can create your own custom COM instance factory, but that's more advanced COM and not available for .NET COM interop.)