Global Interface Table: An Easy Way to Marshal an Interface Pointer
An easy way to marshal your interface pointer between threads!
Introduction
Component Object Modeling (COM) and ActiveX Template Library (ATL) are the toughest areas in Windows programming, but as a Windows programmer, you daily encounter these and seldom face difficulty accessing a COM pointer created in one thread from another thread in a multithreaded environment. COM/ATL has the concept of marshalling and the usage of Global Interface table to cope up with this problem.
Here, I am demonstrating how to marshal an interface pointer from one thread to another worker thread with some quick code. ATL provides an interfaces called IGlobalInterfaceTable
and a COM template class called CComGITPtr
to help the programmer understand this concept easily..
In my example, I have a COM component with an interface IIGitTest
, which I am going to marshal in the GitClient application. If you don't know how to create the COM component, please refer to my Step-by-Step Guide for Creating a COM component.
How to marshal the interface pointer from one apartment to another?
- First of all, we have to create a component exposing the
IIGitTest
with get/set property name and then create a MFC dialog based client application GitClient, which will access this interface from one apartment to another? - Our component name is
GitInterface
. I will import the DLL using a#import
directive since I don't want the namespace in the imported tlh and tli files, so I will use theno_namespace
suffix with#import
. I.e., in our example: - Now we can access our IIGitTest. We now add the member variable in our class like:
- Allocate memory to our interface and don't forget to initialize the COM model in the application.
- Now, give some values to our interface pointer. This will help us check if we are able to marshal our interface properly.
- Now unleash our GIT Code in the client application.
- Now create a thread which will get the
IIGitTest
pointer from the Global Interface table. - Now, initialize COM inside the new thread, and retrieve the cookie identification which we have sent through the
lpParam
parameter. - Now create the
CComGITPtr
object and passdwCookie
as the parameter, which will instruct theCComGITPtr
object to search GIT and retrieve the interface pointer of our interface. - Now, just copy the interface pointer to our interface pointer using the
CComGITPtr::CopyTO
function. - Now, retrieve the value. Remeber step 5.
#import "..\\output\\GitInterface.dll" no_namespace
IIGitTestPtr m_pToInterface;
CoInitialize(NULL);
// initialize the com CLSID pclsid;
CLSIDFromString(L"GitInterface.IGitTest.1",&pclsid);
m_pToInterface.CreateInstance(pclsid);
TCHAR szValues[256];
m_edtGitValue.GetWindowText(szValues,256);
CComBSTR bstrValues(szValues);
m_pToInterface->put_Value(bstrValues);
CComGITPtr<IIGITTEST> pToGITTest(m_pToInterface);
DWORD dwGitCookie = pToGITTest.Detach();
You must be wondering what this dwGitCookie
will do and how the template class is working? CComGITPtr
is a template wrapper class, which encapsulates the IGlobalInterfaceTable
interface. We are passing our interface pointer to the template class CComGITPtr
, and in return it is returning the DWORD
cookie, which will help us find our interface in the global interface table.
/// Create thread to try marshalling
CreateThread(NULL,NULL,ThreadProc,
reinterpret_cast<LPVOID>(dwGitCookie),NULL,NULL);
CoInitialize(NULL);
DWORD dwCookie = reinterpret_cast<DWORD>(lpParam);
CComGITPtr<IIGITTEST>pToGITTest(dwCookie);
IIGitTest *pToGitPtr;
pToGITTest.CopyTo(&pToGitPtr);
CComBSTR bstValue;
pToGitPtr->get_Value(&bstValue);
MessageBoxW(NULL,bstValue,L"From Git Interface",MB_OK);
The End :)
In this way, you can marshal an interface pointer from one thread to an other without thinking about the technicality of marshalling and other related stuff. Let us revise the process again.
Quick Revision
- Create an interface pointer and attach it to the GIT template class
- Retrieve the cookie from the GIT object
- In another thread, using a
DWORD
cookie, find the interface in the GIT table - Copy the object into an interface pointer
- Voila, now you can use your interface without any exception :)