Reputation: 900
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.
Upvotes: 1
Views: 697
Reputation: 69706
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.
Upvotes: 4